| /* |
| * 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) |
| { |
| struct hfi_event_data event = {0}; |
| int num_properties_changed; |
| struct hfi_framesize *frame_sz; |
| struct hfi_profile_level *profile_level; |
| 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; |
| 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; |
| } |
| |
| 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 = 1; |
| break; |
| case HFI_BUFFER_MODE_DYNAMIC: |
| inst->cap_bufs_mode_dynamic = 1; |
| 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; |
| } |