This implements the handlng of responses sent from firmware to the host driver. Signed-off-by: Dikshita Agarwal <quic_dikshita@xxxxxxxxxxx> Signed-off-by: Vikash Garodia <quic_vgarodia@xxxxxxxxxxx> --- .../qcom/iris/vidc/inc/venus_hfi_response.h | 26 + .../qcom/iris/vidc/src/venus_hfi_response.c | 1607 ++++++++++++++++++++ 2 files changed, 1633 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c diff --git a/drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h b/drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h new file mode 100644 index 0000000..92e6c0e --- /dev/null +++ b/drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __VENUS_HFI_RESPONSE_H__ +#define __VENUS_HFI_RESPONSE_H__ + +#include "hfi_packet.h" + +extern struct msm_vidc_core *g_core; +int handle_response(struct msm_vidc_core *core, + void *response); +int validate_packet(u8 *response_pkt, u8 *core_resp_pkt, + u32 core_resp_pkt_size, const char *func); +bool is_valid_port(struct msm_vidc_inst *inst, u32 port, + const char *func); +bool is_valid_hfi_buffer_type(struct msm_vidc_inst *inst, + u32 buffer_type, const char *func); +int handle_system_error(struct msm_vidc_core *core, + struct hfi_packet *pkt); +int handle_release_output_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer); + +#endif // __VENUS_HFI_RESPONSE_H__ diff --git a/drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c b/drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c new file mode 100644 index 0000000..b12a564 --- /dev/null +++ b/drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c @@ -0,0 +1,1607 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/of_address.h> + +#include "hfi_packet.h" +#include "msm_vdec.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_driver.h" +#include "msm_vidc_memory.h" +#include "msm_vidc_platform.h" +#include "venus_hfi.h" +#include "venus_hfi_response.h" + +#define in_range(range, val) (((range.begin) < (val)) && ((range.end) > (val))) + +struct msm_vidc_core_hfi_range { + u32 begin; + u32 end; + int (*handle)(struct msm_vidc_core *core, struct hfi_packet *pkt); +}; + +struct msm_vidc_inst_hfi_range { + u32 begin; + u32 end; + int (*handle)(struct msm_vidc_inst *inst, struct hfi_packet *pkt); +}; + +struct msm_vidc_hfi_buffer_handle { + enum hfi_buffer_type type; + int (*handle)(struct msm_vidc_inst *inst, struct hfi_buffer *buffer); +}; + +struct msm_vidc_hfi_packet_handle { + enum hfi_buffer_type type; + int (*handle)(struct msm_vidc_inst *inst, struct hfi_packet *pkt); +}; + +void print_psc_properties(const char *str, struct msm_vidc_inst *inst, + struct msm_vidc_subscription_params subsc_params) +{ + i_vpr_h(inst, + "%s: w %d, h %d, crop: offsets[0] %#x offsets[1] %#x, bit depth %#x, coded frames %d, fw min count %d, poc %d, color info %d, profile %d, level %d, tier %d\n", + str, (subsc_params.bitstream_resolution & HFI_BITMASK_BITSTREAM_WIDTH) >> 16, + (subsc_params.bitstream_resolution & HFI_BITMASK_BITSTREAM_HEIGHT), + subsc_params.crop_offsets[0], subsc_params.crop_offsets[1], + subsc_params.bit_depth, subsc_params.coded_frames, + subsc_params.fw_min_count, subsc_params.pic_order_cnt, + subsc_params.color_info, subsc_params.profile, subsc_params.level, + subsc_params.tier); +} + +static void print_sfr_message(struct msm_vidc_core *core) +{ + struct msm_vidc_sfr *vsfr = NULL; + u32 vsfr_size = 0; + void *p = NULL; + + vsfr = (struct msm_vidc_sfr *)core->sfr.align_virtual_addr; + if (vsfr) { + if (vsfr->bufsize != core->sfr.mem_size) { + d_vpr_e("Invalid SFR buf size %d actual %d\n", + vsfr->bufsize, core->sfr.mem_size); + return; + } + vsfr_size = vsfr->bufsize - sizeof(u32); + p = memchr(vsfr->rg_data, '\0', vsfr_size); + /* SFR isn't guaranteed to be NULL terminated */ + if (!p) + vsfr->rg_data[vsfr_size - 1] = '\0'; + + d_vpr_e(FMT_STRING_MSG_SFR, vsfr->rg_data); + } +} + +u32 vidc_port_from_hfi(struct msm_vidc_inst *inst, + enum hfi_packet_port_type hfi_port) +{ + enum msm_vidc_port_type port = MAX_PORT; + + if (is_decode_session(inst)) { + switch (hfi_port) { + case HFI_PORT_BITSTREAM: + port = INPUT_PORT; + break; + case HFI_PORT_RAW: + port = OUTPUT_PORT; + break; + case HFI_PORT_NONE: + port = PORT_NONE; + break; + default: + i_vpr_e(inst, "%s: invalid hfi port type %d\n", + __func__, hfi_port); + break; + } + } else if (is_encode_session(inst)) { + switch (hfi_port) { + case HFI_PORT_RAW: + port = INPUT_PORT; + break; + case HFI_PORT_BITSTREAM: + port = OUTPUT_PORT; + break; + case HFI_PORT_NONE: + port = PORT_NONE; + break; + default: + i_vpr_e(inst, "%s: invalid hfi port type %d\n", + __func__, hfi_port); + break; + } + } else { + i_vpr_e(inst, "%s: invalid domain %#x\n", + __func__, inst->domain); + } + + return port; +} + +bool is_valid_hfi_port(struct msm_vidc_inst *inst, u32 port, + u32 buffer_type, const char *func) +{ + if (port == HFI_PORT_NONE && + buffer_type != HFI_BUFFER_ARP && + buffer_type != HFI_BUFFER_PERSIST) + goto invalid; + + if (port != HFI_PORT_BITSTREAM && port != HFI_PORT_RAW) + goto invalid; + + return true; + +invalid: + i_vpr_e(inst, "%s: invalid port %#x buffer_type %u\n", + func, port, buffer_type); + return false; +} + +bool is_valid_hfi_buffer_type(struct msm_vidc_inst *inst, + u32 buffer_type, const char *func) +{ + if (buffer_type != HFI_BUFFER_BITSTREAM && + buffer_type != HFI_BUFFER_RAW && + buffer_type != HFI_BUFFER_BIN && + buffer_type != HFI_BUFFER_ARP && + buffer_type != HFI_BUFFER_COMV && + buffer_type != HFI_BUFFER_NON_COMV && + buffer_type != HFI_BUFFER_LINE && + buffer_type != HFI_BUFFER_DPB && + buffer_type != HFI_BUFFER_PERSIST && + buffer_type != HFI_BUFFER_VPSS) { + i_vpr_e(inst, "%s: invalid buffer type %#x\n", + func, buffer_type); + return false; + } + return true; +} + +int validate_packet(u8 *response_pkt, u8 *core_resp_pkt, + u32 core_resp_pkt_size, const char *func) +{ + u8 *response_limit; + u32 response_pkt_size = 0; + + if (!response_pkt || !core_resp_pkt || !core_resp_pkt_size) { + d_vpr_e("%s: invalid params\n", func); + return -EINVAL; + } + + response_limit = core_resp_pkt + core_resp_pkt_size; + + if (response_pkt < core_resp_pkt || response_pkt > response_limit) { + d_vpr_e("%s: invalid packet address\n", func); + return -EINVAL; + } + + response_pkt_size = *(u32 *)response_pkt; + if (!response_pkt_size) { + d_vpr_e("%s: response packet size cannot be zero\n", func); + return -EINVAL; + } + + if (response_pkt_size < sizeof(struct hfi_packet)) { + d_vpr_e("%s: invalid packet size %d\n", + func, response_pkt_size); + return -EINVAL; + } + + if (response_pkt + response_pkt_size > response_limit) { + d_vpr_e("%s: invalid packet size %d\n", + func, response_pkt_size); + return -EINVAL; + } + return 0; +} + +static int validate_hdr_packet(struct msm_vidc_core *core, + struct hfi_header *hdr, const char *function) +{ + struct hfi_packet *packet; + u8 *pkt; + int i, rc = 0; + + if (hdr->size < sizeof(struct hfi_header) + sizeof(struct hfi_packet)) { + d_vpr_e("%s: invalid header size %d\n", __func__, hdr->size); + return -EINVAL; + } + + pkt = (u8 *)((u8 *)hdr + sizeof(struct hfi_header)); + + /* validate all packets */ + for (i = 0; i < hdr->num_packets; i++) { + packet = (struct hfi_packet *)pkt; + rc = validate_packet(pkt, core->response_packet, core->packet_size, function); + if (rc) + return rc; + + pkt += packet->size; + } + + return 0; +} + +static bool check_for_packet_payload(struct msm_vidc_inst *inst, + struct hfi_packet *pkt, const char *func) +{ + u32 payload_size = 0; + + if (pkt->payload_info == HFI_PAYLOAD_NONE) { + i_vpr_h(inst, "%s: no playload available for packet %#x\n", + func, pkt->type); + return false; + } + + 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 hfi_buffer); + break; + default: + payload_size = 0; + break; + } + + if (pkt->size < sizeof(struct hfi_packet) + payload_size) { + i_vpr_e(inst, + "%s: invalid payload size %u payload type %#x for packet %#x\n", + func, pkt->size, pkt->payload_info, pkt->type); + return false; + } + + return true; +} + +static int handle_session_last_flag_info(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int rc = 0; + + if (pkt->type == HFI_INFO_HFI_FLAG_PSC_LAST) { + if (msm_vidc_allow_psc_last_flag(inst)) + rc = msm_vidc_process_psc_last_flag(inst); + else + rc = -EINVAL; + } else if (pkt->type == HFI_INFO_HFI_FLAG_DRAIN_LAST) { + if (msm_vidc_allow_drain_last_flag(inst)) + rc = msm_vidc_process_drain_last_flag(inst); + else + rc = -EINVAL; + } else { + i_vpr_e(inst, "%s: invalid packet type %#x\n", __func__, + pkt->type); + } + + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + + return rc; +} + +static int handle_session_info(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int rc = 0; + char *info; + + switch (pkt->type) { + case HFI_INFO_UNSUPPORTED: + info = "unsupported"; + break; + case HFI_INFO_DATA_CORRUPT: + info = "data corrupt"; + inst->hfi_frame_info.data_corrupt = 1; + break; + case HFI_INFO_BUFFER_OVERFLOW: + info = "buffer overflow"; + inst->hfi_frame_info.overflow = 1; + break; + case HFI_INFO_HFI_FLAG_DRAIN_LAST: + info = "drain last flag"; + rc = handle_session_last_flag_info(inst, pkt); + break; + case HFI_INFO_HFI_FLAG_PSC_LAST: + info = "drc last flag"; + rc = handle_session_last_flag_info(inst, pkt); + break; + default: + info = "unknown"; + break; + } + + i_vpr_h(inst, "session info (%#x): %s\n", pkt->type, info); + + return rc; +} + +static int handle_session_error(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int rc = 0; + 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; + default: + error = "unknown"; + break; + } + + i_vpr_e(inst, "%s: session error received %#x: %s\n", + __func__, pkt->type, error); + + rc = msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + return rc; +} + +int handle_system_error(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + d_vpr_e("%s: system error received\n", __func__); + print_sfr_message(core); + + msm_vidc_core_deinit(core, true); + + return 0; +} + +static int handle_system_init(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) { + d_vpr_h("%s: unhandled. flags=%d\n", __func__, pkt->flags); + return 0; + } + + core_lock(core, __func__); + if (pkt->packet_id != core->sys_init_id) { + d_vpr_e("%s: invalid pkt id %u, expected %u\n", __func__, + pkt->packet_id, core->sys_init_id); + goto unlock; + } + + msm_vidc_change_core_state(core, MSM_VIDC_CORE_INIT, __func__); + d_vpr_h("%s: successful\n", __func__); + +unlock: + core_unlock(core, __func__); + return 0; +} + +static int handle_session_open(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful\n", __func__); + + return 0; +} + +static int handle_session_close(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful\n", __func__); + + signal_session_msg_receipt(inst, SIGNAL_CMD_CLOSE); + return 0; +} + +static int handle_session_start(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful for port %d\n", + __func__, pkt->port); + return 0; +} + +static int handle_session_stop(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int rc = 0; + enum signal_session_response signal_type = -1; + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful for port %d\n", + __func__, pkt->port); + + if (is_encode_session(inst)) { + if (pkt->port == HFI_PORT_RAW) { + signal_type = SIGNAL_CMD_STOP_INPUT; + } else if (pkt->port == HFI_PORT_BITSTREAM) { + signal_type = SIGNAL_CMD_STOP_OUTPUT; + } else { + i_vpr_e(inst, "%s: invalid port: %d\n", + __func__, pkt->port); + return -EINVAL; + } + } else if (is_decode_session(inst)) { + if (pkt->port == HFI_PORT_RAW) { + signal_type = SIGNAL_CMD_STOP_OUTPUT; + } else if (pkt->port == HFI_PORT_BITSTREAM) { + signal_type = SIGNAL_CMD_STOP_INPUT; + } else { + i_vpr_e(inst, "%s: invalid port: %d\n", + __func__, pkt->port); + return -EINVAL; + } + } else { + i_vpr_e(inst, "%s: invalid session\n", __func__); + return -EINVAL; + } + + if (signal_type != -1) { + rc = msm_vidc_process_stop_done(inst, signal_type); + if (rc) + return rc; + } + + return 0; +} + +static int handle_session_drain(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful\n", __func__); + + return msm_vidc_process_drain_done(inst); +} + +static int get_driver_buffer_flags(struct msm_vidc_inst *inst, u32 hfi_flags) +{ + u32 driver_flags = 0; + + if (inst->hfi_frame_info.picture_type & HFI_PICTURE_IDR) + driver_flags |= MSM_VIDC_BUF_FLAG_KEYFRAME; + else if (inst->hfi_frame_info.picture_type & HFI_PICTURE_P) + driver_flags |= MSM_VIDC_BUF_FLAG_PFRAME; + else if (inst->hfi_frame_info.picture_type & HFI_PICTURE_B) + driver_flags |= MSM_VIDC_BUF_FLAG_BFRAME; + else if (inst->hfi_frame_info.picture_type & HFI_PICTURE_I) + driver_flags |= MSM_VIDC_BUF_FLAG_KEYFRAME; + else if (inst->hfi_frame_info.picture_type & HFI_PICTURE_CRA) + driver_flags |= MSM_VIDC_BUF_FLAG_KEYFRAME; + else if (inst->hfi_frame_info.picture_type & HFI_PICTURE_BLA) + driver_flags |= MSM_VIDC_BUF_FLAG_KEYFRAME; + + if (inst->hfi_frame_info.data_corrupt) + driver_flags |= MSM_VIDC_BUF_FLAG_ERROR; + + if (inst->hfi_frame_info.overflow) + driver_flags |= MSM_VIDC_BUF_FLAG_ERROR; + + if ((is_encode_session(inst) && + (hfi_flags & HFI_BUF_FW_FLAG_LAST)) || + (is_decode_session(inst) && + ((hfi_flags & HFI_BUF_FW_FLAG_LAST) || + (hfi_flags & HFI_BUF_FW_FLAG_PSC_LAST)))) + driver_flags |= MSM_VIDC_BUF_FLAG_LAST; + + return driver_flags; +} + +static int handle_read_only_buffer(struct msm_vidc_inst *inst, + struct msm_vidc_buffer *buf) +{ + struct msm_vidc_buffer *ro_buf; + struct msm_vidc_core *core; + bool found = false; + + core = inst->core; + + if (!is_decode_session(inst) || !is_output_buffer(buf->type)) + return 0; + + if (!(buf->attr & MSM_VIDC_ATTR_READ_ONLY)) + return 0; + + list_for_each_entry(ro_buf, &inst->buffers.read_only.list, list) { + if (ro_buf->device_addr == buf->device_addr) { + found = true; + break; + } + } + /* + * RO flag: add to read_only list if buffer is not present + * if present, do nothing + */ + if (!found) { + ro_buf = msm_vidc_pool_alloc(inst, MSM_MEM_POOL_BUFFER); + if (!ro_buf) { + i_vpr_e(inst, "%s: buffer alloc failed\n", __func__); + return -ENOMEM; + } + ro_buf->index = -1; + ro_buf->inst = inst; + ro_buf->type = buf->type; + ro_buf->fd = buf->fd; + ro_buf->dmabuf = buf->dmabuf; + ro_buf->device_addr = buf->device_addr; + ro_buf->data_offset = buf->data_offset; + ro_buf->dbuf_get = buf->dbuf_get; + buf->dbuf_get = 0; + INIT_LIST_HEAD(&ro_buf->list); + list_add_tail(&ro_buf->list, &inst->buffers.read_only.list); + print_vidc_buffer(VIDC_LOW, "low ", "ro buf added", inst, ro_buf); + } else { + print_vidc_buffer(VIDC_LOW, "low ", "ro buf found", inst, ro_buf); + } + ro_buf->attr |= MSM_VIDC_ATTR_READ_ONLY; + + return 0; +} + +static int handle_non_read_only_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + struct msm_vidc_buffer *ro_buf; + + if (!is_decode_session(inst) || buffer->type != HFI_BUFFER_RAW) + return 0; + + if (buffer->flags & HFI_BUF_FW_FLAG_READONLY) + return 0; + + list_for_each_entry(ro_buf, &inst->buffers.read_only.list, list) { + if (ro_buf->device_addr == buffer->base_address) { + ro_buf->attr &= ~MSM_VIDC_ATTR_READ_ONLY; + break; + } + } + + return 0; +} + +static int handle_psc_last_flag_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + if (!(buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST)) + return 0; + + if (!msm_vidc_allow_psc_last_flag(inst)) + return -EINVAL; + + return msm_vidc_process_psc_last_flag(inst); +} + +static int handle_drain_last_flag_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + int rc = 0; + + if (!(buffer->flags & HFI_BUF_FW_FLAG_LAST)) + return 0; + + if (!msm_vidc_allow_drain_last_flag(inst)) + return -EINVAL; + + if (is_decode_session(inst)) { + rc = msm_vidc_process_drain_last_flag(inst); + if (rc) + return rc; + } else if (is_encode_session(inst)) { + rc = msm_vidc_state_change_drain_last_flag(inst); + if (rc) + return rc; + } + + return rc; +} + +static int handle_input_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + int rc = 0; + struct msm_vidc_buffers *buffers; + struct msm_vidc_buffer *buf; + struct msm_vidc_core *core; + bool found; + + core = inst->core; + buffers = msm_vidc_get_buffers(inst, MSM_VIDC_BUF_INPUT, __func__); + if (!buffers) + return -EINVAL; + + found = false; + list_for_each_entry(buf, &buffers->list, list) { + if (buf->index == buffer->index) { + found = true; + break; + } + } + if (!found) { + i_vpr_e(inst, "%s: invalid buffer idx %d addr %#llx data_offset %d\n", + __func__, buffer->index, buffer->base_address, + buffer->data_offset); + return -EINVAL; + } + + if (!(buf->attr & MSM_VIDC_ATTR_QUEUED)) { + print_vidc_buffer(VIDC_ERR, "err ", "not queued", inst, buf); + return 0; + } + + buf->data_size = buffer->data_size; + buf->attr &= ~MSM_VIDC_ATTR_QUEUED; + buf->attr |= MSM_VIDC_ATTR_DEQUEUED; + + buf->flags = 0; + buf->flags = get_driver_buffer_flags(inst, buffer->flags); + + print_vidc_buffer(VIDC_HIGH, "high", "dqbuf", inst, buf); + msm_vidc_update_stats(inst, buf, MSM_VIDC_DEBUGFS_EVENT_EBD); + + return rc; +} + +static int handle_output_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + int rc = 0; + struct msm_vidc_buffers *buffers; + struct msm_vidc_buffer *buf; + struct msm_vidc_core *core; + bool found, fatal = false; + + core = inst->core; + + /* handle drain last flag buffer */ + if (buffer->flags & HFI_BUF_FW_FLAG_LAST) { + rc = handle_drain_last_flag_buffer(inst, buffer); + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } + + if (is_decode_session(inst)) { + /* handle release response for decoder output buffer */ + if (buffer->flags & HFI_BUF_FW_FLAG_RELEASE_DONE) + return handle_release_output_buffer(inst, buffer); + /* handle psc last flag buffer */ + if (buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST) { + rc = handle_psc_last_flag_buffer(inst, buffer); + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } + /* handle non-read only buffer */ + if (!(buffer->flags & HFI_BUF_FW_FLAG_READONLY)) { + rc = handle_non_read_only_buffer(inst, buffer); + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } + } + + buffers = msm_vidc_get_buffers(inst, MSM_VIDC_BUF_OUTPUT, __func__); + if (!buffers) + return -EINVAL; + + found = false; + list_for_each_entry(buf, &buffers->list, list) { + if (!(buf->attr & MSM_VIDC_ATTR_QUEUED)) + continue; + if (is_decode_session(inst)) + found = (buf->index == buffer->index && + buf->device_addr == buffer->base_address && + buf->data_offset == buffer->data_offset); + else + found = (buf->index == buffer->index); + + if (found) + break; + } + if (!found) { + i_vpr_l(inst, "%s: invalid idx %d daddr %#llx\n", + __func__, buffer->index, buffer->base_address); + return 0; + } + + buf->data_offset = buffer->data_offset; + buf->data_size = buffer->data_size; + buf->timestamp = buffer->timestamp; + + buf->attr &= ~MSM_VIDC_ATTR_QUEUED; + buf->attr |= MSM_VIDC_ATTR_DEQUEUED; + + if (is_encode_session(inst)) { + /* encoder output is not expected to be corrupted */ + if (inst->hfi_frame_info.data_corrupt) { + i_vpr_e(inst, "%s: encode output is corrupted\n", __func__); + fatal = true; + } + if (inst->hfi_frame_info.overflow) { + /* overflow not expected for cbr_cfr session */ + if (!buffer->data_size && inst->hfi_rc_type == HFI_RC_CBR_CFR) { + i_vpr_e(inst, "%s: overflow detected for cbr_cfr session\n", + __func__); + fatal = true; + } + } + if (fatal) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } + + /* + * reset data size to zero for last flag buffer. + * reset RO flag for last flag buffer. + */ + if ((buffer->flags & HFI_BUF_FW_FLAG_LAST) || + (buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST)) { + if (buffer->data_size) { + i_vpr_e(inst, "%s: reset data size to zero for last flag buffer\n", + __func__); + buf->data_size = 0; + } + if (buffer->flags & HFI_BUF_FW_FLAG_READONLY) { + i_vpr_e(inst, "%s: reset RO flag for last flag buffer\n", + __func__); + buffer->flags &= ~HFI_BUF_FW_FLAG_READONLY; + } + } + + if (is_decode_session(inst)) { + /* RO flag is not expected when internal dpb buffers are allocated */ + if (inst->buffers.dpb.size && buffer->flags & HFI_BUF_FW_FLAG_READONLY) { + print_vidc_buffer(VIDC_ERR, "err ", "unexpected RO flag", inst, buf); + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } + + if (buffer->flags & HFI_BUF_FW_FLAG_READONLY) { + buf->attr |= MSM_VIDC_ATTR_READ_ONLY; + rc = handle_read_only_buffer(inst, buf); + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } else { + buf->attr &= ~MSM_VIDC_ATTR_READ_ONLY; + } + } + + buf->flags = 0; + buf->flags = get_driver_buffer_flags(inst, buffer->flags); + + if (is_decode_session(inst)) { + inst->power.fw_cr = inst->hfi_frame_info.cr; + inst->power.fw_cf = inst->hfi_frame_info.cf; + } else { + inst->power.fw_cr = inst->hfi_frame_info.cr; + } + + if (is_decode_session(inst) && buf->data_size) + msm_vidc_update_timestamp_rate(inst, buf->timestamp); + + print_vidc_buffer(VIDC_HIGH, "high", "dqbuf", inst, buf); + msm_vidc_update_stats(inst, buf, MSM_VIDC_DEBUGFS_EVENT_FBD); + + return rc; +} + +static int handle_dequeue_buffers(struct msm_vidc_inst *inst) +{ + int rc = 0; + int i; + struct msm_vidc_buffers *buffers; + struct msm_vidc_buffer *buf; + struct msm_vidc_buffer *dummy; + struct msm_vidc_core *core; + static const enum msm_vidc_buffer_type buffer_type[] = { + MSM_VIDC_BUF_INPUT, + MSM_VIDC_BUF_OUTPUT, + }; + + core = inst->core; + for (i = 0; i < ARRAY_SIZE(buffer_type); i++) { + buffers = msm_vidc_get_buffers(inst, buffer_type[i], __func__); + if (!buffers) + return -EINVAL; + + list_for_each_entry_safe(buf, dummy, &buffers->list, list) { + if (buf->attr & MSM_VIDC_ATTR_DEQUEUED) { + buf->attr &= ~MSM_VIDC_ATTR_DEQUEUED; + /* + * do not send vb2_buffer_done when fw returns + * same buffer again + */ + if (buf->attr & MSM_VIDC_ATTR_BUFFER_DONE) { + print_vidc_buffer(VIDC_HIGH, "high", + "vb2 done already", + inst, buf); + } else { + buf->attr |= MSM_VIDC_ATTR_BUFFER_DONE; + if (buf->dbuf_get) { + call_mem_op(core, dma_buf_put, inst, buf->dmabuf); + buf->dbuf_get = 0; + } + rc = msm_vidc_vb2_buffer_done(inst, buf); + if (rc) { + print_vidc_buffer(VIDC_HIGH, "err ", + "vb2 done failed", + inst, buf); + /* ignore the error */ + rc = 0; + } + } + } + } + } + + return rc; +} + +static int handle_release_internal_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + int rc = 0; + struct msm_vidc_buffers *buffers; + struct msm_vidc_buffer *buf; + bool found; + + buffers = msm_vidc_get_buffers(inst, hfi_buf_type_to_driver(inst->domain, buffer->type, + HFI_PORT_NONE), __func__); + if (!buffers) + return -EINVAL; + + found = false; + list_for_each_entry(buf, &buffers->list, list) { + if (buf->device_addr == buffer->base_address) { + found = true; + break; + } + } + if (!found) { + i_vpr_e(inst, "%s: invalid idx %d daddr %#llx\n", + __func__, buffer->index, buffer->base_address); + return -EINVAL; + } + + if (!is_internal_buffer(buf->type)) + return 0; + + /* remove QUEUED attribute */ + buf->attr &= ~MSM_VIDC_ATTR_QUEUED; + + /* + * firmware will return/release internal buffer in two cases + * - driver sent release cmd in which case driver should destroy the buffer + * - as part stop cmd in which case driver can reuse the buffer, so skip + * destroying the buffer + */ + if (buf->attr & MSM_VIDC_ATTR_PENDING_RELEASE) { + rc = msm_vidc_destroy_internal_buffer(inst, buf); + if (rc) + return rc; + } + return rc; +} + +int handle_release_output_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ + int rc = 0; + struct msm_vidc_buffer *buf; + bool found = false; + + list_for_each_entry(buf, &inst->buffers.read_only.list, list) { + if (buf->device_addr == buffer->base_address && + buf->attr & MSM_VIDC_ATTR_PENDING_RELEASE) { + found = true; + break; + } + } + if (!found) { + i_vpr_e(inst, "%s: invalid idx %d daddr %#llx\n", + __func__, buffer->index, buffer->base_address); + return -EINVAL; + } + + buf->attr &= ~MSM_VIDC_ATTR_READ_ONLY; + buf->attr &= ~MSM_VIDC_ATTR_PENDING_RELEASE; + print_vidc_buffer(VIDC_LOW, "low ", "release done", inst, buf); + + return rc; +} + +static int handle_session_buffer(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int i, rc = 0; + struct hfi_buffer *buffer; + u32 hfi_handle_size = 0; + const struct msm_vidc_hfi_buffer_handle *hfi_handle_arr = NULL; + static const struct msm_vidc_hfi_buffer_handle enc_input_hfi_handle[] = { + {HFI_BUFFER_RAW, handle_input_buffer }, + {HFI_BUFFER_VPSS, handle_release_internal_buffer }, + }; + static const struct msm_vidc_hfi_buffer_handle enc_output_hfi_handle[] = { + {HFI_BUFFER_BITSTREAM, handle_output_buffer }, + {HFI_BUFFER_BIN, handle_release_internal_buffer }, + {HFI_BUFFER_COMV, handle_release_internal_buffer }, + {HFI_BUFFER_NON_COMV, handle_release_internal_buffer }, + {HFI_BUFFER_LINE, handle_release_internal_buffer }, + {HFI_BUFFER_ARP, handle_release_internal_buffer }, + {HFI_BUFFER_DPB, handle_release_internal_buffer }, + }; + static const struct msm_vidc_hfi_buffer_handle dec_input_hfi_handle[] = { + {HFI_BUFFER_BITSTREAM, handle_input_buffer }, + {HFI_BUFFER_BIN, handle_release_internal_buffer }, + {HFI_BUFFER_COMV, handle_release_internal_buffer }, + {HFI_BUFFER_NON_COMV, handle_release_internal_buffer }, + {HFI_BUFFER_LINE, handle_release_internal_buffer }, + {HFI_BUFFER_PERSIST, handle_release_internal_buffer }, + }; + static const struct msm_vidc_hfi_buffer_handle dec_output_hfi_handle[] = { + {HFI_BUFFER_RAW, handle_output_buffer }, + {HFI_BUFFER_DPB, handle_release_internal_buffer }, + }; + + if (pkt->payload_info == HFI_PAYLOAD_NONE) { + i_vpr_h(inst, "%s: received hfi buffer packet without payload\n", + __func__); + return 0; + } + + if (!check_for_packet_payload(inst, pkt, __func__)) { + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + return 0; + } + + buffer = (struct hfi_buffer *)((u8 *)pkt + sizeof(struct hfi_packet)); + if (!is_valid_hfi_buffer_type(inst, buffer->type, __func__)) { + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + return 0; + } + + if (!is_valid_hfi_port(inst, pkt->port, buffer->type, __func__)) { + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + return 0; + } + + if (is_encode_session(inst)) { + if (pkt->port == HFI_PORT_RAW) { + hfi_handle_size = ARRAY_SIZE(enc_input_hfi_handle); + hfi_handle_arr = enc_input_hfi_handle; + } else if (pkt->port == HFI_PORT_BITSTREAM) { + hfi_handle_size = ARRAY_SIZE(enc_output_hfi_handle); + hfi_handle_arr = enc_output_hfi_handle; + } + } else if (is_decode_session(inst)) { + if (pkt->port == HFI_PORT_BITSTREAM) { + hfi_handle_size = ARRAY_SIZE(dec_input_hfi_handle); + hfi_handle_arr = dec_input_hfi_handle; + } else if (pkt->port == HFI_PORT_RAW) { + hfi_handle_size = ARRAY_SIZE(dec_output_hfi_handle); + hfi_handle_arr = dec_output_hfi_handle; + } + } + + /* handle invalid session */ + if (!hfi_handle_arr || !hfi_handle_size) { + i_vpr_e(inst, "%s: invalid session %d\n", __func__, inst->domain); + return -EINVAL; + } + + /* handle session buffer */ + for (i = 0; i < hfi_handle_size; i++) { + if (hfi_handle_arr[i].type == buffer->type) { + rc = hfi_handle_arr[i].handle(inst, buffer); + if (rc) + return rc; + break; + } + } + + /* handle unknown buffer type */ + if (i == hfi_handle_size) { + i_vpr_e(inst, "%s: port %u, unknown buffer type %#x\n", __func__, + pkt->port, buffer->type); + return -EINVAL; + } + + return rc; +} + +static int handle_input_port_settings_change(struct msm_vidc_inst *inst) +{ + int rc = 0; + enum msm_vidc_allow allow = MSM_VIDC_DISALLOW; + + allow = msm_vidc_allow_input_psc(inst); + if (allow == MSM_VIDC_DISALLOW) { + return -EINVAL; + } else if (allow == MSM_VIDC_ALLOW) { + rc = msm_vidc_state_change_input_psc(inst); + if (rc) + return rc; + print_psc_properties("INPUT_PSC", inst, inst->subcr_params[INPUT_PORT]); + rc = msm_vdec_input_port_settings_change(inst); + if (rc) + return rc; + } + + return rc; +} + +static int handle_output_port_settings_change(struct msm_vidc_inst *inst) +{ + print_psc_properties("OUTPUT_PSC", inst, inst->subcr_params[OUTPUT_PORT]); + + return 0; +} + +static int handle_port_settings_change(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int rc = 0; + + i_vpr_h(inst, "%s: Received port settings change, type %d\n", + __func__, pkt->port); + + if (pkt->port == HFI_PORT_RAW) { + rc = handle_output_port_settings_change(inst); + if (rc) + goto exit; + } else if (pkt->port == HFI_PORT_BITSTREAM) { + rc = handle_input_port_settings_change(inst); + if (rc) + goto exit; + } else { + i_vpr_e(inst, "%s: invalid port type: %#x\n", + __func__, pkt->port); + rc = -EINVAL; + goto exit; + } + +exit: + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + return rc; +} + +static int handle_session_subscribe_mode(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful\n", __func__); + return 0; +} + +static int handle_session_pause(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful\n", __func__); + return 0; +} + +static int handle_session_resume(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + i_vpr_h(inst, "%s: successful\n", __func__); + return 0; +} + +static int handle_session_command(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int i, rc; + static const struct msm_vidc_hfi_packet_handle hfi_pkt_handle[] = { + {HFI_CMD_OPEN, handle_session_open }, + {HFI_CMD_CLOSE, handle_session_close }, + {HFI_CMD_START, handle_session_start }, + {HFI_CMD_STOP, handle_session_stop }, + {HFI_CMD_DRAIN, handle_session_drain }, + {HFI_CMD_BUFFER, handle_session_buffer }, + {HFI_CMD_SETTINGS_CHANGE, handle_port_settings_change }, + {HFI_CMD_SUBSCRIBE_MODE, handle_session_subscribe_mode }, + {HFI_CMD_PAUSE, handle_session_pause }, + {HFI_CMD_RESUME, handle_session_resume }, + }; + + /* handle session pkt */ + for (i = 0; i < ARRAY_SIZE(hfi_pkt_handle); i++) { + if (hfi_pkt_handle[i].type == pkt->type) { + rc = hfi_pkt_handle[i].handle(inst, pkt); + if (rc) + return rc; + break; + } + } + + /* handle unknown buffer type */ + if (i == ARRAY_SIZE(hfi_pkt_handle)) { + i_vpr_e(inst, "%s: Unsupported command type: %#x\n", __func__, pkt->type); + return -EINVAL; + } + + return 0; +} + +static int handle_dpb_list_property(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + u32 payload_size, num_words_in_payload; + u8 *payload_start; + int i = 0; + struct msm_vidc_buffer *ro_buf; + bool found = false; + u64 device_addr; + + if (!is_decode_session(inst)) { + i_vpr_e(inst, + "%s: unsupported for non-decode session\n", __func__); + return -EINVAL; + } + + payload_size = pkt->size - sizeof(struct hfi_packet); + num_words_in_payload = payload_size / 4; + payload_start = (u8 *)((u8 *)pkt + sizeof(struct hfi_packet)); + memset(inst->dpb_list_payload, 0, MAX_DPB_LIST_ARRAY_SIZE); + + if (payload_size > MAX_DPB_LIST_PAYLOAD_SIZE) { + i_vpr_e(inst, + "%s: dpb list payload size %d exceeds expected max size %d\n", + __func__, payload_size, MAX_DPB_LIST_PAYLOAD_SIZE); + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + return -EINVAL; + } + memcpy(inst->dpb_list_payload, payload_start, payload_size); + + /* + * dpb_list_payload details: + * payload[0-1] : 64 bits base_address of DPB-1 + * payload[2] : 32 bits addr_offset of DPB-1 + * payload[3] : 32 bits data_offset of DPB-1 + */ + for (i = 0; (i + 3) < num_words_in_payload; i = i + 4) { + i_vpr_l(inst, + "%s: base addr %#x %#x, addr offset %#x, data offset %#x\n", + __func__, inst->dpb_list_payload[i], inst->dpb_list_payload[i + 1], + inst->dpb_list_payload[i + 2], inst->dpb_list_payload[i + 3]); + } + + list_for_each_entry(ro_buf, &inst->buffers.read_only.list, list) { + found = false; + /* do not mark RELEASE_ELIGIBLE for non-read only buffers */ + if (!(ro_buf->attr & MSM_VIDC_ATTR_READ_ONLY)) + continue; + /* no need to mark RELEASE_ELIGIBLE again */ + if (ro_buf->attr & MSM_VIDC_ATTR_RELEASE_ELIGIBLE) + continue; + /* + * do not add RELEASE_ELIGIBLE to buffers for which driver + * sent release cmd already + */ + if (ro_buf->attr & MSM_VIDC_ATTR_PENDING_RELEASE) + continue; + for (i = 0; (i + 3) < num_words_in_payload; i = i + 4) { + device_addr = *((u64 *)(&inst->dpb_list_payload[i])); + if (ro_buf->device_addr == device_addr) { + found = true; + break; + } + } + /* mark a buffer as RELEASE_ELIGIBLE if not found in dpb list */ + if (!found) + ro_buf->attr |= MSM_VIDC_ATTR_RELEASE_ELIGIBLE; + } + + return 0; +} + +static int handle_property_with_payload(struct msm_vidc_inst *inst, + struct hfi_packet *pkt, u32 port) +{ + int rc = 0; + u32 *payload_ptr = NULL; + + payload_ptr = (u32 *)((u8 *)pkt + sizeof(struct hfi_packet)); + if (!payload_ptr) { + i_vpr_e(inst, + "%s: payload_ptr cannot be null\n", __func__); + return -EINVAL; + } + + switch (pkt->type) { + case HFI_PROP_BITSTREAM_RESOLUTION: + inst->subcr_params[port].bitstream_resolution = payload_ptr[0]; + break; + case HFI_PROP_CROP_OFFSETS: + inst->subcr_params[port].crop_offsets[0] = payload_ptr[0]; + inst->subcr_params[port].crop_offsets[1] = payload_ptr[1]; + break; + case HFI_PROP_LUMA_CHROMA_BIT_DEPTH: + inst->subcr_params[port].bit_depth = payload_ptr[0]; + break; + case HFI_PROP_CODED_FRAMES: + inst->subcr_params[port].coded_frames = payload_ptr[0]; + break; + case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT: + inst->subcr_params[port].fw_min_count = payload_ptr[0]; + break; + case HFI_PROP_PIC_ORDER_CNT_TYPE: + inst->subcr_params[port].pic_order_cnt = payload_ptr[0]; + break; + case HFI_PROP_SIGNAL_COLOR_INFO: + inst->subcr_params[port].color_info = payload_ptr[0]; + break; + case HFI_PROP_PROFILE: + inst->subcr_params[port].profile = payload_ptr[0]; + break; + case HFI_PROP_LEVEL: + inst->subcr_params[port].level = payload_ptr[0]; + break; + case HFI_PROP_TIER: + inst->subcr_params[port].tier = payload_ptr[0]; + break; + case HFI_PROP_PICTURE_TYPE: + inst->hfi_frame_info.picture_type = payload_ptr[0]; + if (inst->hfi_frame_info.picture_type & HFI_PICTURE_B) + inst->has_bframe = true; + if (inst->hfi_frame_info.picture_type & HFI_PICTURE_IDR) + inst->iframe = true; + else + inst->iframe = false; + break; + case HFI_PROP_SUBFRAME_INPUT: + if (port != INPUT_PORT) { + i_vpr_e(inst, + "%s: invalid port: %d for property %#x\n", + __func__, pkt->port, pkt->type); + break; + } + inst->hfi_frame_info.subframe_input = 1; + break; + case HFI_PROP_WORST_COMPRESSION_RATIO: + inst->hfi_frame_info.cr = payload_ptr[0]; + break; + case HFI_PROP_WORST_COMPLEXITY_FACTOR: + inst->hfi_frame_info.cf = payload_ptr[0]; + break; + case HFI_PROP_CABAC_SESSION: + if (payload_ptr[0] == 1) + msm_vidc_update_cap_value(inst, ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + __func__); + else + msm_vidc_update_cap_value(inst, ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + __func__); + break; + case HFI_PROP_DPB_LIST: + rc = handle_dpb_list_property(inst, pkt); + if (rc) + break; + break; + case HFI_PROP_QUALITY_MODE: + if (inst->capabilities[QUALITY_MODE].value != payload_ptr[0]) + i_vpr_e(inst, + "%s: fw quality mode(%d) not matching the capability value(%d)\n", + __func__, payload_ptr[0], + inst->capabilities[QUALITY_MODE].value); + break; + case HFI_PROP_STAGE: + if (inst->capabilities[STAGE].value != payload_ptr[0]) + i_vpr_e(inst, + "%s: fw stage mode(%d) not matching the capability value(%d)\n", + __func__, payload_ptr[0], inst->capabilities[STAGE].value); + break; + case HFI_PROP_PIPE: + if (inst->capabilities[PIPE].value != payload_ptr[0]) + i_vpr_e(inst, + "%s: fw pipe mode(%d) not matching the capability value(%d)\n", + __func__, payload_ptr[0], inst->capabilities[PIPE].value); + break; + default: + i_vpr_e(inst, "%s: invalid property %#x\n", + __func__, pkt->type); + break; + } + + return rc; +} + +static int handle_property_without_payload(struct msm_vidc_inst *inst, + struct hfi_packet *pkt, u32 port) +{ + int rc = 0; + + switch (pkt->type) { + case HFI_PROP_DPB_LIST: + /* + * if fw sends dpb list property without payload, + * it means there are no more reference buffers. + */ + rc = handle_dpb_list_property(inst, pkt); + if (rc) + break; + break; + case HFI_PROP_NO_OUTPUT: + if (port != INPUT_PORT) { + i_vpr_e(inst, + "%s: invalid port: %d for property %#x\n", + __func__, pkt->port, pkt->type); + break; + } + i_vpr_h(inst, "received no_output property\n"); + inst->hfi_frame_info.no_output = 1; + break; + default: + i_vpr_e(inst, "%s: invalid property %#x\n", + __func__, pkt->type); + break; + } + + return rc; +} + +static int handle_session_property(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + int rc = 0; + u32 port; + + i_vpr_l(inst, "%s: property type %#x\n", __func__, pkt->type); + + port = vidc_port_from_hfi(inst, pkt->port); + if (port >= MAX_PORT) { + i_vpr_e(inst, + "%s: invalid port: %d for property %#x\n", + __func__, pkt->port, pkt->type); + return -EINVAL; + } + + if (pkt->flags & HFI_FW_FLAGS_INFORMATION) { + i_vpr_h(inst, + "%s: information flag received for property %#x packet\n", + __func__, pkt->type); + return 0; + } + + if (check_for_packet_payload(inst, pkt, __func__)) { + rc = handle_property_with_payload(inst, pkt, port); + if (rc) + return rc; + } else { + rc = handle_property_without_payload(inst, pkt, port); + if (rc) + return rc; + } + + return rc; +} + +static int handle_image_version_property(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + u32 i = 0; + u8 *str_image_version; + u32 req_bytes; + + req_bytes = pkt->size - sizeof(*pkt); + if (req_bytes < VENUS_VERSION_LENGTH - 1) { + d_vpr_e("%s: bad_pkt: %d\n", __func__, req_bytes); + return -EINVAL; + } + str_image_version = (u8 *)pkt + sizeof(struct hfi_packet); + /* + * The version string returned by firmware includes null + * characters at the start and in between. Replace the null + * characters with space, to print the version info. + */ + for (i = 0; i < VENUS_VERSION_LENGTH - 1; i++) { + if (str_image_version[i] != '\0') + core->fw_version[i] = str_image_version[i]; + else + core->fw_version[i] = ' '; + } + core->fw_version[i] = '\0'; + + d_vpr_h("%s: F/W version: %s\n", __func__, core->fw_version); + return 0; +} + +static int handle_system_property(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + int rc = 0; + + switch (pkt->type) { + case HFI_PROP_IMAGE_VERSION: + rc = handle_image_version_property(core, pkt); + break; + default: + d_vpr_h("%s: property type %#x successful\n", + __func__, pkt->type); + break; + } + return rc; +} + +static int handle_system_response(struct msm_vidc_core *core, + struct hfi_header *hdr) +{ + int rc = 0; + struct hfi_packet *packet; + u8 *pkt, *start_pkt; + int i, j; + static const struct msm_vidc_core_hfi_range be[] = { + {HFI_SYSTEM_ERROR_BEGIN, HFI_SYSTEM_ERROR_END, handle_system_error }, + {HFI_PROP_BEGIN, HFI_PROP_END, handle_system_property }, + {HFI_CMD_BEGIN, HFI_CMD_END, handle_system_init }, + }; + + start_pkt = (u8 *)((u8 *)hdr + sizeof(struct hfi_header)); + for (i = 0; i < ARRAY_SIZE(be); i++) { + pkt = start_pkt; + for (j = 0; j < hdr->num_packets; j++) { + packet = (struct hfi_packet *)pkt; + /* handle system error */ + if (packet->flags & HFI_FW_FLAGS_SYSTEM_ERROR) { + d_vpr_e("%s: received system error %#x\n", + __func__, packet->type); + rc = handle_system_error(core, packet); + if (rc) + goto exit; + goto exit; + } + if (in_range(be[i], packet->type)) { + rc = be[i].handle(core, packet); + if (rc) + goto exit; + + /* skip processing anymore packets after system error */ + if (!i) { + d_vpr_e("%s: skip processing anymore packets\n", __func__); + goto exit; + } + } + pkt += packet->size; + } + } + +exit: + return rc; +} + +static int __handle_session_response(struct msm_vidc_inst *inst, + struct hfi_header *hdr) +{ + int rc = 0; + struct hfi_packet *packet; + u8 *pkt, *start_pkt; + bool dequeue = false; + int i, j; + static const struct msm_vidc_inst_hfi_range be[] = { + {HFI_SESSION_ERROR_BEGIN, HFI_SESSION_ERROR_END, handle_session_error }, + {HFI_INFORMATION_BEGIN, HFI_INFORMATION_END, handle_session_info }, + {HFI_PROP_BEGIN, HFI_PROP_END, handle_session_property }, + {HFI_CMD_BEGIN, HFI_CMD_END, handle_session_command }, + }; + + memset(&inst->hfi_frame_info, 0, sizeof(struct msm_vidc_hfi_frame_info)); + start_pkt = (u8 *)((u8 *)hdr + sizeof(struct hfi_header)); + for (i = 0; i < ARRAY_SIZE(be); i++) { + pkt = start_pkt; + for (j = 0; j < hdr->num_packets; j++) { + packet = (struct hfi_packet *)pkt; + /* handle session error */ + if (packet->flags & HFI_FW_FLAGS_SESSION_ERROR) { + i_vpr_e(inst, "%s: received session error %#x\n", + __func__, packet->type); + handle_session_error(inst, packet); + } + if (in_range(be[i], packet->type)) { + dequeue |= (packet->type == HFI_CMD_BUFFER); + rc = be[i].handle(inst, packet); + if (rc) + msm_vidc_change_state(inst, MSM_VIDC_ERROR, __func__); + } + pkt += packet->size; + } + } + + if (dequeue) { + rc = handle_dequeue_buffers(inst); + if (rc) + return rc; + } + memset(&inst->hfi_frame_info, 0, sizeof(struct msm_vidc_hfi_frame_info)); + + return rc; +} + +static int handle_session_response(struct msm_vidc_core *core, + struct hfi_header *hdr) +{ + struct msm_vidc_inst *inst; + struct hfi_packet *packet; + u8 *pkt; + int i, rc = 0; + bool found_ipsc = false; + + inst = get_inst(core, hdr->session_id); + if (!inst) { + d_vpr_e("%s: Invalid inst\n", __func__); + return -EINVAL; + } + + inst_lock(inst, __func__); + /* search for cmd settings change pkt */ + pkt = (u8 *)((u8 *)hdr + sizeof(struct hfi_header)); + for (i = 0; i < hdr->num_packets; i++) { + packet = (struct hfi_packet *)pkt; + if (packet->type == HFI_CMD_SETTINGS_CHANGE) { + if (packet->port == HFI_PORT_BITSTREAM) { + found_ipsc = true; + break; + } + } + pkt += packet->size; + } + + /* if ipsc packet is found, initialise subsc_params */ + if (found_ipsc) + msm_vdec_init_input_subcr_params(inst); + + rc = __handle_session_response(inst, hdr); + if (rc) + goto exit; + +exit: + inst_unlock(inst, __func__); + put_inst(inst); + return rc; +} + +int handle_response(struct msm_vidc_core *core, void *response) +{ + struct hfi_header *hdr; + int rc = 0; + + hdr = (struct hfi_header *)response; + rc = validate_hdr_packet(core, hdr, __func__); + if (rc) { + d_vpr_e("%s: hdr pkt validation failed\n", __func__); + return handle_system_error(core, NULL); + } + + if (!hdr->session_id) + return handle_system_response(core, hdr); + else + return handle_session_response(core, hdr); + + return 0; +} -- 2.7.4