linux/drivers/media/platform/qcom/venus/hfi_msgs.c

1098 lines
28 KiB
C
Raw Normal View History

/*
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2017 Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/hash.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <media/videobuf2-v4l2.h>
#include "core.h"
#include "hfi.h"
#include "hfi_helper.h"
#include "hfi_msgs.h"
static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
enum hfi_version ver = core->res->hfi_version;
struct hfi_event_data event = {0};
int num_properties_changed;
struct hfi_framesize *frame_sz;
struct hfi_profile_level *profile_level;
struct hfi_bit_depth *pixel_depth;
struct hfi_pic_struct *pic_struct;
struct hfi_colour_space *colour_info;
struct hfi_buffer_requirements *bufreq;
struct hfi_extradata_input_crop *crop;
u8 *data_ptr;
u32 ptype;
inst->error = HFI_ERR_NONE;
switch (pkt->event_data1) {
case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
break;
default:
inst->error = HFI_ERR_SESSION_INVALID_PARAMETER;
goto done;
}
event.event_type = pkt->event_data1;
num_properties_changed = pkt->event_data2;
if (!num_properties_changed) {
inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
goto done;
}
data_ptr = (u8 *)&pkt->ext_event_data[0];
do {
ptype = *((u32 *)data_ptr);
switch (ptype) {
case HFI_PROPERTY_PARAM_FRAME_SIZE:
data_ptr += sizeof(u32);
frame_sz = (struct hfi_framesize *)data_ptr;
event.width = frame_sz->width;
event.height = frame_sz->height;
data_ptr += sizeof(*frame_sz);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
data_ptr += sizeof(u32);
profile_level = (struct hfi_profile_level *)data_ptr;
event.profile = profile_level->profile;
event.level = profile_level->level;
data_ptr += sizeof(*profile_level);
break;
case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
data_ptr += sizeof(u32);
pixel_depth = (struct hfi_bit_depth *)data_ptr;
event.bit_depth = pixel_depth->bit_depth;
data_ptr += sizeof(*pixel_depth);
break;
case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
data_ptr += sizeof(u32);
pic_struct = (struct hfi_pic_struct *)data_ptr;
event.pic_struct = pic_struct->progressive_only;
data_ptr += sizeof(*pic_struct);
break;
case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
data_ptr += sizeof(u32);
colour_info = (struct hfi_colour_space *)data_ptr;
event.colour_space = colour_info->colour_space;
data_ptr += sizeof(*colour_info);
break;
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
data_ptr += sizeof(u32);
event.entropy_mode = *(u32 *)data_ptr;
data_ptr += sizeof(u32);
break;
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
data_ptr += sizeof(u32);
bufreq = (struct hfi_buffer_requirements *)data_ptr;
event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver);
data_ptr += sizeof(*bufreq);
break;
case HFI_INDEX_EXTRADATA_INPUT_CROP:
data_ptr += sizeof(u32);
crop = (struct hfi_extradata_input_crop *)data_ptr;
event.input_crop.left = crop->left;
event.input_crop.top = crop->top;
event.input_crop.width = crop->width;
event.input_crop.height = crop->height;
data_ptr += sizeof(*crop);
break;
default:
break;
}
num_properties_changed--;
} while (num_properties_changed > 0);
done:
inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
}
static void event_release_buffer_ref(struct venus_core *core,
struct venus_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
struct hfi_event_data event = {0};
struct hfi_msg_event_release_buffer_ref_pkt *data;
data = (struct hfi_msg_event_release_buffer_ref_pkt *)
pkt->ext_event_data;
event.event_type = HFI_EVENT_RELEASE_BUFFER_REFERENCE;
event.packet_buffer = data->packet_buffer;
event.extradata_buffer = data->extradata_buffer;
event.tag = data->output_tag;
inst->error = HFI_ERR_NONE;
inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
}
static void event_sys_error(struct venus_core *core, u32 event,
struct hfi_msg_event_notify_pkt *pkt)
{
if (pkt)
dev_dbg(core->dev,
"sys error (session id:%x, data1:%x, data2:%x)\n",
pkt->shdr.session_id, pkt->event_data1,
pkt->event_data2);
core->core_ops->event_notify(core, event);
}
static void
event_session_error(struct venus_core *core, struct venus_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
struct device *dev = core->dev;
dev_dbg(dev, "session error: event id:%x, session id:%x\n",
pkt->event_data1, pkt->shdr.session_id);
if (!inst)
return;
switch (pkt->event_data1) {
/* non fatal session errors */
case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
inst->error = HFI_ERR_NONE;
break;
default:
dev_err(dev, "session error: event id:%x (%x), session id:%x\n",
pkt->event_data1, pkt->event_data2,
pkt->shdr.session_id);
inst->error = pkt->event_data1;
inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
break;
}
}
static void hfi_event_notify(struct venus_core *core, struct venus_inst *inst,
void *packet)
{
struct hfi_msg_event_notify_pkt *pkt = packet;
if (!packet)
return;
switch (pkt->event_id) {
case HFI_EVENT_SYS_ERROR:
event_sys_error(core, EVT_SYS_ERROR, pkt);
break;
case HFI_EVENT_SESSION_ERROR:
event_session_error(core, inst, pkt);
break;
case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
event_seq_changed(core, inst, pkt);
break;
case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
event_release_buffer_ref(core, inst, pkt);
break;
case HFI_EVENT_SESSION_PROPERTY_CHANGED:
break;
default:
break;
}
}
static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst,
void *packet)
{
struct hfi_msg_sys_init_done_pkt *pkt = packet;
u32 rem_bytes, read_bytes = 0, num_properties;
u32 error, ptype;
u8 *data;
error = pkt->error_type;
if (error != HFI_ERR_NONE)
goto err_no_prop;
num_properties = pkt->num_properties;
if (!num_properties) {
error = HFI_ERR_SYS_INVALID_PARAMETER;
goto err_no_prop;
}
rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32);
if (!rem_bytes) {
/* missing property data */
error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
goto err_no_prop;
}
data = (u8 *)&pkt->data[0];
if (core->res->hfi_version == HFI_VERSION_3XX)
goto err_no_prop;
while (num_properties && rem_bytes >= sizeof(u32)) {
ptype = *((u32 *)data);
data += sizeof(u32);
switch (ptype) {
case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: {
struct hfi_codec_supported *prop;
prop = (struct hfi_codec_supported *)data;
if (rem_bytes < sizeof(*prop)) {
error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
break;
}
read_bytes += sizeof(*prop) + sizeof(u32);
core->dec_codecs = prop->dec_codecs;
core->enc_codecs = prop->enc_codecs;
break;
}
case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: {
struct hfi_max_sessions_supported *prop;
if (rem_bytes < sizeof(*prop)) {
error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
break;
}
prop = (struct hfi_max_sessions_supported *)data;
read_bytes += sizeof(*prop) + sizeof(u32);
core->max_sessions_supported = prop->max_sessions;
break;
}
default:
error = HFI_ERR_SYS_INVALID_PARAMETER;
break;
}
media: venus: hfi: fix error handling in hfi_sys_init_done() Not entirely sure what triggers it, but with venus build as kernel module and in initrd, we hit this crash: Unable to handle kernel paging request at virtual address ffff80003c039000 pgd = ffff00000a14f000 [ffff80003c039000] *pgd=00000000bd9f7003, *pud=00000000bd9f6003, *pmd=00000000bd9f0003, *pte=0000000000000000 Internal error: Oops: 96000007 [#1] SMP Modules linked in: qcom_wcnss_pil(E+) crc32_ce(E) qcom_common(E) venus_core(E+) remoteproc(E) snd_soc_msm8916_digital(E) virtio_ring(E) cdc_ether(E) snd_soc_lpass_apq8016(E) snd_soc_lpass_cpu(E) snd_soc_apq8016_sbc(E) snd_soc_lpass_platform(E) v4l2_mem2mem(E) virtio(E) snd_soc_core(E) ac97_bus(E) snd_pcm_dmaengine(E) snd_seq(E) leds_gpio(E) videobuf2_v4l2(E) videobuf2_core(E) snd_seq_device(E) snd_pcm(E) videodev(E) media(E) nvmem_qfprom(E) msm(E) snd_timer(E) snd(E) soundcore(E) spi_qup(E) mdt_loader(E) qcom_tsens(E) qcom_spmi_temp_alarm(E) nvmem_core(E) msm_rng(E) uas(E) usb_storage(E) dm9601(E) usbnet(E) mii(E) mmc_block(E) adv7511(E) drm_kms_helper(E) syscopyarea(E) sysfillrect(E) sysimgblt(E) fb_sys_fops(E) qcom_spmi_vadc(E) qcom_vadc_common(PE) industrialio(E) pinctrl_spmi_mpp(E) pinctrl_spmi_gpio(E) rtc_pm8xxx(E) clk_smd_rpm(E) sdhci_msm(E) sdhci_pltfm(E) qcom_smd_regulator(E) drm(E) smd_rpm(E) qcom_spmi_pmic(E) regmap_spmi(E) ci_hdrc_msm(E) ci_hdrc(E) usb3503(E) extcon_usb_gpio(E) phy_msm_usb(E) udc_core(E) qcom_hwspinlock(E) extcon_core(E) ehci_msm(E) i2c_qup(E) sdhci(E) mmc_core(E) spmi_pmic_arb(E) spmi(E) qcom_smd(E) smsm(E) rpmsg_core(E) smp2p(E) smem(E) hwspinlock_core(E) gpio_keys(E) CPU: 2 PID: 551 Comm: irq/150-venus Tainted: P E 4.12.0+ #1625 Hardware name: qualcomm dragonboard410c/dragonboard410c, BIOS 2017.07-rc2-00144-ga97bdbdf72-dirty 07/08/2017 task: ffff800037338000 task.stack: ffff800038e00000 PC is at hfi_sys_init_done+0x64/0x140 [venus_core] LR is at hfi_process_msg_packet+0xcc/0x1e8 [venus_core] pc : [<ffff00000118b384>] lr : [<ffff00000118c11c>] pstate: 20400145 sp : ffff800038e03c60 x29: ffff800038e03c60 x28: 0000000000000000 x27: 00000000000df018 x26: ffff00000118f4d0 x25: 0000000000020003 x24: ffff80003a8d3010 x23: ffff00000118f760 x22: ffff800037b40028 x21: ffff8000382981f0 x20: ffff800037b40028 x19: ffff80003c039000 x18: 0000000000000020 x17: 0000000000000000 x16: ffff800037338000 x15: ffffffffffffffff x14: 0000001000000014 x13: 0000000100001007 x12: 0000000100000020 x11: 0000100e00000000 x10: 0000000000000001 x9 : 0000000200000000 x8 : 0000001400000001 x7 : 0000000000001010 x6 : 0000000000000148 x5 : 0000000000001009 x4 : ffff80003c039000 x3 : 00000000cd770abb x2 : 0000000000000042 x1 : 0000000000000788 x0 : 0000000000000002 Process irq/150-venus (pid: 551, stack limit = 0xffff800038e00000) Call trace: [<ffff00000118b384>] hfi_sys_init_done+0x64/0x140 [venus_core] [<ffff00000118c11c>] hfi_process_msg_packet+0xcc/0x1e8 [venus_core] [<ffff00000118a2b4>] venus_isr_thread+0x1b4/0x208 [venus_core] [<ffff00000118e750>] hfi_isr_thread+0x28/0x38 [venus_core] [<ffff000008161550>] irq_thread_fn+0x30/0x70 [<ffff0000081617fc>] irq_thread+0x14c/0x1c8 [<ffff000008105e68>] kthread+0x138/0x140 [<ffff000008083590>] ret_from_fork+0x10/0x40 Code: 52820125 52820207 7a431820 54000249 (b9400263) ---[ end trace c963460f20a984b6 ]--- The problem is that in the error case, we've incremented the data ptr but not decremented rem_bytes, and keep reading (presumably garbage) until eventually we go beyond the end of the buffer. Instead, on first error, we should probably just bail out. Other option is to increment read_bytes by sizeof(u32) before the switch, rather than only accounting for the ptype header in the non-error case. Note that in this case it is HFI_ERR_SYS_INVALID_PARAMETER, ie. an unrecognized/unsupported parameter, so interpreting the next word as a property type would be bogus. The other error cases are due to truncated buffer, so there isn't likely to be anything valid to interpret in the remainder of the buffer. So just bailing seems like a reasonable solution. Signed-off-by: Rob Clark <robdclark@gmail.com> Reviewed-by: Stanimir Varbanov <stanimir.varbanov@linaro.org> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-07-17 16:56:50 +08:00
if (error)
break;
rem_bytes -= read_bytes;
data += read_bytes;
num_properties--;
}
err_no_prop:
core->error = error;
complete(&core->done);
}
static void
sys_get_prop_image_version(struct device *dev,
struct hfi_msg_sys_property_info_pkt *pkt)
{
int req_bytes;
req_bytes = pkt->hdr.size - sizeof(*pkt);
if (req_bytes < 128 || !pkt->data[1] || pkt->num_properties > 1)
/* bad packet */
return;
dev_dbg(dev, "F/W version: %s\n", (u8 *)&pkt->data[1]);
}
static void hfi_sys_property_info(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_sys_property_info_pkt *pkt = packet;
struct device *dev = core->dev;
if (!pkt->num_properties) {
dev_dbg(dev, "%s: no properties\n", __func__);
return;
}
switch (pkt->data[0]) {
case HFI_PROPERTY_SYS_IMAGE_VERSION:
sys_get_prop_image_version(dev, pkt);
break;
default:
dev_dbg(dev, "%s: unknown property data\n", __func__);
break;
}
}
static void hfi_sys_rel_resource_done(struct venus_core *core,
struct venus_inst *inst,
void *packet)
{
struct hfi_msg_sys_release_resource_done_pkt *pkt = packet;
core->error = pkt->error_type;
complete(&core->done);
}
static void hfi_sys_ping_done(struct venus_core *core, struct venus_inst *inst,
void *packet)
{
struct hfi_msg_sys_ping_ack_pkt *pkt = packet;
core->error = HFI_ERR_NONE;
if (pkt->client_data != 0xbeef)
core->error = HFI_ERR_SYS_FATAL;
complete(&core->done);
}
static void hfi_sys_idle_done(struct venus_core *core, struct venus_inst *inst,
void *packet)
{
dev_dbg(core->dev, "sys idle\n");
}
static void hfi_sys_pc_prepare_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_sys_pc_prep_done_pkt *pkt = packet;
dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type);
}
static void
hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst)
{
if (!in || !inst)
return;
switch (in->capability_type) {
case HFI_CAPABILITY_FRAME_WIDTH:
inst->cap_width = *in;
break;
case HFI_CAPABILITY_FRAME_HEIGHT:
inst->cap_height = *in;
break;
case HFI_CAPABILITY_MBS_PER_FRAME:
inst->cap_mbs_per_frame = *in;
break;
case HFI_CAPABILITY_MBS_PER_SECOND:
inst->cap_mbs_per_sec = *in;
break;
case HFI_CAPABILITY_FRAMERATE:
inst->cap_framerate = *in;
break;
case HFI_CAPABILITY_SCALE_X:
inst->cap_scale_x = *in;
break;
case HFI_CAPABILITY_SCALE_Y:
inst->cap_scale_y = *in;
break;
case HFI_CAPABILITY_BITRATE:
inst->cap_bitrate = *in;
break;
case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS:
inst->cap_hier_p = *in;
break;
case HFI_CAPABILITY_ENC_LTR_COUNT:
inst->cap_ltr_count = *in;
break;
case HFI_CAPABILITY_CP_OUTPUT2_THRESH:
inst->cap_secure_output2_threshold = *in;
break;
default:
break;
}
}
static unsigned int
session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt,
struct hfi_profile_level *profile_level)
{
struct hfi_profile_level *hfi;
u32 req_bytes;
req_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
if (!req_bytes || req_bytes % sizeof(struct hfi_profile_level))
/* bad packet */
return HFI_ERR_SESSION_INVALID_PARAMETER;
hfi = (struct hfi_profile_level *)&pkt->data[1];
profile_level->profile = hfi->profile;
profile_level->level = hfi->level;
return HFI_ERR_NONE;
}
static unsigned int
session_get_prop_buf_req(struct hfi_msg_session_property_info_pkt *pkt,
struct hfi_buffer_requirements *bufreq)
{
struct hfi_buffer_requirements *buf_req;
u32 req_bytes;
unsigned int idx = 0;
req_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
if (!req_bytes || req_bytes % sizeof(*buf_req) || !pkt->data[1])
/* bad packet */
return HFI_ERR_SESSION_INVALID_PARAMETER;
buf_req = (struct hfi_buffer_requirements *)&pkt->data[1];
if (!buf_req)
return HFI_ERR_SESSION_INVALID_PARAMETER;
while (req_bytes) {
memcpy(&bufreq[idx], buf_req, sizeof(*bufreq));
idx++;
if (idx > HFI_BUFFER_TYPE_MAX)
return HFI_ERR_SESSION_INVALID_PARAMETER;
req_bytes -= sizeof(struct hfi_buffer_requirements);
buf_req++;
}
return HFI_ERR_NONE;
}
static void hfi_session_prop_info(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_property_info_pkt *pkt = packet;
struct device *dev = core->dev;
union hfi_get_property *hprop = &inst->hprop;
unsigned int error = HFI_ERR_NONE;
if (!pkt->num_properties) {
error = HFI_ERR_SESSION_INVALID_PARAMETER;
dev_err(dev, "%s: no properties\n", __func__);
goto done;
}
switch (pkt->data[0]) {
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
memset(hprop->bufreq, 0, sizeof(hprop->bufreq));
error = session_get_prop_buf_req(pkt, hprop->bufreq);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
memset(&hprop->profile_level, 0, sizeof(hprop->profile_level));
error = session_get_prop_profile_level(pkt,
&hprop->profile_level);
break;
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
break;
default:
dev_dbg(dev, "%s: unknown property id:%x\n", __func__,
pkt->data[0]);
return;
}
done:
inst->error = error;
complete(&inst->done);
}
static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst,
struct hfi_msg_session_init_done_pkt *pkt)
{
struct device *dev = core->dev;
u32 rem_bytes, num_props;
u32 ptype, next_offset = 0;
u32 err;
u8 *data;
rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32);
if (!rem_bytes) {
dev_err(dev, "%s: missing property info\n", __func__);
return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
}
err = pkt->error_type;
if (err)
return err;
data = (u8 *)&pkt->data[0];
num_props = pkt->num_properties;
while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) {
ptype = *((u32 *)data);
next_offset = sizeof(u32);
switch (ptype) {
case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: {
struct hfi_codec_mask_supported *masks =
(struct hfi_codec_mask_supported *)
(data + next_offset);
next_offset += sizeof(*masks);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: {
struct hfi_capabilities *caps;
struct hfi_capability *cap;
u32 num_caps;
if ((rem_bytes - next_offset) < sizeof(*cap)) {
err = HFI_ERR_SESSION_INVALID_PARAMETER;
break;
}
caps = (struct hfi_capabilities *)(data + next_offset);
num_caps = caps->num_capabilities;
cap = &caps->data[0];
next_offset += sizeof(u32);
while (num_caps &&
(rem_bytes - next_offset) >= sizeof(u32)) {
hfi_copy_cap_prop(cap, inst);
cap++;
next_offset += sizeof(*cap);
num_caps--;
}
num_props--;
break;
}
case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: {
struct hfi_uncompressed_format_supported *prop =
(struct hfi_uncompressed_format_supported *)
(data + next_offset);
u32 num_fmt_entries;
u8 *fmt;
struct hfi_uncompressed_plane_info *inf;
if ((rem_bytes - next_offset) < sizeof(*prop)) {
err = HFI_ERR_SESSION_INVALID_PARAMETER;
break;
}
num_fmt_entries = prop->format_entries;
next_offset = sizeof(*prop) - sizeof(u32);
fmt = (u8 *)&prop->format_info[0];
dev_dbg(dev, "uncomm format support num entries:%u\n",
num_fmt_entries);
while (num_fmt_entries) {
struct hfi_uncompressed_plane_constraints *cnts;
u32 bytes_to_skip;
inf = (struct hfi_uncompressed_plane_info *)fmt;
if ((rem_bytes - next_offset) < sizeof(*inf)) {
err = HFI_ERR_SESSION_INVALID_PARAMETER;
break;
}
dev_dbg(dev, "plane info: fmt:%x, planes:%x\n",
inf->format, inf->num_planes);
cnts = &inf->plane_format[0];
dev_dbg(dev, "%u %u %u %u\n",
cnts->stride_multiples,
cnts->max_stride,
cnts->min_plane_buffer_height_multiple,
cnts->buffer_alignment);
bytes_to_skip = sizeof(*inf) - sizeof(*cnts) +
inf->num_planes * sizeof(*cnts);
fmt += bytes_to_skip;
next_offset += bytes_to_skip;
num_fmt_entries--;
}
num_props--;
break;
}
case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: {
struct hfi_properties_supported *prop =
(struct hfi_properties_supported *)
(data + next_offset);
next_offset += sizeof(*prop) - sizeof(u32)
+ prop->num_properties * sizeof(u32);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: {
struct hfi_profile_level_supported *prop =
(struct hfi_profile_level_supported *)
(data + next_offset);
struct hfi_profile_level *pl;
unsigned int prop_count = 0;
unsigned int count = 0;
u8 *ptr;
ptr = (u8 *)&prop->profile_level[0];
prop_count = prop->profile_count;
if (prop_count > HFI_MAX_PROFILE_COUNT)
prop_count = HFI_MAX_PROFILE_COUNT;
while (prop_count) {
ptr++;
pl = (struct hfi_profile_level *)ptr;
inst->pl[count].profile = pl->profile;
inst->pl[count].level = pl->level;
prop_count--;
count++;
ptr += sizeof(*pl) / sizeof(u32);
}
inst->pl_count = count;
next_offset += sizeof(*prop) - sizeof(*pl) +
prop->profile_count * sizeof(*pl);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: {
next_offset +=
sizeof(struct hfi_interlace_format_supported);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: {
struct hfi_nal_stream_format *nal =
(struct hfi_nal_stream_format *)
(data + next_offset);
dev_dbg(dev, "NAL format: %x\n", nal->format);
next_offset += sizeof(*nal);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: {
next_offset += sizeof(u32);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: {
u32 *max_seq_sz = (u32 *)(data + next_offset);
dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz);
next_offset += sizeof(u32);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: {
next_offset += sizeof(struct hfi_intra_refresh);
num_props--;
break;
}
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: {
struct hfi_buffer_alloc_mode_supported *prop =
(struct hfi_buffer_alloc_mode_supported *)
(data + next_offset);
unsigned int i;
for (i = 0; i < prop->num_entries; i++) {
if (prop->buffer_type == HFI_BUFFER_OUTPUT ||
prop->buffer_type == HFI_BUFFER_OUTPUT2) {
switch (prop->data[i]) {
case HFI_BUFFER_MODE_STATIC:
inst->cap_bufs_mode_static = true;
break;
case HFI_BUFFER_MODE_DYNAMIC:
inst->cap_bufs_mode_dynamic = true;
break;
default:
break;
}
}
}
next_offset += sizeof(*prop) -
sizeof(u32) + prop->num_entries * sizeof(u32);
num_props--;
break;
}
default:
dev_dbg(dev, "%s: default case %#x\n", __func__, ptype);
break;
}
rem_bytes -= next_offset;
data += next_offset;
}
return err;
}
static void hfi_session_init_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_init_done_pkt *pkt = packet;
unsigned int error;
error = pkt->error_type;
if (error != HFI_ERR_NONE)
goto done;
if (core->res->hfi_version != HFI_VERSION_1XX)
goto done;
error = init_done_read_prop(core, inst, pkt);
done:
inst->error = error;
complete(&inst->done);
}
static void hfi_session_load_res_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_load_resources_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_flush_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_flush_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_etb_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
inst->error = pkt->error_type;
inst->ops->buf_done(inst, HFI_BUFFER_INPUT, pkt->input_tag,
pkt->filled_len, pkt->offset, 0, 0, 0);
}
static void hfi_session_ftb_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
u32 session_type = inst->session_type;
u64 timestamp_us = 0;
u32 timestamp_hi = 0, timestamp_lo = 0;
unsigned int error;
u32 flags = 0, hfi_flags = 0, offset = 0, filled_len = 0;
u32 pic_type = 0, buffer_type = 0, output_tag = -1;
if (session_type == VIDC_SESSION_TYPE_ENC) {
struct hfi_msg_session_fbd_compressed_pkt *pkt = packet;
timestamp_hi = pkt->time_stamp_hi;
timestamp_lo = pkt->time_stamp_lo;
hfi_flags = pkt->flags;
offset = pkt->offset;
filled_len = pkt->filled_len;
pic_type = pkt->picture_type;
output_tag = pkt->output_tag;
buffer_type = HFI_BUFFER_OUTPUT;
error = pkt->error_type;
} else if (session_type == VIDC_SESSION_TYPE_DEC) {
struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt =
packet;
timestamp_hi = pkt->time_stamp_hi;
timestamp_lo = pkt->time_stamp_lo;
hfi_flags = pkt->flags;
offset = pkt->offset;
filled_len = pkt->filled_len;
pic_type = pkt->picture_type;
output_tag = pkt->output_tag;
if (pkt->stream_id == 0)
buffer_type = HFI_BUFFER_OUTPUT;
else if (pkt->stream_id == 1)
buffer_type = HFI_BUFFER_OUTPUT2;
error = pkt->error_type;
} else {
error = HFI_ERR_SESSION_INVALID_PARAMETER;
}
if (buffer_type != HFI_BUFFER_OUTPUT)
goto done;
if (hfi_flags & HFI_BUFFERFLAG_EOS)
flags |= V4L2_BUF_FLAG_LAST;
switch (pic_type) {
case HFI_PICTURE_IDR:
case HFI_PICTURE_I:
flags |= V4L2_BUF_FLAG_KEYFRAME;
break;
case HFI_PICTURE_P:
flags |= V4L2_BUF_FLAG_PFRAME;
break;
case HFI_PICTURE_B:
flags |= V4L2_BUF_FLAG_BFRAME;
break;
case HFI_FRAME_NOTCODED:
case HFI_UNUSED_PICT:
case HFI_FRAME_YUV:
default:
break;
}
if (!(hfi_flags & HFI_BUFFERFLAG_TIMESTAMPINVALID) && filled_len) {
timestamp_us = timestamp_hi;
timestamp_us = (timestamp_us << 32) | timestamp_lo;
}
done:
inst->error = error;
inst->ops->buf_done(inst, buffer_type, output_tag, filled_len,
offset, flags, hfi_flags, timestamp_us);
}
static void hfi_session_start_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_start_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_stop_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_stop_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_rel_res_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_release_resources_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_rel_buf_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_release_buffers_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_end_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_end_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_abort_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_sys_session_abort_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
static void hfi_session_get_seq_hdr_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_get_sequence_hdr_done_pkt *pkt = packet;
inst->error = pkt->error_type;
complete(&inst->done);
}
struct hfi_done_handler {
u32 pkt;
u32 pkt_sz;
u32 pkt_sz2;
void (*done)(struct venus_core *, struct venus_inst *, void *);
bool is_sys_pkt;
};
static const struct hfi_done_handler handlers[] = {
{.pkt = HFI_MSG_EVENT_NOTIFY,
.pkt_sz = sizeof(struct hfi_msg_event_notify_pkt),
.done = hfi_event_notify,
},
{.pkt = HFI_MSG_SYS_INIT,
.pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt),
.done = hfi_sys_init_done,
.is_sys_pkt = true,
},
{.pkt = HFI_MSG_SYS_PROPERTY_INFO,
.pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt),
.done = hfi_sys_property_info,
.is_sys_pkt = true,
},
{.pkt = HFI_MSG_SYS_RELEASE_RESOURCE,
.pkt_sz = sizeof(struct hfi_msg_sys_release_resource_done_pkt),
.done = hfi_sys_rel_resource_done,
.is_sys_pkt = true,
},
{.pkt = HFI_MSG_SYS_PING_ACK,
.pkt_sz = sizeof(struct hfi_msg_sys_ping_ack_pkt),
.done = hfi_sys_ping_done,
.is_sys_pkt = true,
},
{.pkt = HFI_MSG_SYS_IDLE,
.pkt_sz = sizeof(struct hfi_msg_sys_idle_pkt),
.done = hfi_sys_idle_done,
.is_sys_pkt = true,
},
{.pkt = HFI_MSG_SYS_PC_PREP,
.pkt_sz = sizeof(struct hfi_msg_sys_pc_prep_done_pkt),
.done = hfi_sys_pc_prepare_done,
.is_sys_pkt = true,
},
{.pkt = HFI_MSG_SYS_SESSION_INIT,
.pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt),
.done = hfi_session_init_done,
},
{.pkt = HFI_MSG_SYS_SESSION_END,
.pkt_sz = sizeof(struct hfi_msg_session_end_done_pkt),
.done = hfi_session_end_done,
},
{.pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
.pkt_sz = sizeof(struct hfi_msg_session_load_resources_done_pkt),
.done = hfi_session_load_res_done,
},
{.pkt = HFI_MSG_SESSION_START,
.pkt_sz = sizeof(struct hfi_msg_session_start_done_pkt),
.done = hfi_session_start_done,
},
{.pkt = HFI_MSG_SESSION_STOP,
.pkt_sz = sizeof(struct hfi_msg_session_stop_done_pkt),
.done = hfi_session_stop_done,
},
{.pkt = HFI_MSG_SYS_SESSION_ABORT,
.pkt_sz = sizeof(struct hfi_msg_sys_session_abort_done_pkt),
.done = hfi_session_abort_done,
},
{.pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
.pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
.done = hfi_session_etb_done,
},
{.pkt = HFI_MSG_SESSION_FILL_BUFFER,
.pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt),
.pkt_sz2 = sizeof(struct hfi_msg_session_fbd_compressed_pkt),
.done = hfi_session_ftb_done,
},
{.pkt = HFI_MSG_SESSION_FLUSH,
.pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
.done = hfi_session_flush_done,
},
{.pkt = HFI_MSG_SESSION_PROPERTY_INFO,
.pkt_sz = sizeof(struct hfi_msg_session_property_info_pkt),
.done = hfi_session_prop_info,
},
{.pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
.pkt_sz = sizeof(struct hfi_msg_session_release_resources_done_pkt),
.done = hfi_session_rel_res_done,
},
{.pkt = HFI_MSG_SESSION_GET_SEQUENCE_HEADER,
.pkt_sz = sizeof(struct hfi_msg_session_get_sequence_hdr_done_pkt),
.done = hfi_session_get_seq_hdr_done,
},
{.pkt = HFI_MSG_SESSION_RELEASE_BUFFERS,
.pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt),
.done = hfi_session_rel_buf_done,
},
};
void hfi_process_watchdog_timeout(struct venus_core *core)
{
event_sys_error(core, EVT_SYS_WATCHDOG_TIMEOUT, NULL);
}
static struct venus_inst *to_instance(struct venus_core *core, u32 session_id)
{
struct venus_inst *inst;
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list)
if (hash32_ptr(inst) == session_id) {
mutex_unlock(&core->lock);
return inst;
}
mutex_unlock(&core->lock);
return NULL;
}
u32 hfi_process_msg_packet(struct venus_core *core, struct hfi_pkt_hdr *hdr)
{
const struct hfi_done_handler *handler;
struct device *dev = core->dev;
struct venus_inst *inst;
bool found = false;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
handler = &handlers[i];
if (handler->pkt != hdr->pkt_type)
continue;
found = true;
break;
}
if (!found)
return hdr->pkt_type;
if (hdr->size && hdr->size < handler->pkt_sz &&
hdr->size < handler->pkt_sz2) {
dev_err(dev, "bad packet size (%d should be %d, pkt type:%x)\n",
hdr->size, handler->pkt_sz, hdr->pkt_type);
return hdr->pkt_type;
}
if (handler->is_sys_pkt) {
inst = NULL;
} else {
struct hfi_session_pkt *pkt;
pkt = (struct hfi_session_pkt *)hdr;
inst = to_instance(core, pkt->shdr.session_id);
if (!inst)
dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n",
pkt->shdr.session_id,
handler ? handler->pkt : 0);
/*
* Event of type HFI_EVENT_SYS_ERROR will not have any session
* associated with it
*/
if (!inst && hdr->pkt_type != HFI_MSG_EVENT_NOTIFY) {
dev_err(dev, "got invalid session id:%x\n",
pkt->shdr.session_id);
goto invalid_session;
}
}
handler->done(core, inst, hdr);
invalid_session:
return hdr->pkt_type;
}