diff options
Diffstat (limited to 'drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c')
-rw-r--r-- | drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c | 934 |
1 files changed, 934 insertions, 0 deletions
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c new file mode 100644 index 000000000000..b75a01641d5d --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <media/v4l2-mem2mem.h> + +#include "iris_hfi_gen2.h" +#include "iris_hfi_gen2_defines.h" +#include "iris_hfi_gen2_packet.h" +#include "iris_vdec.h" +#include "iris_vpu_buffer.h" +#include "iris_vpu_common.h" + +struct iris_hfi_gen2_core_hfi_range { + u32 begin; + u32 end; + int (*handle)(struct iris_core *core, struct iris_hfi_packet *pkt); +}; + +struct iris_hfi_gen2_inst_hfi_range { + u32 begin; + u32 end; + int (*handle)(struct iris_inst *inst, struct iris_hfi_packet *pkt); +}; + +struct iris_hfi_gen2_packet_handle { + enum hfi_buffer_type type; + int (*handle)(struct iris_inst *inst, struct iris_hfi_packet *pkt); +}; + +static u32 iris_hfi_gen2_buf_type_to_driver(enum hfi_buffer_type buf_type) +{ + switch (buf_type) { + case HFI_BUFFER_BITSTREAM: + return BUF_INPUT; + case HFI_BUFFER_RAW: + return BUF_OUTPUT; + case HFI_BUFFER_BIN: + return BUF_BIN; + case HFI_BUFFER_ARP: + return BUF_ARP; + case HFI_BUFFER_COMV: + return BUF_COMV; + case HFI_BUFFER_NON_COMV: + return BUF_NON_COMV; + case HFI_BUFFER_LINE: + return BUF_LINE; + case HFI_BUFFER_DPB: + return BUF_DPB; + case HFI_BUFFER_PERSIST: + return BUF_PERSIST; + default: + return 0; + } +} + +static bool iris_hfi_gen2_is_valid_hfi_buffer_type(u32 buffer_type) +{ + switch (buffer_type) { + case HFI_BUFFER_BITSTREAM: + case HFI_BUFFER_RAW: + case HFI_BUFFER_BIN: + case HFI_BUFFER_ARP: + case HFI_BUFFER_COMV: + case HFI_BUFFER_NON_COMV: + case HFI_BUFFER_LINE: + case HFI_BUFFER_DPB: + case HFI_BUFFER_PERSIST: + case HFI_BUFFER_VPSS: + return true; + default: + return false; + } +} + +static bool iris_hfi_gen2_is_valid_hfi_port(u32 port, u32 buffer_type) +{ + if (port == HFI_PORT_NONE && buffer_type != HFI_BUFFER_PERSIST) + return false; + + if (port != HFI_PORT_BITSTREAM && port != HFI_PORT_RAW) + return false; + + return true; +} + +static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi_flags) +{ + u32 keyframe = HFI_PICTURE_IDR | HFI_PICTURE_I | HFI_PICTURE_CRA | HFI_PICTURE_BLA; + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + u32 driver_flags = 0; + + if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe) + driver_flags |= V4L2_BUF_FLAG_KEYFRAME; + else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_P) + driver_flags |= V4L2_BUF_FLAG_PFRAME; + else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_B) + driver_flags |= V4L2_BUF_FLAG_BFRAME; + + if (inst_hfi_gen2->hfi_frame_info.data_corrupt || inst_hfi_gen2->hfi_frame_info.overflow) + driver_flags |= V4L2_BUF_FLAG_ERROR; + + if (hfi_flags & HFI_BUF_FW_FLAG_LAST || + hfi_flags & HFI_BUF_FW_FLAG_PSC_LAST) + driver_flags |= V4L2_BUF_FLAG_LAST; + + return driver_flags; +} + +static bool iris_hfi_gen2_validate_packet_payload(struct iris_hfi_packet *pkt) +{ + u32 payload_size = 0; + + switch (pkt->payload_info) { + case HFI_PAYLOAD_U32: + case HFI_PAYLOAD_S32: + case HFI_PAYLOAD_Q16: + case HFI_PAYLOAD_U32_ENUM: + case HFI_PAYLOAD_32_PACKED: + payload_size = 4; + break; + case HFI_PAYLOAD_U64: + case HFI_PAYLOAD_S64: + case HFI_PAYLOAD_64_PACKED: + payload_size = 8; + break; + case HFI_PAYLOAD_STRUCTURE: + if (pkt->type == HFI_CMD_BUFFER) + payload_size = sizeof(struct iris_hfi_buffer); + break; + default: + payload_size = 0; + break; + } + + if (pkt->size < sizeof(struct iris_hfi_packet) + payload_size) + return false; + + return true; +} + +static int iris_hfi_gen2_validate_packet(u8 *response_pkt, u8 *core_resp_pkt) +{ + u8 *response_limit = core_resp_pkt + IFACEQ_CORE_PKT_SIZE; + u32 response_pkt_size = *(u32 *)response_pkt; + + if (!response_pkt_size) + return -EINVAL; + + if (response_pkt_size < sizeof(struct iris_hfi_packet)) + return -EINVAL; + + if (response_pkt + response_pkt_size > response_limit) + return -EINVAL; + + return 0; +} + +static int iris_hfi_gen2_validate_hdr_packet(struct iris_core *core, struct iris_hfi_header *hdr) +{ + struct iris_hfi_packet *packet; + int ret; + u8 *pkt; + u32 i; + + if (hdr->size < sizeof(*hdr) + sizeof(*packet)) + return -EINVAL; + + pkt = (u8 *)((u8 *)hdr + sizeof(*hdr)); + + for (i = 0; i < hdr->num_packets; i++) { + packet = (struct iris_hfi_packet *)pkt; + ret = iris_hfi_gen2_validate_packet(pkt, core->response_packet); + if (ret) + return ret; + + pkt += packet->size; + } + + return 0; +} + +static int iris_hfi_gen2_handle_session_info(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + struct iris_core *core = inst->core; + int ret = 0; + char *info; + + switch (pkt->type) { + case HFI_INFO_UNSUPPORTED: + info = "unsupported"; + break; + case HFI_INFO_DATA_CORRUPT: + info = "data corrupt"; + inst_hfi_gen2->hfi_frame_info.data_corrupt = 1; + break; + case HFI_INFO_BUFFER_OVERFLOW: + info = "buffer overflow"; + inst_hfi_gen2->hfi_frame_info.overflow = 1; + break; + case HFI_INFO_HFI_FLAG_DRAIN_LAST: + info = "drain last flag"; + ret = iris_inst_sub_state_change_drain_last(inst); + break; + case HFI_INFO_HFI_FLAG_PSC_LAST: + info = "drc last flag"; + ret = iris_inst_sub_state_change_drc_last(inst); + break; + default: + info = "unknown"; + break; + } + + dev_dbg(core->dev, "session info received %#x: %s\n", + pkt->type, info); + + return ret; +} + +static int iris_hfi_gen2_handle_session_error(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + struct iris_core *core = inst->core; + char *error; + + switch (pkt->type) { + case HFI_ERROR_MAX_SESSIONS: + error = "exceeded max sessions"; + break; + case HFI_ERROR_UNKNOWN_SESSION: + error = "unknown session id"; + break; + case HFI_ERROR_INVALID_STATE: + error = "invalid operation for current state"; + break; + case HFI_ERROR_INSUFFICIENT_RESOURCES: + error = "insufficient resources"; + break; + case HFI_ERROR_BUFFER_NOT_SET: + error = "internal buffers not set"; + break; + case HFI_ERROR_FATAL: + error = "fatal error"; + break; + case HFI_ERROR_STREAM_UNSUPPORTED: + error = "unsupported stream"; + break; + default: + error = "unknown"; + break; + } + + dev_err(core->dev, "session error received %#x: %s\n", pkt->type, error); + iris_vb2_queue_error(inst); + iris_inst_change_state(inst, IRIS_INST_ERROR); + + return 0; +} + +static int iris_hfi_gen2_handle_system_error(struct iris_core *core, + struct iris_hfi_packet *pkt) +{ + struct iris_inst *instance; + + dev_err(core->dev, "received system error of type %#x\n", pkt->type); + + core->state = IRIS_CORE_ERROR; + + mutex_lock(&core->lock); + list_for_each_entry(instance, &core->instances, list) + iris_inst_change_state(instance, IRIS_INST_ERROR); + mutex_unlock(&core->lock); + + schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10)); + + return 0; +} + +static int iris_hfi_gen2_handle_system_init(struct iris_core *core, + struct iris_hfi_packet *pkt) +{ + if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) { + core->state = IRIS_CORE_ERROR; + return 0; + } + + complete(&core->core_init_done); + + return 0; +} + +static void iris_hfi_gen2_handle_session_close(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) { + iris_inst_change_state(inst, IRIS_INST_ERROR); + return; + } + + complete(&inst->completion); +} + +static int iris_hfi_gen2_handle_input_buffer(struct iris_inst *inst, + struct iris_hfi_buffer *buffer) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct v4l2_m2m_buffer *m2m_buffer, *n; + struct iris_buffer *buf; + bool found = false; + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) { + buf = to_iris_buffer(&m2m_buffer->vb); + if (buf->index == buffer->index) { + found = true; + break; + } + } + if (!found) + return -EINVAL; + + if (!(buf->attr & BUF_ATTR_QUEUED)) + return -EINVAL; + + buf->attr &= ~BUF_ATTR_QUEUED; + buf->attr |= BUF_ATTR_DEQUEUED; + + buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, buffer->flags); + + return 0; +} + +static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst, + struct iris_hfi_buffer *hfi_buffer) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct v4l2_m2m_buffer *m2m_buffer, *n; + struct iris_buffer *buf; + bool found = false; + int ret; + + if (hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST) { + ret = iris_inst_sub_state_change_drain_last(inst); + if (ret) + return ret; + } + + if (hfi_buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST) { + ret = iris_inst_sub_state_change_drc_last(inst); + if (ret) + return ret; + } + + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) { + buf = to_iris_buffer(&m2m_buffer->vb); + if (buf->index == hfi_buffer->index && + buf->device_addr == hfi_buffer->base_address && + buf->data_offset == hfi_buffer->data_offset) { + found = true; + break; + } + } + if (!found) + return -EINVAL; + + if (!(buf->attr & BUF_ATTR_QUEUED)) + return -EINVAL; + + buf->data_offset = hfi_buffer->data_offset; + buf->data_size = hfi_buffer->data_size; + buf->timestamp = hfi_buffer->timestamp; + + buf->attr &= ~BUF_ATTR_QUEUED; + buf->attr |= BUF_ATTR_DEQUEUED; + + buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags); + + return 0; +} + +static void iris_hfi_gen2_handle_dequeue_buffers(struct iris_inst *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct v4l2_m2m_buffer *buffer, *n; + struct iris_buffer *buf = NULL; + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) { + buf = to_iris_buffer(&buffer->vb); + if (buf->attr & BUF_ATTR_DEQUEUED) { + buf->attr &= ~BUF_ATTR_DEQUEUED; + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) { + buf->attr |= BUF_ATTR_BUFFER_DONE; + iris_vb2_buffer_done(inst, buf); + } + } + } + + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) { + buf = to_iris_buffer(&buffer->vb); + if (buf->attr & BUF_ATTR_DEQUEUED) { + buf->attr &= ~BUF_ATTR_DEQUEUED; + if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) { + buf->attr |= BUF_ATTR_BUFFER_DONE; + iris_vb2_buffer_done(inst, buf); + } + } + } +} + +static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst, + struct iris_hfi_buffer *buffer) +{ + u32 buf_type = iris_hfi_gen2_buf_type_to_driver(buffer->type); + struct iris_buffers *buffers = &inst->buffers[buf_type]; + struct iris_buffer *buf, *iter; + bool found = false; + int ret = 0; + + list_for_each_entry(iter, &buffers->list, list) { + if (iter->device_addr == buffer->base_address) { + found = true; + buf = iter; + break; + } + } + if (!found) + return -EINVAL; + + buf->attr &= ~BUF_ATTR_QUEUED; + if (buf->attr & BUF_ATTR_PENDING_RELEASE) + ret = iris_destroy_internal_buffer(inst, buf); + + return ret; +} + +static int iris_hfi_gen2_handle_session_stop(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + int ret = 0; + + if (pkt->port == HFI_PORT_RAW) + ret = iris_inst_sub_state_change_pause(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + else if (pkt->port == HFI_PORT_BITSTREAM) + ret = iris_inst_sub_state_change_pause(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + complete(&inst->completion); + + return ret; +} + +static int iris_hfi_gen2_handle_session_buffer(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + struct iris_hfi_buffer *buffer; + + if (pkt->payload_info == HFI_PAYLOAD_NONE) + return 0; + + if (!iris_hfi_gen2_validate_packet_payload(pkt)) { + iris_inst_change_state(inst, IRIS_INST_ERROR); + return 0; + } + + buffer = (struct iris_hfi_buffer *)((u8 *)pkt + sizeof(*pkt)); + if (!iris_hfi_gen2_is_valid_hfi_buffer_type(buffer->type)) + return 0; + + if (!iris_hfi_gen2_is_valid_hfi_port(pkt->port, buffer->type)) + return 0; + + if (buffer->type == HFI_BUFFER_BITSTREAM) + return iris_hfi_gen2_handle_input_buffer(inst, buffer); + else if (buffer->type == HFI_BUFFER_RAW) + return iris_hfi_gen2_handle_output_buffer(inst, buffer); + else + return iris_hfi_gen2_handle_release_internal_buffer(inst, buffer); +} + +static int iris_hfi_gen2_handle_session_drain(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + int ret = 0; + + if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) { + iris_inst_change_state(inst, IRIS_INST_ERROR); + return 0; + } + + if (inst->sub_state & IRIS_INST_SUB_DRAIN) + ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_INPUT_PAUSE); + + return ret; +} + +static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) +{ + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp; + struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp; + u32 primaries, matrix_coeff, transfer_char; + struct hfi_subscription_params subsc_params; + u32 colour_description_present_flag; + u32 video_signal_type_present_flag; + struct iris_core *core = inst->core; + u32 full_range, width, height; + struct vb2_queue *dst_q; + struct v4l2_ctrl *ctrl; + + subsc_params = inst_hfi_gen2->src_subcr_params; + width = (subsc_params.bitstream_resolution & + HFI_BITMASK_BITSTREAM_WIDTH) >> 16; + height = subsc_params.bitstream_resolution & + HFI_BITMASK_BITSTREAM_HEIGHT; + + pixmp_ip->width = width; + pixmp_ip->height = height; + + pixmp_op->width = ALIGN(width, 128); + pixmp_op->height = ALIGN(height, 32); + pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128); + pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); + + matrix_coeff = subsc_params.color_info & 0xFF; + transfer_char = (subsc_params.color_info & 0xFF00) >> 8; + primaries = (subsc_params.color_info & 0xFF0000) >> 16; + colour_description_present_flag = + (subsc_params.color_info & 0x1000000) >> 24; + full_range = (subsc_params.color_info & 0x2000000) >> 25; + video_signal_type_present_flag = + (subsc_params.color_info & 0x20000000) >> 29; + + pixmp_op->colorspace = V4L2_COLORSPACE_DEFAULT; + pixmp_op->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pixmp_op->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pixmp_op->quantization = V4L2_QUANTIZATION_DEFAULT; + + if (video_signal_type_present_flag) { + pixmp_op->quantization = + full_range ? + V4L2_QUANTIZATION_FULL_RANGE : + V4L2_QUANTIZATION_LIM_RANGE; + if (colour_description_present_flag) { + pixmp_op->colorspace = + iris_hfi_get_v4l2_color_primaries(primaries); + pixmp_op->xfer_func = + iris_hfi_get_v4l2_transfer_char(transfer_char); + pixmp_op->ycbcr_enc = + iris_hfi_get_v4l2_matrix_coefficients(matrix_coeff); + } + } + + pixmp_ip->colorspace = pixmp_op->colorspace; + pixmp_ip->xfer_func = pixmp_op->xfer_func; + pixmp_ip->ycbcr_enc = pixmp_op->ycbcr_enc; + pixmp_ip->quantization = pixmp_op->quantization; + + inst->crop.top = subsc_params.crop_offsets[0] & 0xFFFF; + inst->crop.left = (subsc_params.crop_offsets[0] >> 16) & 0xFFFF; + inst->crop.height = pixmp_ip->height - + (subsc_params.crop_offsets[1] & 0xFFFF) - inst->crop.top; + inst->crop.width = pixmp_ip->width - + ((subsc_params.crop_offsets[1] >> 16) & 0xFFFF) - inst->crop.left; + + inst->fw_caps[PROFILE].value = subsc_params.profile; + inst->fw_caps[LEVEL].value = subsc_params.level; + inst->fw_caps[POC].value = subsc_params.pic_order_cnt; + + if (subsc_params.bit_depth != BIT_DEPTH_8 || + !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) { + dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n", + subsc_params.bit_depth, subsc_params.coded_frames); + iris_inst_change_state(inst, IRIS_INST_ERROR); + } + + inst->fw_min_count = subsc_params.fw_min_count; + inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); + inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage; + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, inst->buffers[BUF_OUTPUT].min_count); + + dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count; +} + +static int iris_hfi_gen2_handle_src_change(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + int ret; + + if (pkt->port != HFI_PORT_BITSTREAM) + return 0; + + ret = iris_inst_sub_state_change_drc(inst); + if (ret) + return ret; + + iris_hfi_gen2_read_input_subcr_params(inst); + iris_vdec_src_change(inst); + + return 0; +} + +static int iris_hfi_gen2_handle_session_command(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + int ret = 0; + + switch (pkt->type) { + case HFI_CMD_CLOSE: + iris_hfi_gen2_handle_session_close(inst, pkt); + break; + case HFI_CMD_STOP: + iris_hfi_gen2_handle_session_stop(inst, pkt); + break; + case HFI_CMD_BUFFER: + ret = iris_hfi_gen2_handle_session_buffer(inst, pkt); + break; + case HFI_CMD_SETTINGS_CHANGE: + ret = iris_hfi_gen2_handle_src_change(inst, pkt); + break; + case HFI_CMD_DRAIN: + ret = iris_hfi_gen2_handle_session_drain(inst, pkt); + break; + default: + break; + } + + return ret; +} + +static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst, + struct iris_hfi_packet *pkt) +{ + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + + if (pkt->port != HFI_PORT_BITSTREAM) + return 0; + + if (pkt->flags & HFI_FW_FLAGS_INFORMATION) + return 0; + + switch (pkt->type) { + case HFI_PROP_BITSTREAM_RESOLUTION: + inst_hfi_gen2->src_subcr_params.bitstream_resolution = pkt->payload[0]; + break; + case HFI_PROP_CROP_OFFSETS: + inst_hfi_gen2->src_subcr_params.crop_offsets[0] = pkt->payload[0]; + inst_hfi_gen2->src_subcr_params.crop_offsets[1] = pkt->payload[1]; + break; + case HFI_PROP_CODED_FRAMES: + inst_hfi_gen2->src_subcr_params.coded_frames = pkt->payload[0]; + break; + case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT: + inst_hfi_gen2->src_subcr_params.fw_min_count = pkt->payload[0]; + break; + case HFI_PROP_PIC_ORDER_CNT_TYPE: + inst_hfi_gen2->src_subcr_params.pic_order_cnt = pkt->payload[0]; + break; + case HFI_PROP_SIGNAL_COLOR_INFO: + inst_hfi_gen2->src_subcr_params.color_info = pkt->payload[0]; + break; + case HFI_PROP_PROFILE: + inst_hfi_gen2->src_subcr_params.profile = pkt->payload[0]; + break; + case HFI_PROP_LEVEL: + inst_hfi_gen2->src_subcr_params.level = pkt->payload[0]; + break; + case HFI_PROP_PICTURE_TYPE: + inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0]; + break; + case HFI_PROP_NO_OUTPUT: + inst_hfi_gen2->hfi_frame_info.no_output = 1; + break; + case HFI_PROP_QUALITY_MODE: + case HFI_PROP_STAGE: + case HFI_PROP_PIPE: + default: + break; + } + + return 0; +} + +static int iris_hfi_gen2_handle_image_version_property(struct iris_core *core, + struct iris_hfi_packet *pkt) +{ + u8 *str_image_version = (u8 *)pkt + sizeof(*pkt); + u32 req_bytes = pkt->size - sizeof(*pkt); + char fw_version[IRIS_FW_VERSION_LENGTH]; + u32 i; + + if (req_bytes < IRIS_FW_VERSION_LENGTH - 1) + return -EINVAL; + + for (i = 0; i < IRIS_FW_VERSION_LENGTH - 1; i++) { + if (str_image_version[i] != '\0') + fw_version[i] = str_image_version[i]; + else + fw_version[i] = ' '; + } + fw_version[i] = '\0'; + dev_dbg(core->dev, "firmware version: %s\n", fw_version); + + return 0; +} + +static int iris_hfi_gen2_handle_system_property(struct iris_core *core, + struct iris_hfi_packet *pkt) +{ + switch (pkt->type) { + case HFI_PROP_IMAGE_VERSION: + return iris_hfi_gen2_handle_image_version_property(core, pkt); + default: + return 0; + } +} + +static int iris_hfi_gen2_handle_system_response(struct iris_core *core, + struct iris_hfi_header *hdr) +{ + u8 *start_pkt = (u8 *)((u8 *)hdr + sizeof(*hdr)); + struct iris_hfi_packet *packet; + u32 i, j; + u8 *pkt; + int ret; + static const struct iris_hfi_gen2_core_hfi_range range[] = { + {HFI_SYSTEM_ERROR_BEGIN, HFI_SYSTEM_ERROR_END, iris_hfi_gen2_handle_system_error }, + {HFI_PROP_BEGIN, HFI_PROP_END, iris_hfi_gen2_handle_system_property }, + {HFI_CMD_BEGIN, HFI_CMD_END, iris_hfi_gen2_handle_system_init }, + }; + + for (i = 0; i < ARRAY_SIZE(range); i++) { + pkt = start_pkt; + for (j = 0; j < hdr->num_packets; j++) { + packet = (struct iris_hfi_packet *)pkt; + if (packet->flags & HFI_FW_FLAGS_SYSTEM_ERROR) { + ret = iris_hfi_gen2_handle_system_error(core, packet); + return ret; + } + + if (packet->type > range[i].begin && packet->type < range[i].end) { + ret = range[i].handle(core, packet); + if (ret) + return ret; + + if (packet->type > HFI_SYSTEM_ERROR_BEGIN && + packet->type < HFI_SYSTEM_ERROR_END) + return 0; + } + pkt += packet->size; + } + } + + return 0; +} + +static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst) +{ + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp; + struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp; + u32 bottom_offset = (pixmp_ip->height - inst->crop.height); + u32 right_offset = (pixmp_ip->width - inst->crop.width); + struct hfi_subscription_params *subsc_params; + u32 primaries, matrix_coeff, transfer_char; + u32 colour_description_present_flag = 0; + u32 video_signal_type_present_flag = 0; + u32 full_range, video_format = 0; + u32 left_offset = inst->crop.left; + u32 top_offset = inst->crop.top; + + subsc_params = &inst_hfi_gen2->src_subcr_params; + subsc_params->bitstream_resolution = + pixmp_ip->width << 16 | pixmp_ip->height; + subsc_params->crop_offsets[0] = + left_offset << 16 | top_offset; + subsc_params->crop_offsets[1] = + right_offset << 16 | bottom_offset; + subsc_params->fw_min_count = inst->buffers[BUF_OUTPUT].min_count; + + primaries = iris_hfi_gen2_get_color_primaries(pixmp_op->colorspace); + matrix_coeff = iris_hfi_gen2_get_matrix_coefficients(pixmp_op->ycbcr_enc); + transfer_char = iris_hfi_gen2_get_transfer_char(pixmp_op->xfer_func); + full_range = pixmp_op->quantization == V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0; + subsc_params->color_info = + iris_hfi_gen2_get_color_info(matrix_coeff, transfer_char, primaries, + colour_description_present_flag, + full_range, video_format, + video_signal_type_present_flag); + + subsc_params->profile = inst->fw_caps[PROFILE].value; + subsc_params->level = inst->fw_caps[LEVEL].value; + subsc_params->pic_order_cnt = inst->fw_caps[POC].value; + subsc_params->bit_depth = inst->fw_caps[BIT_DEPTH].value; + if (inst->fw_caps[CODED_FRAMES].value == + CODED_FRAMES_PROGRESSIVE) + subsc_params->coded_frames = HFI_BITMASK_FRAME_MBS_ONLY_FLAG; + else + subsc_params->coded_frames = 0; +} + +static int iris_hfi_gen2_handle_session_response(struct iris_core *core, + struct iris_hfi_header *hdr) +{ + u8 *pkt = (u8 *)((u8 *)hdr + sizeof(*hdr)); + struct iris_inst_hfi_gen2 *inst_hfi_gen2; + struct iris_hfi_packet *packet; + struct iris_inst *inst; + bool dequeue = false; + int ret = 0; + u32 i, j; + static const struct iris_hfi_gen2_inst_hfi_range range[] = { + {HFI_SESSION_ERROR_BEGIN, HFI_SESSION_ERROR_END, + iris_hfi_gen2_handle_session_error}, + {HFI_INFORMATION_BEGIN, HFI_INFORMATION_END, + iris_hfi_gen2_handle_session_info}, + {HFI_PROP_BEGIN, HFI_PROP_END, + iris_hfi_gen2_handle_session_property}, + {HFI_CMD_BEGIN, HFI_CMD_END, + iris_hfi_gen2_handle_session_command }, + }; + + inst = iris_get_instance(core, hdr->session_id); + if (!inst) + return -EINVAL; + + mutex_lock(&inst->lock); + inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + memset(&inst_hfi_gen2->hfi_frame_info, 0, sizeof(struct iris_hfi_frame_info)); + + for (i = 0; i < hdr->num_packets; i++) { + packet = (struct iris_hfi_packet *)pkt; + if (packet->type == HFI_CMD_SETTINGS_CHANGE) { + if (packet->port == HFI_PORT_BITSTREAM) { + iris_hfi_gen2_init_src_change_param(inst); + break; + } + } + pkt += packet->size; + } + + pkt = (u8 *)((u8 *)hdr + sizeof(*hdr)); + for (i = 0; i < ARRAY_SIZE(range); i++) { + pkt = (u8 *)((u8 *)hdr + sizeof(*hdr)); + for (j = 0; j < hdr->num_packets; j++) { + packet = (struct iris_hfi_packet *)pkt; + if (packet->flags & HFI_FW_FLAGS_SESSION_ERROR) + iris_hfi_gen2_handle_session_error(inst, packet); + + if (packet->type > range[i].begin && packet->type < range[i].end) { + dequeue |= (packet->type == HFI_CMD_BUFFER); + ret = range[i].handle(inst, packet); + if (ret) + iris_inst_change_state(inst, IRIS_INST_ERROR); + } + pkt += packet->size; + } + } + + if (dequeue) + iris_hfi_gen2_handle_dequeue_buffers(inst); + + mutex_unlock(&inst->lock); + + return ret; +} + +static int iris_hfi_gen2_handle_response(struct iris_core *core, void *response) +{ + struct iris_hfi_header *hdr = (struct iris_hfi_header *)response; + int ret; + + ret = iris_hfi_gen2_validate_hdr_packet(core, hdr); + if (ret) + return iris_hfi_gen2_handle_system_error(core, NULL); + + if (!hdr->session_id) + return iris_hfi_gen2_handle_system_response(core, hdr); + else + return iris_hfi_gen2_handle_session_response(core, hdr); +} + +static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet) +{ + struct hfi_debug_header *pkt; + u8 *log; + + while (!iris_hfi_queue_dbg_read(core, packet)) { + pkt = (struct hfi_debug_header *)packet; + + if (pkt->size < sizeof(*pkt)) + continue; + + if (pkt->size >= IFACEQ_CORE_PKT_SIZE) + continue; + + packet[pkt->size] = '\0'; + log = (u8 *)packet + sizeof(*pkt) + 1; + dev_dbg(core->dev, "%s", log); + } +} + +static void iris_hfi_gen2_response_handler(struct iris_core *core) +{ + if (iris_vpu_watchdog(core, core->intr_status)) { + struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT}; + + dev_err(core->dev, "cpu watchdog error received\n"); + core->state = IRIS_CORE_ERROR; + iris_hfi_gen2_handle_system_error(core, &pkt); + + return; + } + + memset(core->response_packet, 0, sizeof(struct iris_hfi_header)); + while (!iris_hfi_queue_msg_read(core, core->response_packet)) { + iris_hfi_gen2_handle_response(core, core->response_packet); + memset(core->response_packet, 0, sizeof(struct iris_hfi_header)); + } + + iris_hfi_gen2_flush_debug_queue(core, core->response_packet); +} + +static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = { + .hfi_response_handler = iris_hfi_gen2_response_handler, +}; + +void iris_hfi_gen2_response_ops_init(struct iris_core *core) +{ + core->hfi_response_ops = &iris_hfi_gen2_response_ops; +} |