diff --git a/Makefile b/Makefile index d43f30ab6f..76bfd1b513 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,8 @@ msm-vidc-objs := driver/vidc/src/msm_vidc_v4l2.o \ driver/vidc/src/msm_vidc_debug.o \ driver/vidc/src/msm_vidc_memory.o \ driver/vidc/src/venus_hfi.o \ - driver/vidc/src/hfi_packet.o + driver/vidc/src/hfi_packet.o \ + driver/vidc/src/venus_hfi_response.o ifneq ($(CONFIG_ARCH_QTI_VM), y) ifeq ($(CONFIG_ARCH_LAHAINA), y) diff --git a/driver/vidc/inc/hfi_command.h b/driver/vidc/inc/hfi_command.h index c2215f91b3..a5246bef5a 100644 --- a/driver/vidc/inc/hfi_command.h +++ b/driver/vidc/inc/hfi_command.h @@ -127,12 +127,13 @@ struct metapayload_header { }; enum hfi_property_mode_type { - HFI_MODE_NONE = 0, - HFI_MODE_PORT_SETTINGS_CHANGE = BIT(0), - HFI_MODE_PROPERTY = BIT(1), - HFI_MODE_METADATA = BIT(2), + HFI_MODE_NONE = 0, + HFI_MODE_PORT_SETTINGS_CHANGE = BIT(0), + HFI_MODE_PROPERTY = BIT(1), + HFI_MODE_METADATA = BIT(2), }; +#define HFI_CMD_BEGIN 0x0 #define HFI_CMD_INIT 0x1 #define HFI_CMD_POWER_COLLAPSE 0x2 #define HFI_CMD_OPEN 0x3 @@ -145,6 +146,6 @@ enum hfi_property_mode_type { #define HFI_CMD_DELIVERY_MODE 0xA #define HFI_CMD_SUBSCRIBE_MODE 0xB #define HFI_CMD_SETTINGS_CHANGE 0xC - +#define HFI_CMD_END 0xD #endif //__H_HFI_COMMAND_H__ diff --git a/driver/vidc/inc/hfi_packet.h b/driver/vidc/inc/hfi_packet.h index 141f599d9b..29cbf473cf 100644 --- a/driver/vidc/inc/hfi_packet.h +++ b/driver/vidc/inc/hfi_packet.h @@ -43,4 +43,3 @@ int hfi_packet_session_property(struct msm_vidc_inst *inst, u32 pkt_type, u32 flags, u32 port, u32 payload_type, void *payload, u32 payload_size); #endif // _HFI_PACKET_H_ - diff --git a/driver/vidc/inc/msm_vidc_core.h b/driver/vidc/inc/msm_vidc_core.h index 044d466b88..140b57985b 100644 --- a/driver/vidc/inc/msm_vidc_core.h +++ b/driver/vidc/inc/msm_vidc_core.h @@ -96,6 +96,7 @@ struct msm_vidc_core { u32 last_packet_type; u8 *packet; u32 packet_size; + u8 *response_packet; struct v4l2_file_operations *v4l2_file_ops; struct v4l2_ioctl_ops *v4l2_ioctl_ops; struct v4l2_ctrl_ops *v4l2_ctrl_ops; @@ -106,6 +107,7 @@ struct msm_vidc_core { struct msm_vidc_memory_ops *mem_ops; u32 header_id; u32 packet_id; + struct completion init_done; }; #endif // _MSM_VIDC_CORE_H_ diff --git a/driver/vidc/inc/msm_vidc_debug.h b/driver/vidc/inc/msm_vidc_debug.h index aafa681554..c7261465d6 100644 --- a/driver/vidc/inc/msm_vidc_debug.h +++ b/driver/vidc/inc/msm_vidc_debug.h @@ -87,6 +87,13 @@ enum vidc_msg_prio { #define d_vpr_b(__fmt, ...) \ dprintk(VIDC_BUS, DEFAULT_SID, __fmt, ##__VA_ARGS__) +#define dprintk_firmware(__level, __fmt, ...) \ + do { \ + pr_err(FW_DBG_TAG __fmt, \ + "fw", \ + ##__VA_ARGS__); \ + } while (0) + #define MSM_VIDC_ERROR(value) \ do { if (value) \ d_vpr_e("BugOn"); \ diff --git a/driver/vidc/inc/msm_vidc_driver.h b/driver/vidc/inc/msm_vidc_driver.h index 84896a2da4..73aacd64ee 100644 --- a/driver/vidc/inc/msm_vidc_driver.h +++ b/driver/vidc/inc/msm_vidc_driver.h @@ -103,7 +103,9 @@ struct msm_vidc_map_info *msm_vidc_get_map_info(struct msm_vidc_inst *inst, enum msm_vidc_buffer_type buffer_type); struct msm_vidc_alloc_info *msm_vidc_get_alloc_info(struct msm_vidc_inst *inst, enum msm_vidc_buffer_type buffer_type); - +struct msm_vidc_inst *get_inst(struct msm_vidc_core *core, + u32 session_id); +void put_inst(struct msm_vidc_inst *inst); void core_lock(struct msm_vidc_core *core, const char *function); void core_unlock(struct msm_vidc_core *core, const char *function); void inst_lock(struct msm_vidc_inst *inst, const char *function); diff --git a/driver/vidc/inc/msm_vidc_inst.h b/driver/vidc/inc/msm_vidc_inst.h index 13bb322006..c0775a2f46 100644 --- a/driver/vidc/inc/msm_vidc_inst.h +++ b/driver/vidc/inc/msm_vidc_inst.h @@ -110,6 +110,7 @@ struct msm_vidc_inst { struct dentry *debugfs_root; struct msm_vidc_debug debug; struct msm_vidc_inst_capability *capabilities; + struct completion completions[MAX_SIGNAL]; }; #endif // _MSM_VIDC_INST_H_ diff --git a/driver/vidc/inc/msm_vidc_internal.h b/driver/vidc/inc/msm_vidc_internal.h index d95b8f9663..efe5d84e34 100644 --- a/driver/vidc/inc/msm_vidc_internal.h +++ b/driver/vidc/inc/msm_vidc_internal.h @@ -299,6 +299,12 @@ enum profiling_points { MAX_PROFILING_POINTS, }; +enum signal_session_response { + SIGNAL_CMD_STOP = 0, + SIGNAL_CMD_CLOSE, + MAX_SIGNAL, +}; + #define HFI_MASK_QHDR_TX_TYPE 0xFF000000 #define HFI_MASK_QHDR_RX_TYPE 0x00FF0000 #define HFI_MASK_QHDR_PRI_TYPE 0x0000FF00 diff --git a/driver/vidc/inc/venus_hfi.h b/driver/vidc/inc/venus_hfi.h index 99f5d264c4..2660cb8e2a 100644 --- a/driver/vidc/inc/venus_hfi.h +++ b/driver/vidc/inc/venus_hfi.h @@ -76,5 +76,6 @@ int __interrupt_init(struct msm_vidc_core *core); int __setup_ucregion_memmap(struct msm_vidc_core *core); int __raise_interrupt(struct msm_vidc_core *core); int __power_off(struct msm_vidc_core *core); +bool __core_in_valid_state(struct msm_vidc_core *core); #endif // _VENUS_HFI_H_ diff --git a/driver/vidc/inc/venus_hfi_response.h b/driver/vidc/inc/venus_hfi_response.h new file mode 100644 index 0000000000..c48a92f0b3 --- /dev/null +++ b/driver/vidc/inc/venus_hfi_response.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __VENUS_HFI_RESPONSE_H__ +#define __VENUS_HFI_RESPONSE_H__ + +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); + +#endif // __VENUS_HFI_RESPONSE_H__ diff --git a/driver/vidc/src/hfi_packet.c b/driver/vidc/src/hfi_packet.c index 5f1e53cc32..87ce3356b5 100644 --- a/driver/vidc/src/hfi_packet.c +++ b/driver/vidc/src/hfi_packet.c @@ -429,7 +429,7 @@ int hfi_packet_sys_debug_config(struct msm_vidc_core *core, goto err_debug; /* HFI_PROP_DEBUG_CONFIG */ - payload = debug_config; /*TODO:Change later*/ + payload = 0; /*TODO:Change later*/ rc = hfi_create_packet(pkt, pkt_size, HFI_PROP_DEBUG_CONFIG, HFI_HOST_FLAGS_NONE, diff --git a/driver/vidc/src/msm_vidc.c b/driver/vidc/src/msm_vidc.c index 0b1a0cc5ae..bb41bd304c 100644 --- a/driver/vidc/src/msm_vidc.c +++ b/driver/vidc/src/msm_vidc.c @@ -589,6 +589,7 @@ void *msm_vidc_open(void *vidc_core, u32 session_type) int rc = 0; struct msm_vidc_inst *inst; struct msm_vidc_core *core; + int i = 0; d_vpr_h("%s()\n", __func__); core = vidc_core; @@ -667,6 +668,9 @@ void *msm_vidc_open(void *vidc_core, u32 session_type) inst->domain = session_type; inst->state = MSM_VIDC_OPEN; inst->request = false; + for (i = 0; i < MAX_SIGNAL; i++) + init_completion(&inst->completions[i]); + //inst->debugfs_root = // msm_vidc_debugfs_init_inst(inst, core->debugfs_root); diff --git a/driver/vidc/src/msm_vidc_debug.c b/driver/vidc/src/msm_vidc_debug.c index e7407a4e3f..74f920e385 100644 --- a/driver/vidc/src/msm_vidc_debug.c +++ b/driver/vidc/src/msm_vidc_debug.c @@ -6,7 +6,8 @@ #include "msm_vidc_debug.h" int msm_vidc_debug = VIDC_HIGH | VIDC_LOW | VIDC_PKT | VIDC_ERR | VIDC_PRINTK | - FW_ERROR | FW_FATAL | FW_FTRACE; + FW_ERROR | FW_FATAL | FW_FTRACE | FW_LOW | FW_MEDIUM | FW_HIGH | + FW_PERF | FW_PRINTK; EXPORT_SYMBOL(msm_vidc_debug); bool msm_vidc_lossless_encode = !true; diff --git a/driver/vidc/src/msm_vidc_driver.c b/driver/vidc/src/msm_vidc_driver.c index 4a13975971..ab1ac2376e 100644 --- a/driver/vidc/src/msm_vidc_driver.c +++ b/driver/vidc/src/msm_vidc_driver.c @@ -14,6 +14,7 @@ #include "msm_vidc_memory.h" #include "msm_vidc_debug.h" #include "venus_hfi.h" +#include "msm_vidc.h" #define COUNT_BITS(a, out) ({ \ while ((a) >= 1) { \ @@ -901,6 +902,11 @@ int msm_vidc_core_init(struct msm_vidc_core *core) if (rc) goto unlock; + core->state = MSM_VIDC_CORE_INIT; + init_completion(&core->init_done); + core->smmu_fault_handled = false; + core->ssr.trigger = false; + rc = venus_hfi_core_init(core); if (rc) { d_vpr_e("%s: core init failed\n", __func__); @@ -908,9 +914,18 @@ int msm_vidc_core_init(struct msm_vidc_core *core) goto unlock; } - core->state = MSM_VIDC_CORE_INIT; - core->smmu_fault_handled = false; - core->ssr.trigger = false; + mutex_unlock(&core->lock); + /*TODO: acquire lock or not */ + rc = wait_for_completion_timeout(&core->init_done, msecs_to_jiffies( + core->platform->data.core_data[DEBUG_TIMEOUT].value)); + if (!rc) { + d_vpr_e("%s: system init timed out\n", __func__); + //msm_comm_kill_session(inst); + rc = -EIO; + } else { + rc = 0; + } + mutex_lock(&core->lock); unlock: mutex_unlock(&core->lock); @@ -945,6 +960,47 @@ void msm_vidc_batch_handler(struct work_struct *work) { } +struct msm_vidc_inst *get_inst(struct msm_vidc_core *core, + u32 session_id) +{ + struct msm_vidc_inst *inst = NULL; + bool matches = false; + + if (!core) { + d_vpr_e("%s: invalid params\n", __func__); + return NULL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_id == session_id) { + matches = true; + break; + } + } + inst = (matches && kref_get_unless_zero(&inst->kref)) ? inst : NULL; + mutex_unlock(&core->lock); + return inst; +} + +static void put_inst_helper(struct kref *kref) +{ + struct msm_vidc_inst *inst = container_of(kref, + struct msm_vidc_inst, kref); + + msm_vidc_close(inst); +} + +void put_inst(struct msm_vidc_inst *inst) +{ + if (!inst) { + d_vpr_e("%s: invalid params\n", __func__); + return; + } + + kref_put(&inst->kref, put_inst_helper); +} + void core_lock(struct msm_vidc_core *core, const char *function) { mutex_lock(&core->lock); @@ -963,4 +1019,4 @@ void inst_lock(struct msm_vidc_inst *inst, const char *function) void inst_unlock(struct msm_vidc_inst *inst, const char *function) { mutex_unlock(&inst->lock); -} \ No newline at end of file +} diff --git a/driver/vidc/src/venus_hfi.c b/driver/vidc/src/venus_hfi.c index 86c3be3eaa..f400132ae2 100644 --- a/driver/vidc/src/venus_hfi.c +++ b/driver/vidc/src/venus_hfi.c @@ -23,6 +23,9 @@ #include "msm_vidc_driver.h" #include "msm_vidc_debug.h" #include "hfi_packet.h" +#include "venus_hfi_response.h" + +#define MIN_PAYLOAD_SIZE 3 static int __resume(struct msm_vidc_core *core); static int __suspend(struct msm_vidc_core *core); @@ -150,7 +153,7 @@ static void __strict_check(struct msm_vidc_core *core) __fatal_error(core, !mutex_is_locked(&core->lock)); } -static bool __core_in_valid_state(struct msm_vidc_core *core) +bool __core_in_valid_state(struct msm_vidc_core *core) { return core->state != MSM_VIDC_CORE_ERROR; } @@ -566,6 +569,7 @@ static int __write_queue(struct msm_vidc_iface_q_info *qinfo, u8 *packet, d_vpr_e("skip writing packet\n"); return 0; + packet_size_in_words = (*(u32 *)packet) >> 2; if (!packet_size_in_words || packet_size_in_words > qinfo->q_array.mem_size>>2) { @@ -624,7 +628,7 @@ static int __write_queue(struct msm_vidc_iface_q_info *qinfo, u8 *packet, mb(); return 0; } -#if 0 + static int __read_queue(struct msm_vidc_iface_q_info *qinfo, u8 *packet, u32 *pb_tx_req_is_set) { @@ -748,7 +752,7 @@ static int __read_queue(struct msm_vidc_iface_q_info *qinfo, u8 *packet, return rc; } -#endif + /* Writes into cmdq without raising an interrupt */ static int __iface_cmdq_write_relaxed(struct msm_vidc_core *core, void *pkt, bool *requires_interrupt) @@ -814,7 +818,7 @@ static int __iface_cmdq_write(struct msm_vidc_core *core, return rc; } -/* + static int __iface_msgq_read(struct msm_vidc_core *core, void *pkt) { u32 tx_req_is_set = 0; @@ -845,8 +849,9 @@ static int __iface_msgq_read(struct msm_vidc_core *core, void *pkt) if (tx_req_is_set) call_venus_op(core, raise_interrupt, core); rc = 0; - } else + } else { rc = -ENODATA; + } read_error_null: return rc; @@ -882,7 +887,74 @@ static int __iface_dbgq_read(struct msm_vidc_core *core, void *pkt) dbg_error_null: return rc; } -*/ + +/*TODO:darshana needs discussion*/ +static void __flush_debug_queue(struct msm_vidc_core *core, u8 *header) +{ + bool local_packet = false; + enum vidc_msg_prio log_level = msm_vidc_debug; + struct hfi_packet *pkt; + u32 payload = 0; + + if (!header) { + header = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_KERNEL); + if (!header) { + d_vpr_e("%s: Fail to allocate mem\n", __func__); + return; + } + + local_packet = true; + + /* + * Local packet is used when error occurred. + * It is good to print these logs to printk as well. + */ + log_level |= FW_PRINTK; + } + +#define SKIP_INVALID_PKT(pkt_size, payload_size, pkt_hdr_size) ({ \ + if (pkt_size < pkt_hdr_size || \ + payload_size < MIN_PAYLOAD_SIZE || \ + payload_size > \ + (pkt_size - pkt_hdr_size + sizeof(u8))) { \ + d_vpr_e("%s: invalid msg size - %d\n", \ + __func__, payload_size); \ + continue; \ + } \ + }) + + while (!__iface_dbgq_read(core, header)) { + struct hfi_header *hdr = + (struct hfi_header *) header; + + if (!validate_packet((u8 *)pkt, core->response_packet, + core->packet_size, __func__)) + return; + + pkt = (struct hfi_packet *)(hdr + sizeof(struct hfi_header)); + if (pkt->type == HFI_PROP_DEBUG_LOG_LEVEL) { + SKIP_INVALID_PKT(pkt->size, + sizeof(u32), sizeof(*pkt)); + + payload = (u32) *((u8 *)pkt + sizeof(struct hfi_packet)); + + /* + * All fw messages starts with new line character. This + * causes dprintk to print this message in two lines + * in the kernel log. Ignoring the first character + * from the message fixes this to print it in a single + * line. + */ + //pkt->rg_msg_data[sizeof(u32)-1] = '\0'; + dprintk_firmware(log_level, "%s", &payload); + } + } +#undef SKIP_INVALID_PKT + + if (local_packet) + kfree(header); +} + static int __sys_set_debug(struct msm_vidc_core *core, u32 debug) { int rc = 0; @@ -2192,9 +2264,29 @@ static void __unload_fw(struct msm_vidc_core *core) d_vpr_h("Firmware unloaded successfully\n"); } +static int __response_handler(struct msm_vidc_core *core) +{ + int rc = 0; + + while (!__iface_msgq_read(core, core->response_packet)) { + rc = handle_response(core, core->response_packet); + if (rc) + continue; + /* check for system error */ + if (core->state != MSM_VIDC_CORE_INIT) + break; + } + + __schedule_power_collapse_work(core); + __flush_debug_queue(core, core->response_packet); + + return rc; +} + void venus_hfi_work_handler(struct work_struct *work) { struct msm_vidc_core *core; + int num_responses = 0; core = container_of(work, struct msm_vidc_core, device_work); if (!core) { @@ -2202,6 +2294,21 @@ void venus_hfi_work_handler(struct work_struct *work) return; } d_vpr_e("%s(): core %pK\n", __func__, core); + + mutex_lock(&core->lock); + if (__resume(core)) { + d_vpr_e("%s: Power on failed\n", __func__); + goto err_no_work; + } + + call_venus_op(core, clear_interrupt, core); + mutex_unlock(&core->lock); + num_responses = __response_handler(core); + +err_no_work: + mutex_unlock(&core->lock); + if (!call_venus_op(core, watchdog, core, core->intr_status)) + enable_irq(core->dt->irq); } void venus_hfi_pm_work_handler(struct work_struct *work) @@ -2273,6 +2380,14 @@ int venus_hfi_core_init(struct msm_vidc_core *core) return -ENOMEM; } + core->response_packet = kzalloc(core->packet_size, GFP_KERNEL); + if (!core->response_packet) { + d_vpr_e("%s(): core response packet allocation failed\n", + __func__); + kfree(core->packet); + return -ENOMEM; + } + rc = __load_fw(core); if (rc) return rc; @@ -2311,6 +2426,7 @@ int venus_hfi_core_init(struct msm_vidc_core *core) error: d_vpr_h("%s(): failed\n", __func__); __unload_fw(core); + kfree(core->response_packet); kfree(core->packet); return rc; } diff --git a/driver/vidc/src/venus_hfi_response.c b/driver/vidc/src/venus_hfi_response.c new file mode 100644 index 0000000000..0fe2ca2bc0 --- /dev/null +++ b/driver/vidc/src/venus_hfi_response.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include "hfi_packet.h" +#include "venus_hfi.h" +#include "venus_hfi_response.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_driver.h" + +bool is_valid_hfi_port(struct msm_vidc_inst *inst, u32 port, + const char *func) +{ + if (!inst) { + s_vpr_e(inst->sid, "%s: invalid params\n", func); + return false; + } + + if (port != HFI_PORT_BITSTREAM && port != HFI_PORT_RAW) { + s_vpr_e(inst->sid, "%s: invalid port %#x\n", func, port); + return false; + } + return true; +} + +bool is_valid_hfi_buffer_type(struct msm_vidc_inst *inst, + u32 buffer_type, const char *func) +{ + if (!inst) { + s_vpr_e(inst->sid, "%s: invalid params\n", func); + return false; + } + + if (buffer_type != HFI_BUFFER_BITSTREAM && + buffer_type != HFI_BUFFER_RAW && + buffer_type != HFI_BUFFER_METADATA) { + s_vpr_e(inst->sid, "%s: invalid buffer type %#x\n", + func, buffer_type); + return false; + } + return true; +} + +static int signal_session_msg_receipt(struct msm_vidc_inst *inst, + enum signal_session_response cmd) +{ + if (cmd < MAX_SIGNAL) + complete(&inst->completions[cmd]); + return 0; +} + +int validate_packet(u8 *response_pkt, u8 *core_resp_pkt, + u32 core_resp_pkt_size, const char *func) +{ + u8 *response_limit; + + 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 - + max(sizeof(struct hfi_header), sizeof(struct hfi_packet)); + + if (response_pkt < core_resp_pkt || response_pkt > response_limit) { + d_vpr_e("%s: invalid packet address\n", func); + return -EINVAL; + } + + if (response_pkt + *(u32 *)response_pkt > response_limit) { + d_vpr_e("%s: invalid packet size %d\n", + func, *(u32 *)response_pkt); + return -EINVAL; + } + return 0; +} + +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_UNSUPPORTED: + error = "unsupported bitstream"; + break; + default: + error = "unknown"; + break; + } + + s_vpr_e(inst->sid, "session error (%#x): %s\n", pkt->type, error); + + rc = msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + return rc; +} + +static int handle_system_error(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + mutex_lock(&core->lock); + if (core->state == MSM_VIDC_CORE_DEINIT) { + d_vpr_e("%s: core already deinitialized\n", __func__); + mutex_unlock(&core->lock); + return 0; + } + + d_vpr_e("%s: system error received\n", __func__); + core->state = MSM_VIDC_CORE_DEINIT; + mutex_unlock(&core->lock); + return 0; +} + +static int handle_system_init(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SYSTEM_ERROR) { + d_vpr_e("%s: received system error\n", __func__); + return 0; + } + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) { + d_vpr_h("%s: successful\n", __func__); + complete(&core->init_done); + } + + return 0; +} + +static int handle_session_open(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SESSION_ERROR) { + s_vpr_e(inst->sid, "%s: received session error\n", __func__); + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + } + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + s_vpr_h(inst->sid, "%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_SESSION_ERROR) { + s_vpr_e(inst->sid, "%s: received session error\n", __func__); + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + } + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + s_vpr_h(inst->sid, "%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_SESSION_ERROR) { + s_vpr_e(inst->sid, "%s: received session error\n", __func__); + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + } + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + s_vpr_h(inst->sid, "%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) +{ + if (pkt->flags & HFI_FW_FLAGS_SESSION_ERROR) { + s_vpr_e(inst->sid, "%s: received session error\n", __func__); + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + } + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + s_vpr_h(inst->sid, "%s: successful for port %d\n", + __func__, pkt->port); + signal_session_msg_receipt(inst, SIGNAL_CMD_STOP); + return 0; +} + +static int handle_session_drain(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + if (pkt->flags & HFI_FW_FLAGS_SESSION_ERROR) { + s_vpr_e(inst->sid, "%s: received session error\n", __func__); + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + } + + if (pkt->flags & HFI_FW_FLAGS_SUCCESS) + s_vpr_h(inst->sid, "%s: successful\n", __func__); + return 0; +} + +static void handle_input_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ +} + +static void handle_output_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ +} + +static void handle_input_metadata_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ +} + +static void handle_output_metadata_buffer(struct msm_vidc_inst *inst, + struct hfi_buffer *buffer) +{ +} + +static int handle_session_buffer(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + struct hfi_buffer *buffer; + u32 buf_type = 0, port_type = 0; + + if (pkt->flags & HFI_FW_FLAGS_SESSION_ERROR) { + s_vpr_e(inst->sid, "%s: received session error\n", __func__); + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + return 0; + } + + port_type = pkt->port; + if (!is_valid_hfi_port(inst, port_type, __func__)) { + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + return 0; + } + + buffer = (struct hfi_buffer *)(pkt + sizeof(struct hfi_packet)); + buf_type = buffer->type; + if (!is_valid_hfi_buffer_type(inst, buf_type, __func__)) { + msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR); + return 0; + } + + s_vpr_h(inst->sid, "%s: Received buffer of type %#x\n", + __func__, buf_type); + + if (is_encode_session(inst)) { + if (port_type == HFI_PORT_BITSTREAM) { + if (buf_type == HFI_BUFFER_METADATA) + handle_output_metadata_buffer(inst, buffer); + else if (buf_type == HFI_BUFFER_BITSTREAM) + handle_output_buffer(inst, buffer); + } else if (port_type == HFI_PORT_RAW) { + if (buf_type == HFI_BUFFER_METADATA) + handle_input_metadata_buffer(inst, buffer); + else if (buf_type == HFI_BUFFER_RAW) + handle_input_buffer(inst, buffer); + } + } else if (is_decode_session(inst)) { + if (port_type == HFI_PORT_BITSTREAM) { + if (buf_type == HFI_BUFFER_METADATA) + handle_input_metadata_buffer(inst, buffer); + else if (buf_type == HFI_BUFFER_BITSTREAM) + handle_input_buffer(inst, buffer); + } else if (port_type == HFI_PORT_RAW) { + if (buf_type == HFI_BUFFER_METADATA) + handle_output_metadata_buffer(inst, buffer); + else if (buf_type == HFI_BUFFER_RAW) + handle_output_buffer(inst, buffer); + } + } else { + s_vpr_e(inst->sid, "%s: invalid session\n", __func__); + } + + return 0; +} + +static int handle_port_settings_change(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + s_vpr_h(inst->sid, "%s: Received port settings change, type %d\n", + __func__, pkt->port); + return 0; +} + +static int handle_session_command(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + switch (pkt->type) { + case HFI_CMD_OPEN: + return handle_session_open(inst, pkt); + case HFI_CMD_CLOSE: + return handle_session_close(inst, pkt); + case HFI_CMD_START: + return handle_session_start(inst, pkt); + case HFI_CMD_STOP: + return handle_session_stop(inst, pkt); + case HFI_CMD_DRAIN: + return handle_session_drain(inst, pkt); + case HFI_CMD_BUFFER: + return handle_session_buffer(inst, pkt); + case HFI_CMD_SETTINGS_CHANGE: + return handle_port_settings_change(inst, pkt); + default: + s_vpr_e(inst->sid, "%s: Unsupported command type: %#x\n", + __func__, pkt->type); + return -EINVAL; + } + return 0; +} + +static int handle_session_property(struct msm_vidc_inst *inst, + struct hfi_packet *pkt) +{ + s_vpr_h(inst->sid, "%s: property type %#x\n", __func__, pkt->type); + return 0; +} + +static int handle_image_version_property(struct hfi_packet *pkt) +{ + u32 i = 0; + char version[256]; + const u32 version_string_size = 128; + u8 *str_image_version; + u32 req_bytes; + + req_bytes = pkt->size - sizeof(*pkt); + if (req_bytes < version_string_size) { + 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 < version_string_size; i++) { + if (str_image_version[i] != '\0') + version[i] = str_image_version[i]; + else + version[i] = ' '; + } + version[i] = '\0'; + d_vpr_h("%s: F/W version: %s\n", __func__, version); + return 0; +} + +static int handle_system_property(struct msm_vidc_core *core, + struct hfi_packet *pkt) +{ + int rc = 0; + + if (pkt->flags & HFI_FW_FLAGS_SYSTEM_ERROR) { + d_vpr_e("%s: received system error for property type %#x\n", + __func__, pkt->type); + return handle_system_error(core, pkt); + } + + switch (pkt->type) { + case HFI_PROP_IMAGE_VERSION: + rc = handle_image_version_property(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 *pkt; + int i; + + pkt = (struct hfi_packet *)(hdr + sizeof(struct hfi_header)); + + for (i = 0; i < hdr->num_packets; i++) { + if (!validate_packet((u8 *)pkt, core->response_packet, + core->packet_size, __func__)) + return -EINVAL; + if (pkt->type == HFI_CMD_INIT) { + rc = handle_system_init(core, pkt); + } else if (pkt->type > HFI_SYSTEM_ERROR_BEGIN && + pkt->type < HFI_SYSTEM_ERROR_END) { + rc = handle_system_error(core, pkt); + } else if (pkt->type > HFI_PROP_BEGIN && + pkt->type < HFI_PROP_CODEC) { + rc = handle_system_property(core, pkt); + } else { + d_vpr_e("%s: Unknown packet type: %#x\n", + __func__, pkt->type); + return -EINVAL; + } + pkt += pkt->size; + } + return rc; +} + +static int handle_session_response(struct msm_vidc_core *core, + struct hfi_header *hdr) +{ + struct hfi_packet *pkt; + struct msm_vidc_inst *inst; + int i, rc = 0; + + inst = get_inst(core, hdr->session_id); + if (!inst) { + d_vpr_e("%s: invalid params\n", __func__); + goto exit; + } + + pkt = (struct hfi_packet *)(hdr + sizeof(struct hfi_header)); + + for (i = 0; i < hdr->num_packets; i++) { + if (!validate_packet((u8 *)pkt, core->response_packet, + core->packet_size, __func__)) + goto exit; + if (pkt->type < HFI_CMD_END && pkt->type > HFI_CMD_BEGIN) { + rc = handle_session_command(inst, pkt); + } else if (pkt->type > HFI_PROP_BEGIN && + pkt->type < HFI_PROP_END) { + rc = handle_session_property(inst, pkt); + } else if (pkt->type > HFI_SESSION_ERROR_BEGIN && + pkt->type < HFI_SESSION_ERROR_END) { + rc = handle_session_error(inst, pkt); + } else { + s_vpr_e(inst->sid, "%s: Unknown packet type: %#x\n", + __func__, pkt->type); + goto exit; + } + pkt += pkt->size; + } +exit: + put_inst(inst); + return rc; +} + +int handle_response(struct msm_vidc_core *core, void *response) +{ + struct hfi_header *hdr; + + if (!core || !response) { + d_vpr_e("%s: invalid params\n", __func__); + return -EINVAL; + } + + hdr = (struct hfi_header *)response; + if (!validate_packet((u8 *)hdr, core->response_packet, + core->packet_size, __func__)) + return -EINVAL; + + if (!hdr->session_id) + return handle_system_response(core, hdr); + else + return handle_session_response(core, hdr); + + return 0; +}