797 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			797 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| /*
 | |
|  * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
 | |
|  * Copyright (C) 2017 Linaro Ltd.
 | |
|  */
 | |
| #include <linux/hash.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/soc/qcom/smem.h>
 | |
| #include <media/videobuf2-v4l2.h>
 | |
| 
 | |
| #include "core.h"
 | |
| #include "hfi.h"
 | |
| #include "hfi_helper.h"
 | |
| #include "hfi_msgs.h"
 | |
| #include "hfi_parser.h"
 | |
| 
 | |
| #define SMEM_IMG_VER_TBL	469
 | |
| #define VER_STR_SZ		128
 | |
| #define SMEM_IMG_OFFSET_VENUS	(14 * 128)
 | |
| 
 | |
| static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
 | |
| 			      struct hfi_msg_event_notify_pkt *pkt)
 | |
| {
 | |
| 	enum hfi_version ver = core->res->hfi_version;
 | |
| 	struct hfi_event_data event = {0};
 | |
| 	int num_properties_changed;
 | |
| 	struct hfi_framesize *frame_sz;
 | |
| 	struct hfi_profile_level *profile_level;
 | |
| 	struct hfi_bit_depth *pixel_depth;
 | |
| 	struct hfi_pic_struct *pic_struct;
 | |
| 	struct hfi_colour_space *colour_info;
 | |
| 	struct hfi_buffer_requirements *bufreq;
 | |
| 	struct hfi_extradata_input_crop *crop;
 | |
| 	struct hfi_dpb_counts *dpb_count;
 | |
| 	u8 *data_ptr;
 | |
| 	u32 ptype;
 | |
| 
 | |
| 	inst->error = HFI_ERR_NONE;
 | |
| 
 | |
| 	switch (pkt->event_data1) {
 | |
| 	case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
 | |
| 	case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
 | |
| 		break;
 | |
| 	default:
 | |
| 		inst->error = HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	event.event_type = pkt->event_data1;
 | |
| 
 | |
| 	num_properties_changed = pkt->event_data2;
 | |
| 	if (!num_properties_changed) {
 | |
| 		inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	data_ptr = (u8 *)&pkt->ext_event_data[0];
 | |
| 	do {
 | |
| 		ptype = *((u32 *)data_ptr);
 | |
| 		switch (ptype) {
 | |
| 		case HFI_PROPERTY_PARAM_FRAME_SIZE:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			frame_sz = (struct hfi_framesize *)data_ptr;
 | |
| 			event.width = frame_sz->width;
 | |
| 			event.height = frame_sz->height;
 | |
| 			data_ptr += sizeof(*frame_sz);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			profile_level = (struct hfi_profile_level *)data_ptr;
 | |
| 			event.profile = profile_level->profile;
 | |
| 			event.level = profile_level->level;
 | |
| 			data_ptr += sizeof(*profile_level);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			pixel_depth = (struct hfi_bit_depth *)data_ptr;
 | |
| 			event.bit_depth = pixel_depth->bit_depth;
 | |
| 			data_ptr += sizeof(*pixel_depth);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			pic_struct = (struct hfi_pic_struct *)data_ptr;
 | |
| 			event.pic_struct = pic_struct->progressive_only;
 | |
| 			data_ptr += sizeof(*pic_struct);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			colour_info = (struct hfi_colour_space *)data_ptr;
 | |
| 			event.colour_space = colour_info->colour_space;
 | |
| 			data_ptr += sizeof(*colour_info);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			event.entropy_mode = *(u32 *)data_ptr;
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			bufreq = (struct hfi_buffer_requirements *)data_ptr;
 | |
| 			event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver);
 | |
| 			data_ptr += sizeof(*bufreq);
 | |
| 			break;
 | |
| 		case HFI_INDEX_EXTRADATA_INPUT_CROP:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			crop = (struct hfi_extradata_input_crop *)data_ptr;
 | |
| 			event.input_crop.left = crop->left;
 | |
| 			event.input_crop.top = crop->top;
 | |
| 			event.input_crop.width = crop->width;
 | |
| 			event.input_crop.height = crop->height;
 | |
| 			data_ptr += sizeof(*crop);
 | |
| 			break;
 | |
| 		case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS:
 | |
| 			data_ptr += sizeof(u32);
 | |
| 			dpb_count = (struct hfi_dpb_counts *)data_ptr;
 | |
| 			event.buf_count = dpb_count->fw_min_cnt;
 | |
| 			data_ptr += sizeof(*dpb_count);
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 		num_properties_changed--;
 | |
| 	} while (num_properties_changed > 0);
 | |
| 
 | |
| done:
 | |
| 	inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
 | |
| }
 | |
| 
 | |
| static void event_release_buffer_ref(struct venus_core *core,
 | |
| 				     struct venus_inst *inst,
 | |
| 				     struct hfi_msg_event_notify_pkt *pkt)
 | |
| {
 | |
| 	struct hfi_event_data event = {0};
 | |
| 	struct hfi_msg_event_release_buffer_ref_pkt *data;
 | |
| 
 | |
| 	data = (struct hfi_msg_event_release_buffer_ref_pkt *)
 | |
| 		pkt->ext_event_data;
 | |
| 
 | |
| 	event.event_type = HFI_EVENT_RELEASE_BUFFER_REFERENCE;
 | |
| 	event.packet_buffer = data->packet_buffer;
 | |
| 	event.extradata_buffer = data->extradata_buffer;
 | |
| 	event.tag = data->output_tag;
 | |
| 
 | |
| 	inst->error = HFI_ERR_NONE;
 | |
| 	inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
 | |
| }
 | |
| 
 | |
| static void event_sys_error(struct venus_core *core, u32 event,
 | |
| 			    struct hfi_msg_event_notify_pkt *pkt)
 | |
| {
 | |
| 	if (pkt)
 | |
| 		dev_dbg(core->dev, VDBGH
 | |
| 			"sys error (session id:%x, data1:%x, data2:%x)\n",
 | |
| 			pkt->shdr.session_id, pkt->event_data1,
 | |
| 			pkt->event_data2);
 | |
| 
 | |
| 	core->core_ops->event_notify(core, event);
 | |
| }
 | |
| 
 | |
| static void
 | |
| event_session_error(struct venus_core *core, struct venus_inst *inst,
 | |
| 		    struct hfi_msg_event_notify_pkt *pkt)
 | |
| {
 | |
| 	struct device *dev = core->dev;
 | |
| 
 | |
| 	dev_dbg(dev, VDBGH "session error: event id:%x, session id:%x\n",
 | |
| 		pkt->event_data1, pkt->shdr.session_id);
 | |
| 
 | |
| 	if (!inst)
 | |
| 		return;
 | |
| 
 | |
| 	switch (pkt->event_data1) {
 | |
| 	/* non fatal session errors */
 | |
| 	case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
 | |
| 	case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
 | |
| 	case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
 | |
| 	case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
 | |
| 		inst->error = HFI_ERR_NONE;
 | |
| 		break;
 | |
| 	default:
 | |
| 		dev_err(dev, "session error: event id:%x (%x), session id:%x\n",
 | |
| 			pkt->event_data1, pkt->event_data2,
 | |
| 			pkt->shdr.session_id);
 | |
| 
 | |
| 		inst->error = pkt->event_data1;
 | |
| 		inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void hfi_event_notify(struct venus_core *core, struct venus_inst *inst,
 | |
| 			     void *packet)
 | |
| {
 | |
| 	struct hfi_msg_event_notify_pkt *pkt = packet;
 | |
| 
 | |
| 	if (!packet)
 | |
| 		return;
 | |
| 
 | |
| 	switch (pkt->event_id) {
 | |
| 	case HFI_EVENT_SYS_ERROR:
 | |
| 		event_sys_error(core, EVT_SYS_ERROR, pkt);
 | |
| 		break;
 | |
| 	case HFI_EVENT_SESSION_ERROR:
 | |
| 		event_session_error(core, inst, pkt);
 | |
| 		break;
 | |
| 	case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
 | |
| 		event_seq_changed(core, inst, pkt);
 | |
| 		break;
 | |
| 	case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
 | |
| 		event_release_buffer_ref(core, inst, pkt);
 | |
| 		break;
 | |
| 	case HFI_EVENT_SESSION_PROPERTY_CHANGED:
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst,
 | |
| 			      void *packet)
 | |
| {
 | |
| 	struct hfi_msg_sys_init_done_pkt *pkt = packet;
 | |
| 	int rem_bytes;
 | |
| 	u32 error;
 | |
| 
 | |
| 	error = pkt->error_type;
 | |
| 	if (error != HFI_ERR_NONE)
 | |
| 		goto done;
 | |
| 
 | |
| 	if (!pkt->num_properties) {
 | |
| 		error = HFI_ERR_SYS_INVALID_PARAMETER;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32);
 | |
| 	if (rem_bytes <= 0) {
 | |
| 		/* missing property data */
 | |
| 		error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	error = hfi_parser(core, inst, pkt->data, rem_bytes);
 | |
| 
 | |
| done:
 | |
| 	core->error = error;
 | |
| 	complete(&core->done);
 | |
| }
 | |
| 
 | |
| static void
 | |
| sys_get_prop_image_version(struct device *dev,
 | |
| 			   struct hfi_msg_sys_property_info_pkt *pkt)
 | |
| {
 | |
| 	u8 *smem_tbl_ptr;
 | |
| 	u8 *img_ver;
 | |
| 	int req_bytes;
 | |
| 	size_t smem_blk_sz;
 | |
| 
 | |
| 	req_bytes = pkt->hdr.size - sizeof(*pkt);
 | |
| 
 | |
| 	if (req_bytes < VER_STR_SZ || !pkt->data[0] || pkt->num_properties > 1)
 | |
| 		/* bad packet */
 | |
| 		return;
 | |
| 
 | |
| 	img_ver = pkt->data;
 | |
| 
 | |
| 	dev_dbg(dev, VDBGL "F/W version: %s\n", img_ver);
 | |
| 
 | |
| 	smem_tbl_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY,
 | |
| 		SMEM_IMG_VER_TBL, &smem_blk_sz);
 | |
| 	if (!IS_ERR(smem_tbl_ptr) && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ)
 | |
| 		memcpy(smem_tbl_ptr + SMEM_IMG_OFFSET_VENUS,
 | |
| 		       img_ver, VER_STR_SZ);
 | |
| }
 | |
| 
 | |
| static void hfi_sys_property_info(struct venus_core *core,
 | |
| 				  struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_sys_property_info_pkt *pkt = packet;
 | |
| 	struct device *dev = core->dev;
 | |
| 
 | |
| 	if (!pkt->num_properties) {
 | |
| 		dev_dbg(dev, VDBGL "no properties\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	switch (pkt->property) {
 | |
| 	case HFI_PROPERTY_SYS_IMAGE_VERSION:
 | |
| 		sys_get_prop_image_version(dev, pkt);
 | |
| 		break;
 | |
| 	default:
 | |
| 		dev_dbg(dev, VDBGL "unknown property data\n");
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void hfi_sys_rel_resource_done(struct venus_core *core,
 | |
| 				      struct venus_inst *inst,
 | |
| 				      void *packet)
 | |
| {
 | |
| 	struct hfi_msg_sys_release_resource_done_pkt *pkt = packet;
 | |
| 
 | |
| 	core->error = pkt->error_type;
 | |
| 	complete(&core->done);
 | |
| }
 | |
| 
 | |
| static void hfi_sys_ping_done(struct venus_core *core, struct venus_inst *inst,
 | |
| 			      void *packet)
 | |
| {
 | |
| 	struct hfi_msg_sys_ping_ack_pkt *pkt = packet;
 | |
| 
 | |
| 	core->error = HFI_ERR_NONE;
 | |
| 
 | |
| 	if (pkt->client_data != 0xbeef)
 | |
| 		core->error = HFI_ERR_SYS_FATAL;
 | |
| 
 | |
| 	complete(&core->done);
 | |
| }
 | |
| 
 | |
| static void hfi_sys_idle_done(struct venus_core *core, struct venus_inst *inst,
 | |
| 			      void *packet)
 | |
| {
 | |
| 	dev_dbg(core->dev, VDBGL "sys idle\n");
 | |
| }
 | |
| 
 | |
| static void hfi_sys_pc_prepare_done(struct venus_core *core,
 | |
| 				    struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_sys_pc_prep_done_pkt *pkt = packet;
 | |
| 
 | |
| 	dev_dbg(core->dev, VDBGL "pc prepare done (error %x)\n",
 | |
| 		pkt->error_type);
 | |
| }
 | |
| 
 | |
| static unsigned int
 | |
| session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt,
 | |
| 			       struct hfi_profile_level *profile_level)
 | |
| {
 | |
| 	struct hfi_profile_level *hfi;
 | |
| 	u32 req_bytes;
 | |
| 
 | |
| 	req_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
 | |
| 
 | |
| 	if (!req_bytes || req_bytes % sizeof(struct hfi_profile_level))
 | |
| 		/* bad packet */
 | |
| 		return HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 
 | |
| 	hfi = (struct hfi_profile_level *)&pkt->data[0];
 | |
| 	profile_level->profile = hfi->profile;
 | |
| 	profile_level->level = hfi->level;
 | |
| 
 | |
| 	return HFI_ERR_NONE;
 | |
| }
 | |
| 
 | |
| static unsigned int
 | |
| session_get_prop_buf_req(struct hfi_msg_session_property_info_pkt *pkt,
 | |
| 			 struct hfi_buffer_requirements *bufreq)
 | |
| {
 | |
| 	struct hfi_buffer_requirements *buf_req;
 | |
| 	u32 req_bytes;
 | |
| 	unsigned int idx = 0;
 | |
| 
 | |
| 	req_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
 | |
| 
 | |
| 	if (!req_bytes || req_bytes % sizeof(*buf_req) || !pkt->data[0])
 | |
| 		/* bad packet */
 | |
| 		return HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 
 | |
| 	buf_req = (struct hfi_buffer_requirements *)&pkt->data[0];
 | |
| 	if (!buf_req)
 | |
| 		return HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 
 | |
| 	while (req_bytes) {
 | |
| 		memcpy(&bufreq[idx], buf_req, sizeof(*bufreq));
 | |
| 		idx++;
 | |
| 
 | |
| 		if (idx >= HFI_BUFFER_TYPE_MAX)
 | |
| 			return HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 
 | |
| 		req_bytes -= sizeof(struct hfi_buffer_requirements);
 | |
| 		buf_req++;
 | |
| 	}
 | |
| 
 | |
| 	return HFI_ERR_NONE;
 | |
| }
 | |
| 
 | |
| static void hfi_session_prop_info(struct venus_core *core,
 | |
| 				  struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_property_info_pkt *pkt = packet;
 | |
| 	struct device *dev = core->dev;
 | |
| 	union hfi_get_property *hprop = &inst->hprop;
 | |
| 	unsigned int error = HFI_ERR_NONE;
 | |
| 
 | |
| 	if (!pkt->num_properties) {
 | |
| 		error = HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 		dev_err(dev, "%s: no properties\n", __func__);
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	switch (pkt->property) {
 | |
| 	case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
 | |
| 		memset(hprop->bufreq, 0, sizeof(hprop->bufreq));
 | |
| 		error = session_get_prop_buf_req(pkt, hprop->bufreq);
 | |
| 		break;
 | |
| 	case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
 | |
| 		memset(&hprop->profile_level, 0, sizeof(hprop->profile_level));
 | |
| 		error = session_get_prop_profile_level(pkt,
 | |
| 						       &hprop->profile_level);
 | |
| 		break;
 | |
| 	case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
 | |
| 		break;
 | |
| 	default:
 | |
| 		dev_dbg(dev, VDBGM "unknown property id:%x\n", pkt->property);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	inst->error = error;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_init_done(struct venus_core *core,
 | |
| 				  struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_init_done_pkt *pkt = packet;
 | |
| 	int rem_bytes;
 | |
| 	u32 error;
 | |
| 
 | |
| 	error = pkt->error_type;
 | |
| 	if (error != HFI_ERR_NONE)
 | |
| 		goto done;
 | |
| 
 | |
| 	if (!IS_V1(core))
 | |
| 		goto done;
 | |
| 
 | |
| 	rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32);
 | |
| 	if (rem_bytes <= 0) {
 | |
| 		error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	error = hfi_parser(core, inst, pkt->data, rem_bytes);
 | |
| done:
 | |
| 	inst->error = error;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_load_res_done(struct venus_core *core,
 | |
| 				      struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_load_resources_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_flush_done(struct venus_core *core,
 | |
| 				   struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_flush_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| 	if (inst->ops->flush_done)
 | |
| 		inst->ops->flush_done(inst);
 | |
| }
 | |
| 
 | |
| static void hfi_session_etb_done(struct venus_core *core,
 | |
| 				 struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	inst->ops->buf_done(inst, HFI_BUFFER_INPUT, pkt->input_tag,
 | |
| 			    pkt->filled_len, pkt->offset, 0, 0, 0);
 | |
| }
 | |
| 
 | |
| static void hfi_session_ftb_done(struct venus_core *core,
 | |
| 				 struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	u32 session_type = inst->session_type;
 | |
| 	u64 timestamp_us = 0;
 | |
| 	u32 timestamp_hi = 0, timestamp_lo = 0;
 | |
| 	unsigned int error;
 | |
| 	u32 flags = 0, hfi_flags = 0, offset = 0, filled_len = 0;
 | |
| 	u32 pic_type = 0, buffer_type = 0, output_tag = -1;
 | |
| 
 | |
| 	if (session_type == VIDC_SESSION_TYPE_ENC) {
 | |
| 		struct hfi_msg_session_fbd_compressed_pkt *pkt = packet;
 | |
| 
 | |
| 		timestamp_hi = pkt->time_stamp_hi;
 | |
| 		timestamp_lo = pkt->time_stamp_lo;
 | |
| 		hfi_flags = pkt->flags;
 | |
| 		offset = pkt->offset;
 | |
| 		filled_len = pkt->filled_len;
 | |
| 		pic_type = pkt->picture_type;
 | |
| 		output_tag = pkt->output_tag;
 | |
| 		buffer_type = HFI_BUFFER_OUTPUT;
 | |
| 
 | |
| 		error = pkt->error_type;
 | |
| 	} else if (session_type == VIDC_SESSION_TYPE_DEC) {
 | |
| 		struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt =
 | |
| 			packet;
 | |
| 
 | |
| 		timestamp_hi = pkt->time_stamp_hi;
 | |
| 		timestamp_lo = pkt->time_stamp_lo;
 | |
| 		hfi_flags = pkt->flags;
 | |
| 		offset = pkt->offset;
 | |
| 		filled_len = pkt->filled_len;
 | |
| 		pic_type = pkt->picture_type;
 | |
| 		output_tag = pkt->output_tag;
 | |
| 
 | |
| 		if (pkt->stream_id == 0)
 | |
| 			buffer_type = HFI_BUFFER_OUTPUT;
 | |
| 		else if (pkt->stream_id == 1)
 | |
| 			buffer_type = HFI_BUFFER_OUTPUT2;
 | |
| 
 | |
| 		error = pkt->error_type;
 | |
| 	} else {
 | |
| 		error = HFI_ERR_SESSION_INVALID_PARAMETER;
 | |
| 	}
 | |
| 
 | |
| 	if (buffer_type != HFI_BUFFER_OUTPUT &&
 | |
| 	    buffer_type != HFI_BUFFER_OUTPUT2)
 | |
| 		goto done;
 | |
| 
 | |
| 	if (hfi_flags & HFI_BUFFERFLAG_EOS)
 | |
| 		flags |= V4L2_BUF_FLAG_LAST;
 | |
| 
 | |
| 	switch (pic_type) {
 | |
| 	case HFI_PICTURE_IDR:
 | |
| 	case HFI_PICTURE_I:
 | |
| 		flags |= V4L2_BUF_FLAG_KEYFRAME;
 | |
| 		break;
 | |
| 	case HFI_PICTURE_P:
 | |
| 		flags |= V4L2_BUF_FLAG_PFRAME;
 | |
| 		break;
 | |
| 	case HFI_PICTURE_B:
 | |
| 		flags |= V4L2_BUF_FLAG_BFRAME;
 | |
| 		break;
 | |
| 	case HFI_FRAME_NOTCODED:
 | |
| 	case HFI_UNUSED_PICT:
 | |
| 	case HFI_FRAME_YUV:
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (!(hfi_flags & HFI_BUFFERFLAG_TIMESTAMPINVALID) && filled_len) {
 | |
| 		timestamp_us = timestamp_hi;
 | |
| 		timestamp_us = (timestamp_us << 32) | timestamp_lo;
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	inst->error = error;
 | |
| 	inst->ops->buf_done(inst, buffer_type, output_tag, filled_len,
 | |
| 			    offset, flags, hfi_flags, timestamp_us);
 | |
| }
 | |
| 
 | |
| static void hfi_session_start_done(struct venus_core *core,
 | |
| 				   struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_start_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_stop_done(struct venus_core *core,
 | |
| 				  struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_stop_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_rel_res_done(struct venus_core *core,
 | |
| 				     struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_release_resources_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_rel_buf_done(struct venus_core *core,
 | |
| 				     struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_release_buffers_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_end_done(struct venus_core *core,
 | |
| 				 struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_end_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_abort_done(struct venus_core *core,
 | |
| 				   struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_sys_session_abort_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| static void hfi_session_get_seq_hdr_done(struct venus_core *core,
 | |
| 					 struct venus_inst *inst, void *packet)
 | |
| {
 | |
| 	struct hfi_msg_session_get_sequence_hdr_done_pkt *pkt = packet;
 | |
| 
 | |
| 	inst->error = pkt->error_type;
 | |
| 	complete(&inst->done);
 | |
| }
 | |
| 
 | |
| struct hfi_done_handler {
 | |
| 	u32 pkt;
 | |
| 	u32 pkt_sz;
 | |
| 	u32 pkt_sz2;
 | |
| 	void (*done)(struct venus_core *, struct venus_inst *, void *);
 | |
| 	bool is_sys_pkt;
 | |
| };
 | |
| 
 | |
| static const struct hfi_done_handler handlers[] = {
 | |
| 	{.pkt = HFI_MSG_EVENT_NOTIFY,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_event_notify_pkt),
 | |
| 	 .done = hfi_event_notify,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_INIT,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt),
 | |
| 	 .done = hfi_sys_init_done,
 | |
| 	 .is_sys_pkt = true,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_PROPERTY_INFO,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt),
 | |
| 	 .done = hfi_sys_property_info,
 | |
| 	 .is_sys_pkt = true,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_RELEASE_RESOURCE,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_release_resource_done_pkt),
 | |
| 	 .done = hfi_sys_rel_resource_done,
 | |
| 	 .is_sys_pkt = true,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_PING_ACK,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_ping_ack_pkt),
 | |
| 	 .done = hfi_sys_ping_done,
 | |
| 	 .is_sys_pkt = true,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_IDLE,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_idle_pkt),
 | |
| 	 .done = hfi_sys_idle_done,
 | |
| 	 .is_sys_pkt = true,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_PC_PREP,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_pc_prep_done_pkt),
 | |
| 	 .done = hfi_sys_pc_prepare_done,
 | |
| 	 .is_sys_pkt = true,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_SESSION_INIT,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt),
 | |
| 	 .done = hfi_session_init_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_SESSION_END,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_end_done_pkt),
 | |
| 	 .done = hfi_session_end_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_load_resources_done_pkt),
 | |
| 	 .done = hfi_session_load_res_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_START,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_start_done_pkt),
 | |
| 	 .done = hfi_session_start_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_STOP,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_stop_done_pkt),
 | |
| 	 .done = hfi_session_stop_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SYS_SESSION_ABORT,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_sys_session_abort_done_pkt),
 | |
| 	 .done = hfi_session_abort_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
 | |
| 	 .done = hfi_session_etb_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_FILL_BUFFER,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt),
 | |
| 	 .pkt_sz2 = sizeof(struct hfi_msg_session_fbd_compressed_pkt),
 | |
| 	 .done = hfi_session_ftb_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_FLUSH,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
 | |
| 	 .done = hfi_session_flush_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_PROPERTY_INFO,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_property_info_pkt),
 | |
| 	 .done = hfi_session_prop_info,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_release_resources_done_pkt),
 | |
| 	 .done = hfi_session_rel_res_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_GET_SEQUENCE_HEADER,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_get_sequence_hdr_done_pkt),
 | |
| 	 .done = hfi_session_get_seq_hdr_done,
 | |
| 	},
 | |
| 	{.pkt = HFI_MSG_SESSION_RELEASE_BUFFERS,
 | |
| 	 .pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt),
 | |
| 	 .done = hfi_session_rel_buf_done,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| void hfi_process_watchdog_timeout(struct venus_core *core)
 | |
| {
 | |
| 	event_sys_error(core, EVT_SYS_WATCHDOG_TIMEOUT, NULL);
 | |
| }
 | |
| 
 | |
| static struct venus_inst *to_instance(struct venus_core *core, u32 session_id)
 | |
| {
 | |
| 	struct venus_inst *inst;
 | |
| 
 | |
| 	mutex_lock(&core->lock);
 | |
| 	list_for_each_entry(inst, &core->instances, list)
 | |
| 		if (hash32_ptr(inst) == session_id) {
 | |
| 			mutex_unlock(&core->lock);
 | |
| 			return inst;
 | |
| 		}
 | |
| 	mutex_unlock(&core->lock);
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| u32 hfi_process_msg_packet(struct venus_core *core, struct hfi_pkt_hdr *hdr)
 | |
| {
 | |
| 	const struct hfi_done_handler *handler;
 | |
| 	struct device *dev = core->dev;
 | |
| 	struct venus_inst *inst;
 | |
| 	bool found = false;
 | |
| 	unsigned int i;
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(handlers); i++) {
 | |
| 		handler = &handlers[i];
 | |
| 		if (handler->pkt != hdr->pkt_type)
 | |
| 			continue;
 | |
| 		found = true;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (!found)
 | |
| 		return hdr->pkt_type;
 | |
| 
 | |
| 	if (hdr->size && hdr->size < handler->pkt_sz &&
 | |
| 	    hdr->size < handler->pkt_sz2) {
 | |
| 		dev_err(dev, "bad packet size (%d should be %d, pkt type:%x)\n",
 | |
| 			hdr->size, handler->pkt_sz, hdr->pkt_type);
 | |
| 
 | |
| 		return hdr->pkt_type;
 | |
| 	}
 | |
| 
 | |
| 	if (handler->is_sys_pkt) {
 | |
| 		inst = NULL;
 | |
| 	} else {
 | |
| 		struct hfi_session_pkt *pkt;
 | |
| 
 | |
| 		pkt = (struct hfi_session_pkt *)hdr;
 | |
| 		inst = to_instance(core, pkt->shdr.session_id);
 | |
| 
 | |
| 		if (!inst)
 | |
| 			dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n",
 | |
| 				 pkt->shdr.session_id,
 | |
| 				 handler ? handler->pkt : 0);
 | |
| 
 | |
| 		/*
 | |
| 		 * Event of type HFI_EVENT_SYS_ERROR will not have any session
 | |
| 		 * associated with it
 | |
| 		 */
 | |
| 		if (!inst && hdr->pkt_type != HFI_MSG_EVENT_NOTIFY) {
 | |
| 			dev_err(dev, "got invalid session id:%x\n",
 | |
| 				pkt->shdr.session_id);
 | |
| 			goto invalid_session;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	handler->done(core, inst, hdr);
 | |
| 
 | |
| invalid_session:
 | |
| 	return hdr->pkt_type;
 | |
| }
 |