Files
android_kernel_samsung_sm86…/driver/vidc/src/venus_hfi_response.c
Darshana Patil 081c9ad524 driver: vidc: fix response handling
Add condition to check if response packet
size is zero.
Skip writing to sfr-register till FW adds support.
Read FW response from the right addr.

Change-Id: I9244d6af024da18750bb21186512c7cc4dfd0c65
Signed-off-by: Darshana Patil <darshana@codeaurora.org>
2020-09-30 14:09:42 -07:00

477 rivejä
12 KiB
C

// 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;
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 -
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;
}
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 + response_pkt_size > 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, __func__);
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, __func__);
}
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, __func__);
}
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, __func__);
}
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, __func__);
}
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, __func__);
}
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, __func__);
return 0;
}
port_type = pkt->port;
if (!is_valid_hfi_port(inst, port_type, __func__)) {
msm_vidc_change_inst_state(inst, MSM_VIDC_ERROR, __func__);
return 0;
}
buffer = (struct hfi_buffer *)((u8 *)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, __func__);
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 *)((u8 *)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 *)((u8 *)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;
}