android_kernel_xiaomi_sm8350/msm/vidc/msm_vidc_common.c
Vikash Garodia 612a31d1ac msm: vidc: update video resolution during reconfig event
Video port resolution can be updated during reconfig event itself.
With this, reconfig height and width was removed. Also relevant
code cleanup was made accordingly.

Change-Id: I4fb38563b4c747eb356f28b7df7e99bd397ff8d1
Signed-off-by: Vikash Garodia <vgarodia@codeaurora.org>
2019-06-06 23:06:24 -07:00

6877 lines
178 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <soc/qcom/subsystem_restart.h>
#include <asm/div64.h>
#include "msm_vidc_common.h"
#include "vidc_hfi_api.h"
#include "vidc_hfi.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_clocks.h"
#include "msm_cvp_external.h"
#include "msm_cvp_internal.h"
#include "msm_vidc_buffer_calculations.h"
#define IS_ALREADY_IN_STATE(__p, __d) (\
(__p >= __d)\
)
#define V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT \
V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT
#define V4L2_EVENT_RELEASE_BUFFER_REFERENCE \
V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE
#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
static void handle_session_error(enum hal_command_response cmd, void *data);
static void msm_vidc_print_running_insts(struct msm_vidc_core *core);
#define V4L2_H264_LEVEL_UNKNOWN V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN
#define V4L2_HEVC_LEVEL_UNKNOWN V4L2_MPEG_VIDEO_HEVC_LEVEL_UNKNOWN
#define V4L2_VP9_LEVEL_61 V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61
int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id)
{
struct v4l2_ctrl *ctrl;
ctrl = get_ctrl(inst, id);
return ctrl->val;
}
static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst,
int num_ctrls)
{
int c = 0;
struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
num_ctrls, GFP_KERNEL);
if (!cluster || !inst) {
kfree(cluster);
return NULL;
}
for (c = 0; c < num_ctrls; c++)
cluster[c] = inst->ctrls[c];
return cluster;
}
int msm_comm_hfi_to_v4l2(int id, int value)
{
switch (id) {
/* H264 */
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
switch (value) {
case HFI_H264_PROFILE_BASELINE:
return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
case HFI_H264_PROFILE_CONSTRAINED_BASE:
return
V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE;
case HFI_H264_PROFILE_MAIN:
return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
case HFI_H264_PROFILE_HIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
case HFI_H264_PROFILE_STEREO_HIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH;
case HFI_H264_PROFILE_MULTIVIEW_HIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH;
case HFI_H264_PROFILE_CONSTRAINED_HIGH:
return V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
switch (value) {
case HFI_H264_LEVEL_1:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
case HFI_H264_LEVEL_1b:
return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
case HFI_H264_LEVEL_11:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
case HFI_H264_LEVEL_12:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_2;
case HFI_H264_LEVEL_13:
return V4L2_MPEG_VIDEO_H264_LEVEL_1_3;
case HFI_H264_LEVEL_2:
return V4L2_MPEG_VIDEO_H264_LEVEL_2_0;
case HFI_H264_LEVEL_21:
return V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
case HFI_H264_LEVEL_22:
return V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
case HFI_H264_LEVEL_3:
return V4L2_MPEG_VIDEO_H264_LEVEL_3_0;
case HFI_H264_LEVEL_31:
return V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
case HFI_H264_LEVEL_32:
return V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
case HFI_H264_LEVEL_4:
return V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
case HFI_H264_LEVEL_41:
return V4L2_MPEG_VIDEO_H264_LEVEL_4_1;
case HFI_H264_LEVEL_42:
return V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
case HFI_H264_LEVEL_5:
return V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
case HFI_H264_LEVEL_51:
return V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
case HFI_H264_LEVEL_52:
return V4L2_MPEG_VIDEO_H264_LEVEL_5_2;
case HFI_H264_LEVEL_6:
return V4L2_MPEG_VIDEO_H264_LEVEL_6_0;
case HFI_H264_LEVEL_61:
return V4L2_MPEG_VIDEO_H264_LEVEL_6_1;
case HFI_H264_LEVEL_62:
return V4L2_MPEG_VIDEO_H264_LEVEL_6_2;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
switch (value) {
case HFI_H264_ENTROPY_CAVLC:
return V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC;
case HFI_H264_ENTROPY_CABAC:
return V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
switch (value) {
case HFI_HEVC_PROFILE_MAIN:
return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN;
case HFI_HEVC_PROFILE_MAIN10:
return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10;
case HFI_HEVC_PROFILE_MAIN_STILL_PIC:
return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
switch (value) {
case HFI_HEVC_LEVEL_1:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_1;
case HFI_HEVC_LEVEL_2:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_2;
case HFI_HEVC_LEVEL_21:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1;
case HFI_HEVC_LEVEL_3:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_3;
case HFI_HEVC_LEVEL_31:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1;
case HFI_HEVC_LEVEL_4:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_4;
case HFI_HEVC_LEVEL_41:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1;
case HFI_HEVC_LEVEL_5:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_5;
case HFI_HEVC_LEVEL_51:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1;
case HFI_HEVC_LEVEL_52:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2;
case HFI_HEVC_LEVEL_6:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_6;
case HFI_HEVC_LEVEL_61:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1;
case HFI_HEVC_LEVEL_62:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2;
case HFI_LEVEL_UNKNOWN:
return V4L2_MPEG_VIDEO_HEVC_LEVEL_UNKNOWN;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
switch (value) {
case HFI_VP8_LEVEL_VERSION_0:
return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0;
case HFI_VP8_LEVEL_VERSION_1:
return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1;
case HFI_VP8_LEVEL_VERSION_2:
return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2;
case HFI_VP8_LEVEL_VERSION_3:
return V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3;
case HFI_LEVEL_UNKNOWN:
return V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
switch (value) {
case HFI_VP9_PROFILE_P0:
return V4L2_MPEG_VIDEO_VP9_PROFILE_0;
case HFI_VP9_PROFILE_P2_10B:
return V4L2_MPEG_VIDEO_VP9_PROFILE_2;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL:
switch (value) {
case HFI_VP9_LEVEL_1:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_1;
case HFI_VP9_LEVEL_11:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_11;
case HFI_VP9_LEVEL_2:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_2;
case HFI_VP9_LEVEL_21:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_21;
case HFI_VP9_LEVEL_3:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_3;
case HFI_VP9_LEVEL_31:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_31;
case HFI_VP9_LEVEL_4:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_4;
case HFI_VP9_LEVEL_41:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_41;
case HFI_VP9_LEVEL_5:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_5;
case HFI_VP9_LEVEL_51:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51;
case HFI_VP9_LEVEL_6:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6;
case HFI_VP9_LEVEL_61:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61;
case HFI_LEVEL_UNKNOWN:
return V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_UNUSED;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE:
switch (value) {
case HFI_MPEG2_PROFILE_SIMPLE:
return V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE;
case HFI_MPEG2_PROFILE_MAIN:
return V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN;
default:
goto unknown_value;
}
case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL:
/* This mapping is not defined properly in V4L2 */
switch (value) {
case HFI_MPEG2_LEVEL_LL:
return V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0;
case HFI_MPEG2_LEVEL_ML:
return V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1;
case HFI_MPEG2_LEVEL_HL:
return V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2;
default:
goto unknown_value;
}
}
unknown_value:
dprintk(VIDC_ERR, "Unknown control (%x, %d)\n", id, value);
return -EINVAL;
}
static int h264_level_v4l2_to_hfi(int value)
{
switch (value) {
case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
return HFI_H264_LEVEL_1;
case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
return HFI_H264_LEVEL_1b;
case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
return HFI_H264_LEVEL_11;
case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
return HFI_H264_LEVEL_12;
case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
return HFI_H264_LEVEL_13;
case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
return HFI_H264_LEVEL_2;
case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
return HFI_H264_LEVEL_21;
case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
return HFI_H264_LEVEL_22;
case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
return HFI_H264_LEVEL_3;
case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
return HFI_H264_LEVEL_31;
case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
return HFI_H264_LEVEL_32;
case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
return HFI_H264_LEVEL_4;
case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
return HFI_H264_LEVEL_41;
case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
return HFI_H264_LEVEL_42;
case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
return HFI_H264_LEVEL_5;
case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
return HFI_H264_LEVEL_51;
case V4L2_MPEG_VIDEO_H264_LEVEL_5_2:
return HFI_H264_LEVEL_52;
case V4L2_MPEG_VIDEO_H264_LEVEL_6_0:
return HFI_H264_LEVEL_6;
case V4L2_MPEG_VIDEO_H264_LEVEL_6_1:
return HFI_H264_LEVEL_61;
case V4L2_MPEG_VIDEO_H264_LEVEL_6_2:
return HFI_H264_LEVEL_62;
case V4L2_MPEG_VIDEO_H264_LEVEL_UNKNOWN:
return HFI_LEVEL_UNKNOWN;
default:
goto unknown_value;
}
unknown_value:
dprintk(VIDC_ERR, "Unknown level (%d)\n", value);
return -EINVAL;
}
static int hevc_level_v4l2_to_hfi(int value)
{
switch (value) {
case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
return HFI_HEVC_LEVEL_1;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
return HFI_HEVC_LEVEL_2;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
return HFI_HEVC_LEVEL_21;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
return HFI_HEVC_LEVEL_3;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
return HFI_HEVC_LEVEL_31;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
return HFI_HEVC_LEVEL_4;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
return HFI_HEVC_LEVEL_41;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
return HFI_HEVC_LEVEL_5;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
return HFI_HEVC_LEVEL_51;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
return HFI_HEVC_LEVEL_52;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_6:
return HFI_HEVC_LEVEL_6;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1:
return HFI_HEVC_LEVEL_61;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2:
return HFI_HEVC_LEVEL_62;
case V4L2_MPEG_VIDEO_HEVC_LEVEL_UNKNOWN:
return HFI_LEVEL_UNKNOWN;
default:
goto unknown_value;
}
unknown_value:
dprintk(VIDC_ERR, "Unknown level (%d)\n", value);
return -EINVAL;
}
static int vp9_level_v4l2_to_hfi(int value)
{
switch (value) {
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_1:
return HFI_VP9_LEVEL_1;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_11:
return HFI_VP9_LEVEL_11;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_2:
return HFI_VP9_LEVEL_2;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_21:
return HFI_VP9_LEVEL_21;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_3:
return HFI_VP9_LEVEL_3;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_31:
return HFI_VP9_LEVEL_31;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_4:
return HFI_VP9_LEVEL_4;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_41:
return HFI_VP9_LEVEL_41;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_5:
return HFI_VP9_LEVEL_5;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_51:
return HFI_VP9_LEVEL_51;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_6:
return HFI_VP9_LEVEL_6;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61:
return HFI_VP9_LEVEL_61;
case V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_UNUSED:
return HFI_LEVEL_UNKNOWN;
default:
goto unknown_value;
}
unknown_value:
dprintk(VIDC_ERR, "Unknown level (%d)\n", value);
return -EINVAL;
}
int msm_comm_v4l2_to_hfi(int id, int value)
{
switch (id) {
/* H264 */
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
switch (value) {
case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
return HFI_H264_PROFILE_BASELINE;
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
return HFI_H264_PROFILE_CONSTRAINED_BASE;
case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
return HFI_H264_PROFILE_MAIN;
case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
return HFI_H264_PROFILE_HIGH;
case V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH:
return HFI_H264_PROFILE_STEREO_HIGH;
case V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH:
return HFI_H264_PROFILE_MULTIVIEW_HIGH;
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH:
return HFI_H264_PROFILE_CONSTRAINED_HIGH;
default:
return HFI_H264_PROFILE_HIGH;
}
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
return h264_level_v4l2_to_hfi(value);
case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
switch (value) {
case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC:
return HFI_H264_ENTROPY_CAVLC;
case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC:
return HFI_H264_ENTROPY_CABAC;
default:
return HFI_H264_ENTROPY_CABAC;
}
case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
switch (value) {
case V4L2_MPEG_VIDEO_VP8_PROFILE_0:
return HFI_VP8_PROFILE_MAIN;
default:
return HFI_VP8_PROFILE_MAIN;
}
case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
switch (value) {
case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0:
return HFI_VP8_LEVEL_VERSION_0;
case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1:
return HFI_VP8_LEVEL_VERSION_1;
case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2:
return HFI_VP8_LEVEL_VERSION_2;
case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3:
return HFI_VP8_LEVEL_VERSION_3;
case V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED:
return HFI_LEVEL_UNKNOWN;
default:
return HFI_LEVEL_UNKNOWN;
}
case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
switch (value) {
case V4L2_MPEG_VIDEO_VP9_PROFILE_0:
return HFI_VP9_PROFILE_P0;
case V4L2_MPEG_VIDEO_VP9_PROFILE_2:
return HFI_VP9_PROFILE_P2_10B;
default:
return HFI_VP9_PROFILE_P0;
}
case V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL:
return vp9_level_v4l2_to_hfi(value);
case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
switch (value) {
case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
return HFI_HEVC_PROFILE_MAIN;
case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
return HFI_HEVC_PROFILE_MAIN10;
case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
return HFI_HEVC_PROFILE_MAIN_STILL_PIC;
default:
return HFI_HEVC_PROFILE_MAIN;
}
case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
return hevc_level_v4l2_to_hfi(value);
case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
switch (value) {
case V4L2_MPEG_VIDEO_HEVC_TIER_MAIN:
return HFI_HEVC_TIER_MAIN;
case V4L2_MPEG_VIDEO_HEVC_TIER_HIGH:
return HFI_HEVC_TIER_HIGH;
default:
return HFI_HEVC_TIER_HIGH;
}
case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE:
switch (value) {
case V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE:
return HFI_MPEG2_PROFILE_SIMPLE;
case V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN:
return HFI_MPEG2_PROFILE_MAIN;
default:
return HFI_MPEG2_PROFILE_MAIN;
}
case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL:
/* This mapping is not defined properly in V4L2 */
switch (value) {
case V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0:
return HFI_MPEG2_LEVEL_LL;
case V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1:
return HFI_MPEG2_LEVEL_ML;
case V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2:
return HFI_MPEG2_LEVEL_HL;
default:
return HFI_MPEG2_LEVEL_HL;
}
case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
switch (value) {
case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED:
return HFI_H264_DB_MODE_DISABLE;
case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED:
return HFI_H264_DB_MODE_ALL_BOUNDARY;
case L_MODE:
return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY;
default:
return HFI_H264_DB_MODE_ALL_BOUNDARY;
}
}
dprintk(VIDC_ERR, "Unknown control (%x, %d)\n", id, value);
return -EINVAL;
}
int msm_comm_get_v4l2_profile(int fourcc, int profile)
{
switch (fourcc) {
case V4L2_PIX_FMT_H264:
return msm_comm_hfi_to_v4l2(
V4L2_CID_MPEG_VIDEO_H264_PROFILE,
profile);
case V4L2_PIX_FMT_HEVC:
return msm_comm_hfi_to_v4l2(
V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
profile);
case V4L2_PIX_FMT_VP8:
case V4L2_PIX_FMT_VP9:
case V4L2_PIX_FMT_MPEG2:
return 0;
default:
dprintk(VIDC_ERR, "Unknown codec id %x\n", fourcc);
return 0;
}
}
int msm_comm_get_v4l2_level(int fourcc, int level)
{
switch (fourcc) {
case V4L2_PIX_FMT_H264:
return msm_comm_hfi_to_v4l2(
V4L2_CID_MPEG_VIDEO_H264_LEVEL,
level);
case V4L2_PIX_FMT_HEVC:
level &= ~(0xF << 28);
return msm_comm_hfi_to_v4l2(
V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
level);
case V4L2_PIX_FMT_VP8:
return msm_comm_hfi_to_v4l2(
V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
level);
case V4L2_PIX_FMT_VP9:
case V4L2_PIX_FMT_MPEG2:
return 0;
default:
dprintk(VIDC_ERR, "Unknown codec id %x\n", fourcc);
return 0;
}
}
int msm_comm_ctrl_init(struct msm_vidc_inst *inst,
struct msm_vidc_ctrl *drv_ctrls, u32 num_ctrls,
const struct v4l2_ctrl_ops *ctrl_ops)
{
int idx = 0;
struct v4l2_ctrl_config ctrl_cfg = {0};
int ret_val = 0;
if (!inst || !drv_ctrls || !ctrl_ops || !num_ctrls) {
dprintk(VIDC_ERR, "%s - invalid input\n", __func__);
return -EINVAL;
}
inst->ctrls = kcalloc(num_ctrls, sizeof(struct v4l2_ctrl *),
GFP_KERNEL);
if (!inst->ctrls) {
dprintk(VIDC_ERR, "%s - failed to allocate ctrl\n", __func__);
return -ENOMEM;
}
ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls);
if (ret_val) {
dprintk(VIDC_ERR, "CTRL ERR: Control handler init failed, %d\n",
inst->ctrl_handler.error);
return ret_val;
}
for (; idx < (int) num_ctrls; idx++) {
struct v4l2_ctrl *ctrl = NULL;
if (IS_PRIV_CTRL(drv_ctrls[idx].id)) {
/*add private control*/
ctrl_cfg.def = drv_ctrls[idx].default_value;
ctrl_cfg.flags = 0;
ctrl_cfg.id = drv_ctrls[idx].id;
ctrl_cfg.max = drv_ctrls[idx].maximum;
ctrl_cfg.min = drv_ctrls[idx].minimum;
ctrl_cfg.menu_skip_mask =
drv_ctrls[idx].menu_skip_mask;
ctrl_cfg.name = drv_ctrls[idx].name;
ctrl_cfg.ops = ctrl_ops;
ctrl_cfg.step = drv_ctrls[idx].step;
ctrl_cfg.type = drv_ctrls[idx].type;
ctrl_cfg.qmenu = drv_ctrls[idx].qmenu;
ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
&ctrl_cfg, NULL);
} else {
if (drv_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
ctrl = v4l2_ctrl_new_std_menu(
&inst->ctrl_handler,
ctrl_ops,
drv_ctrls[idx].id,
(u8) drv_ctrls[idx].maximum,
drv_ctrls[idx].menu_skip_mask,
(u8) drv_ctrls[idx].default_value);
} else {
ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
ctrl_ops,
drv_ctrls[idx].id,
drv_ctrls[idx].minimum,
drv_ctrls[idx].maximum,
drv_ctrls[idx].step,
drv_ctrls[idx].default_value);
}
}
if (!ctrl) {
dprintk(VIDC_ERR, "%s - invalid ctrl %s\n", __func__,
drv_ctrls[idx].name);
return -EINVAL;
}
ret_val = inst->ctrl_handler.error;
if (ret_val) {
dprintk(VIDC_ERR,
"Error adding ctrl (%s) to ctrl handle, %d\n",
drv_ctrls[idx].name, inst->ctrl_handler.error);
return ret_val;
}
ctrl->flags |= drv_ctrls[idx].flags;
inst->ctrls[idx] = ctrl;
}
inst->num_ctrls = num_ctrls;
/* Construct a super cluster of all controls */
inst->cluster = get_super_cluster(inst, num_ctrls);
if (!inst->cluster) {
dprintk(VIDC_ERR,
"Failed to setup super cluster\n");
return -EINVAL;
}
v4l2_ctrl_cluster(num_ctrls, inst->cluster);
return ret_val;
}
int msm_comm_ctrl_deinit(struct msm_vidc_inst *inst)
{
if (!inst) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
kfree(inst->ctrls);
kfree(inst->cluster);
v4l2_ctrl_handler_free(&inst->ctrl_handler);
return 0;
}
int msm_comm_set_stream_output_mode(struct msm_vidc_inst *inst,
enum multi_stream mode)
{
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return -EINVAL;
}
if (!is_decode_session(inst)) {
dprintk(VIDC_HIGH, "%s: not a decode session %x\n",
__func__, hash32_ptr(inst->session));
return -EINVAL;
}
if (mode == HAL_VIDEO_DECODER_SECONDARY)
inst->stream_output_mode = HAL_VIDEO_DECODER_SECONDARY;
else
inst->stream_output_mode = HAL_VIDEO_DECODER_PRIMARY;
return 0;
}
enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst)
{
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid params, return default mode\n",
__func__);
return HAL_VIDEO_DECODER_PRIMARY;
}
if (!is_decode_session(inst))
return HAL_VIDEO_DECODER_PRIMARY;
if (inst->stream_output_mode == HAL_VIDEO_DECODER_SECONDARY)
return HAL_VIDEO_DECODER_SECONDARY;
else
return HAL_VIDEO_DECODER_PRIMARY;
}
static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst)
{
int input_port_mbs, output_port_mbs;
int fps;
struct v4l2_format *f;
f = &inst->fmts[INPUT_PORT].v4l2_fmt;
input_port_mbs = NUM_MBS_PER_FRAME(f->fmt.pix_mp.width,
f->fmt.pix_mp.height);
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
output_port_mbs = NUM_MBS_PER_FRAME(f->fmt.pix_mp.width,
f->fmt.pix_mp.height);
if (inst->clk_data.operating_rate > inst->clk_data.frame_rate)
fps = (inst->clk_data.operating_rate >> 16) ?
inst->clk_data.operating_rate >> 16 : 1;
else
fps = inst->clk_data.frame_rate >> 16;
return max(input_port_mbs, output_port_mbs) * fps;
}
int msm_comm_get_inst_load(struct msm_vidc_inst *inst,
enum load_calc_quirks quirks)
{
int load = 0;
mutex_lock(&inst->lock);
if (!(inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_STOP_DONE))
goto exit;
load = msm_comm_get_mbs_per_sec(inst);
if (is_thumbnail_session(inst)) {
if (quirks & LOAD_CALC_IGNORE_THUMBNAIL_LOAD)
load = 0;
}
if (is_turbo_session(inst)) {
if (!(quirks & LOAD_CALC_IGNORE_TURBO_LOAD))
load = inst->core->resources.max_load;
}
/* Clock and Load calculations for REALTIME/NON-REALTIME
* OPERATING RATE SET/NO OPERATING RATE SET
*
* | OPERATING RATE SET | OPERATING RATE NOT SET |
* ----------------|--------------------- |------------------------|
* REALTIME | load = res * op_rate | load = res * fps |
* | clk = res * op_rate | clk = res * fps |
* ----------------|----------------------|------------------------|
* NON-REALTIME | load = res * 1 fps | load = res * 1 fps |
* | clk = res * op_rate | clk = res * fps |
* ----------------|----------------------|------------------------|
*/
if (!is_realtime_session(inst) &&
(quirks & LOAD_CALC_IGNORE_NON_REALTIME_LOAD)) {
if (!(inst->clk_data.frame_rate >> 16)) {
dprintk(VIDC_LOW, "instance:%pK fps = 0\n", inst);
load = 0;
} else {
load = msm_comm_get_mbs_per_sec(inst) /
(inst->clk_data.frame_rate >> 16);
}
}
exit:
mutex_unlock(&inst->lock);
return load;
}
int msm_comm_get_inst_load_per_core(struct msm_vidc_inst *inst,
enum load_calc_quirks quirks)
{
int load = msm_comm_get_inst_load(inst, quirks);
if (inst->clk_data.core_id == VIDC_CORE_ID_3)
load = load / 2;
return load;
}
int msm_comm_get_load(struct msm_vidc_core *core,
enum session_type type, enum load_calc_quirks quirks)
{
struct msm_vidc_inst *inst = NULL;
int num_mbs_per_sec = 0;
if (!core) {
dprintk(VIDC_ERR, "Invalid args: %pK\n", core);
return -EINVAL;
}
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
if (inst->session_type != type)
continue;
num_mbs_per_sec += msm_comm_get_inst_load(inst, quirks);
}
mutex_unlock(&core->lock);
return num_mbs_per_sec;
}
enum hal_domain get_hal_domain(int session_type)
{
enum hal_domain domain;
switch (session_type) {
case MSM_VIDC_ENCODER:
domain = HAL_VIDEO_DOMAIN_ENCODER;
break;
case MSM_VIDC_DECODER:
domain = HAL_VIDEO_DOMAIN_DECODER;
break;
case MSM_VIDC_CVP:
domain = HAL_VIDEO_DOMAIN_CVP;
break;
default:
dprintk(VIDC_ERR, "Wrong domain %d\n", session_type);
domain = HAL_UNUSED_DOMAIN;
break;
}
return domain;
}
enum hal_video_codec get_hal_codec(int fourcc)
{
enum hal_video_codec codec;
switch (fourcc) {
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_H264_NO_SC:
codec = HAL_VIDEO_CODEC_H264;
break;
case V4L2_PIX_FMT_H264_MVC:
codec = HAL_VIDEO_CODEC_MVC;
break;
case V4L2_PIX_FMT_MPEG1:
codec = HAL_VIDEO_CODEC_MPEG1;
break;
case V4L2_PIX_FMT_MPEG2:
codec = HAL_VIDEO_CODEC_MPEG2;
break;
case V4L2_PIX_FMT_VP8:
codec = HAL_VIDEO_CODEC_VP8;
break;
case V4L2_PIX_FMT_VP9:
codec = HAL_VIDEO_CODEC_VP9;
break;
case V4L2_PIX_FMT_HEVC:
codec = HAL_VIDEO_CODEC_HEVC;
break;
case V4L2_PIX_FMT_TME:
codec = HAL_VIDEO_CODEC_TME;
break;
case V4L2_PIX_FMT_CVP:
codec = HAL_VIDEO_CODEC_CVP;
break;
default:
dprintk(VIDC_ERR, "Wrong codec: %#x\n", fourcc);
codec = HAL_UNUSED_CODEC;
break;
}
return codec;
}
enum hal_uncompressed_format msm_comm_get_hal_uncompressed(int fourcc)
{
enum hal_uncompressed_format format = HAL_UNUSED_COLOR;
switch (fourcc) {
case V4L2_PIX_FMT_NV12:
format = HAL_COLOR_FORMAT_NV12;
break;
case V4L2_PIX_FMT_NV12_512:
format = HAL_COLOR_FORMAT_NV12_512;
break;
case V4L2_PIX_FMT_NV21:
format = HAL_COLOR_FORMAT_NV21;
break;
case V4L2_PIX_FMT_NV12_UBWC:
format = HAL_COLOR_FORMAT_NV12_UBWC;
break;
case V4L2_PIX_FMT_NV12_TP10_UBWC:
format = HAL_COLOR_FORMAT_NV12_TP10_UBWC;
break;
case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS:
format = HAL_COLOR_FORMAT_P010;
break;
default:
format = HAL_UNUSED_COLOR;
break;
}
return format;
}
u32 msm_comm_get_hfi_uncompressed(int fourcc)
{
u32 format;
switch (fourcc) {
case V4L2_PIX_FMT_NV12:
format = HFI_COLOR_FORMAT_NV12;
break;
case V4L2_PIX_FMT_NV12_512:
format = HFI_COLOR_FORMAT_NV12;
break;
case V4L2_PIX_FMT_NV21:
format = HFI_COLOR_FORMAT_NV21;
break;
case V4L2_PIX_FMT_NV12_UBWC:
format = HFI_COLOR_FORMAT_NV12_UBWC;
break;
case V4L2_PIX_FMT_NV12_TP10_UBWC:
format = HFI_COLOR_FORMAT_YUV420_TP10_UBWC;
break;
case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS:
format = HFI_COLOR_FORMAT_P010;
break;
default:
format = HFI_COLOR_FORMAT_NV12_UBWC;
dprintk(VIDC_ERR, "Invalid format, defaulting to UBWC");
break;
}
return format;
}
struct msm_vidc_core *get_vidc_core(int core_id)
{
struct msm_vidc_core *core;
int found = 0;
if (core_id > MSM_VIDC_CORES_MAX) {
dprintk(VIDC_ERR, "Core id = %d is greater than max = %d\n",
core_id, MSM_VIDC_CORES_MAX);
return NULL;
}
mutex_lock(&vidc_driver->lock);
list_for_each_entry(core, &vidc_driver->cores, list) {
if (core->id == core_id) {
found = 1;
break;
}
}
mutex_unlock(&vidc_driver->lock);
if (found)
return core;
return NULL;
}
const struct msm_vidc_format_desc *msm_comm_get_pixel_fmt_index(
const struct msm_vidc_format_desc fmt[], int size, int index)
{
int i, k = 0;
if (!fmt || index < 0) {
dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK, index = %d\n",
fmt, index);
return NULL;
}
for (i = 0; i < size; i++) {
if (k == index)
break;
k++;
}
if (i == size) {
dprintk(VIDC_HIGH, "Format not found\n");
return NULL;
}
return &fmt[i];
}
struct msm_vidc_format_desc *msm_comm_get_pixel_fmt_fourcc(
struct msm_vidc_format_desc fmt[], int size, int fourcc)
{
int i;
if (!fmt) {
dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK\n", fmt);
return NULL;
}
for (i = 0; i < size; i++) {
if (fmt[i].fourcc == fourcc)
break;
}
if (i == size) {
dprintk(VIDC_HIGH, "Format not found\n");
return NULL;
}
return &fmt[i];
}
struct msm_vidc_format_constraint *msm_comm_get_pixel_fmt_constraints(
struct msm_vidc_format_constraint fmt[], int size, int fourcc)
{
int i;
if (!fmt) {
dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK\n", fmt);
return NULL;
}
for (i = 0; i < size; i++) {
if (fmt[i].fourcc == fourcc)
break;
}
if (i == size) {
dprintk(VIDC_HIGH, "Format constraint not found.\n");
return NULL;
}
return &fmt[i];
}
struct buf_queue *msm_comm_get_vb2q(
struct msm_vidc_inst *inst, enum v4l2_buf_type type)
{
if (type == OUTPUT_MPLANE)
return &inst->bufq[OUTPUT_PORT];
if (type == INPUT_MPLANE)
return &inst->bufq[INPUT_PORT];
return NULL;
}
static void update_capability(struct msm_vidc_codec_capability *in,
struct msm_vidc_capability *capability)
{
if (!in || !capability) {
dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
return;
}
if (in->capability_type < CAP_MAX) {
capability->cap[in->capability_type].capability_type =
in->capability_type;
capability->cap[in->capability_type].min = in->min;
capability->cap[in->capability_type].max = in->max;
capability->cap[in->capability_type].step_size = in->step_size;
capability->cap[in->capability_type].default_value =
in->default_value;
} else {
dprintk(VIDC_ERR, "%s: invalid capability_type %d\n",
__func__, in->capability_type);
}
}
static int msm_vidc_capabilities(struct msm_vidc_core *core)
{
int rc = 0;
struct msm_vidc_codec_capability *platform_caps;
int i, j, num_platform_caps;
if (!core || !core->capabilities) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return -EINVAL;
}
platform_caps = core->resources.codec_caps;
num_platform_caps = core->resources.codec_caps_count;
dprintk(VIDC_HIGH, "%s: num caps %d\n", __func__, num_platform_caps);
/* loop over each platform capability */
for (i = 0; i < num_platform_caps; i++) {
/* select matching core codec and update it */
for (j = 0; j < core->resources.codecs_count; j++) {
if ((platform_caps[i].domains &
core->capabilities[j].domain) &&
(platform_caps[i].codecs &
core->capabilities[j].codec)) {
/* update core capability */
update_capability(&platform_caps[i],
&core->capabilities[j]);
}
}
}
return rc;
}
static void handle_sys_init_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core;
if (!IS_HAL_SYS_CMD(cmd)) {
dprintk(VIDC_ERR, "%s - invalid cmd\n", __func__);
return;
}
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys init\n");
return;
}
core = get_vidc_core(response->device_id);
if (!core) {
dprintk(VIDC_ERR, "Wrong device_id received\n");
return;
}
dprintk(VIDC_HIGH, "%s: core %pK\n", __func__, core);
complete(&(core->completions[SYS_MSG_INDEX(cmd)]));
}
static void put_inst_helper(struct kref *kref)
{
struct msm_vidc_inst *inst = container_of(kref,
struct msm_vidc_inst, kref);
msm_vidc_destroy(inst);
}
void put_inst(struct msm_vidc_inst *inst)
{
if (!inst)
return;
kref_put(&inst->kref, put_inst_helper);
}
struct msm_vidc_inst *get_inst(struct msm_vidc_core *core,
void *session_id)
{
struct msm_vidc_inst *inst = NULL;
bool matches = false;
if (!core || !session_id)
return NULL;
mutex_lock(&core->lock);
/*
* This is as good as !list_empty(!inst->list), but at this point
* we don't really know if inst was kfree'd via close syscall before
* hardware could respond. So manually walk thru the list of active
* sessions
*/
list_for_each_entry(inst, &core->instances, list) {
if (inst == session_id) {
/*
* Even if the instance is valid, we really shouldn't
* be receiving or handling callbacks when we've deleted
* our session with HFI
*/
matches = !!inst->session;
break;
}
}
/*
* kref_* is atomic_int backed, so no need for inst->lock. But we can
* always acquire inst->lock and release it in put_inst for a stronger
* locking system.
*/
inst = (matches && kref_get_unless_zero(&inst->kref)) ? inst : NULL;
mutex_unlock(&core->lock);
return inst;
}
static void handle_session_release_buf_done(enum hal_command_response cmd,
void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct internal_buf *buf;
struct list_head *ptr, *next;
struct hal_buffer_info *buffer;
u32 buf_found = false;
u32 address;
if (!response) {
dprintk(VIDC_ERR, "Invalid release_buf_done response\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
buffer = &response->data.buffer_info;
address = buffer->buffer_addr;
mutex_lock(&inst->scratchbufs.lock);
list_for_each_safe(ptr, next, &inst->scratchbufs.list) {
buf = list_entry(ptr, struct internal_buf, list);
if (address == buf->smem.device_addr) {
dprintk(VIDC_HIGH, "releasing scratch: %x\n",
buf->smem.device_addr);
buf_found = true;
}
}
mutex_unlock(&inst->scratchbufs.lock);
mutex_lock(&inst->persistbufs.lock);
list_for_each_safe(ptr, next, &inst->persistbufs.list) {
buf = list_entry(ptr, struct internal_buf, list);
if (address == buf->smem.device_addr) {
dprintk(VIDC_HIGH, "releasing persist: %x\n",
buf->smem.device_addr);
buf_found = true;
}
}
mutex_unlock(&inst->persistbufs.lock);
if (!buf_found)
dprintk(VIDC_ERR, "invalid buffer received from firmware");
if (IS_HAL_SESSION_CMD(cmd))
complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
else
dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
put_inst(inst);
}
static void handle_sys_release_res_done(
enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys init\n");
return;
}
core = get_vidc_core(response->device_id);
if (!core) {
dprintk(VIDC_ERR, "Wrong device_id received\n");
return;
}
complete(&core->completions[
SYS_MSG_INDEX(HAL_SYS_RELEASE_RESOURCE_DONE)]);
}
void change_inst_state(struct msm_vidc_inst *inst, enum instance_state state)
{
if (!inst) {
dprintk(VIDC_ERR, "Invalid parameter %s\n", __func__);
return;
}
mutex_lock(&inst->lock);
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_HIGH,
"Inst: %pK is in bad state can't change state to %d\n",
inst, state);
goto exit;
}
dprintk(VIDC_HIGH, "Moved inst: %pK from state: %d to state: %d\n",
inst, inst->state, state);
inst->state = state;
exit:
mutex_unlock(&inst->lock);
}
static int signal_session_msg_receipt(enum hal_command_response cmd,
struct msm_vidc_inst *inst)
{
if (!inst) {
dprintk(VIDC_ERR, "Invalid(%pK) instance id\n", inst);
return -EINVAL;
}
if (IS_HAL_SESSION_CMD(cmd)) {
complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
} else {
dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
return -EINVAL;
}
return 0;
}
static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
enum hal_command_response cmd)
{
int rc = 0;
struct hfi_device *hdev;
if (!IS_HAL_SESSION_CMD(cmd)) {
dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
return -EINVAL;
}
hdev = (struct hfi_device *)(inst->core->device);
rc = wait_for_completion_timeout(
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(
inst->core->resources.msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "Wait interrupted or timed out: %d\n",
SESSION_MSG_INDEX(cmd));
msm_comm_kill_session(inst);
rc = -EIO;
} else {
rc = 0;
}
return rc;
}
static int wait_for_state(struct msm_vidc_inst *inst,
enum instance_state flipped_state,
enum instance_state desired_state,
enum hal_command_response hal_cmd)
{
int rc = 0;
if (IS_ALREADY_IN_STATE(flipped_state, desired_state)) {
dprintk(VIDC_HIGH, "inst: %pK is already in state: %d\n",
inst, inst->state);
goto err_same_state;
}
dprintk(VIDC_HIGH, "Waiting for hal_cmd: %d\n", hal_cmd);
rc = wait_for_sess_signal_receipt(inst, hal_cmd);
if (!rc)
change_inst_state(inst, desired_state);
err_same_state:
return rc;
}
void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type)
{
struct v4l2_event event = {.id = 0, .type = event_type};
v4l2_event_queue_fh(&inst->event_handler, &event);
}
static void msm_comm_generate_max_clients_error(struct msm_vidc_inst *inst)
{
enum hal_command_response cmd = HAL_SESSION_ERROR;
struct msm_vidc_cb_cmd_done response = {0};
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return;
}
dprintk(VIDC_ERR, "%s: Too many clients\n", __func__);
response.session_id = inst;
response.status = VIDC_ERR_MAX_CLIENTS;
handle_session_error(cmd, (void *)&response);
}
static void print_cap(const char *type,
struct hal_capability_supported *cap)
{
dprintk(VIDC_HIGH,
"%-24s: %-10d %-10d %-10d %-10d\n",
type, cap->min, cap->max, cap->step_size, cap->default_value);
}
static int msm_vidc_comm_update_ctrl(struct msm_vidc_inst *inst,
u32 id, struct hal_capability_supported *cap)
{
struct v4l2_ctrl *ctrl = NULL;
int rc = 0;
ctrl = v4l2_ctrl_find(&inst->ctrl_handler, id);
if (!ctrl) {
dprintk(VIDC_ERR,
"%s: Conrol id %d not found\n", __func__, id);
return -EINVAL;
}
rc = v4l2_ctrl_modify_range(ctrl, cap->min, cap->max,
cap->step_size, cap->default_value);
if (rc) {
dprintk(VIDC_ERR,
"%s: failed: control name %s, min %d, max %d, step %d, default_value %d\n",
__func__, ctrl->name, cap->min, cap->max,
cap->step_size, cap->default_value);
goto error;
}
/*
* v4l2_ctrl_modify_range() is not updating default_value,
* so use v4l2_ctrl_s_ctrl() to update it.
*/
rc = v4l2_ctrl_s_ctrl(ctrl, cap->default_value);
if (rc) {
dprintk(VIDC_ERR,
"%s: failed s_ctrl: %s with value %d\n",
__func__, ctrl->name, cap->default_value);
goto error;
}
dprintk(VIDC_HIGH,
"Updated control: %s: min %lld, max %lld, step %lld, default value = %lld\n",
ctrl->name, ctrl->minimum, ctrl->maximum,
ctrl->step, ctrl->default_value);
error:
return rc;
}
static void msm_vidc_comm_update_ctrl_limits(struct msm_vidc_inst *inst)
{
struct v4l2_format *f;
if (inst->session_type == MSM_VIDC_ENCODER) {
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
if (get_hal_codec(f->fmt.pix_mp.pixelformat) ==
HAL_VIDEO_CODEC_TME)
return;
msm_vidc_comm_update_ctrl(inst, V4L2_CID_MPEG_VIDEO_BITRATE,
&inst->capability.cap[CAP_BITRATE]);
msm_vidc_comm_update_ctrl(inst,
V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT,
&inst->capability.cap[CAP_LTR_COUNT]);
msm_vidc_comm_update_ctrl(inst,
V4L2_CID_MPEG_VIDEO_B_FRAMES,
&inst->capability.cap[CAP_BFRAME]);
}
}
static void handle_session_init_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst = NULL;
struct msm_vidc_capability *capability = NULL;
struct msm_vidc_core *core;
u32 i, codec;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for session init\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
if (response->status) {
dprintk(VIDC_ERR,
"Session init response from FW : %#x\n",
response->status);
goto error;
}
if (inst->session_type == MSM_VIDC_CVP) {
dprintk(VIDC_HIGH, "%s: cvp session %#x\n",
__func__, hash32_ptr(inst->session));
signal_session_msg_receipt(cmd, inst);
put_inst(inst);
return;
}
core = inst->core;
codec = get_v4l2_codec(inst);
for (i = 0; i < core->resources.codecs_count; i++) {
if (core->capabilities[i].codec ==
get_hal_codec(codec) &&
core->capabilities[i].domain ==
get_hal_domain(inst->session_type)) {
capability = &core->capabilities[i];
break;
}
}
if (!capability) {
dprintk(VIDC_ERR,
"%s: capabilities not found for domain %#x codec %#x\n",
__func__, get_hal_domain(inst->session_type),
get_hal_codec(codec));
goto error;
}
dprintk(VIDC_HIGH,
"%s: capabilities for domain %#x codec %#x\n",
__func__, capability->domain, capability->codec);
memcpy(&inst->capability, capability,
sizeof(struct msm_vidc_capability));
dprintk(VIDC_HIGH,
"Capability type : min max step_size default_value\n");
print_cap("width", &inst->capability.cap[CAP_FRAME_WIDTH]);
print_cap("height", &inst->capability.cap[CAP_FRAME_HEIGHT]);
print_cap("mbs_per_frame", &inst->capability.cap[CAP_MBS_PER_FRAME]);
print_cap("mbs_per_sec", &inst->capability.cap[CAP_MBS_PER_SECOND]);
print_cap("frame_rate", &inst->capability.cap[CAP_FRAMERATE]);
print_cap("bitrate", &inst->capability.cap[CAP_BITRATE]);
print_cap("scale_x", &inst->capability.cap[CAP_SCALE_X]);
print_cap("scale_y", &inst->capability.cap[CAP_SCALE_Y]);
print_cap("hier_p", &inst->capability.cap[CAP_HIER_P_NUM_ENH_LAYERS]);
print_cap("ltr_count", &inst->capability.cap[CAP_LTR_COUNT]);
print_cap("bframe", &inst->capability.cap[CAP_BFRAME]);
print_cap("mbs_per_sec_low_power",
&inst->capability.cap[CAP_MBS_PER_SECOND_POWER_SAVE]);
print_cap("i_qp", &inst->capability.cap[CAP_I_FRAME_QP]);
print_cap("p_qp", &inst->capability.cap[CAP_P_FRAME_QP]);
print_cap("b_qp", &inst->capability.cap[CAP_B_FRAME_QP]);
print_cap("slice_bytes", &inst->capability.cap[CAP_SLICE_BYTE]);
print_cap("slice_mbs", &inst->capability.cap[CAP_SLICE_MB]);
msm_vidc_comm_update_ctrl_limits(inst);
signal_session_msg_receipt(cmd, inst);
put_inst(inst);
return;
error:
if (response->status == VIDC_ERR_MAX_CLIENTS)
msm_comm_generate_max_clients_error(inst);
else
msm_comm_generate_session_error(inst);
signal_session_msg_receipt(cmd, inst);
put_inst(inst);
}
static void msm_vidc_queue_rbr_event(struct msm_vidc_inst *inst,
int fd, u32 offset)
{
struct v4l2_event buf_event = {0};
u32 *ptr;
buf_event.type = V4L2_EVENT_RELEASE_BUFFER_REFERENCE;
ptr = (u32 *)buf_event.u.data;
ptr[0] = fd;
ptr[1] = offset;
v4l2_event_queue_fh(&inst->event_handler, &buf_event);
}
static void handle_event_change(enum hal_command_response cmd, void *data)
{
struct msm_vidc_inst *inst = NULL;
struct msm_vidc_cb_event *event_notify = data;
int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
struct v4l2_event seq_changed_event = {0};
int rc = 0;
struct hfi_device *hdev;
u32 *ptr = NULL;
struct msm_vidc_format *fmt;
struct v4l2_format *f;
int extra_buff_count = 0;
u32 codec;
if (!event_notify) {
dprintk(VIDC_ERR, "Got an empty event from hfi\n");
return;
}
inst = get_inst(get_vidc_core(event_notify->device_id),
event_notify->session_id);
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
goto err_bad_event;
}
hdev = inst->core->device;
switch (event_notify->hal_event_type) {
case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
{
/*
* Check if there is some parameter has changed
* If there is no change then no need to notify client
* If there is a change, then raise an insufficient event
*/
bool event_fields_changed = false;
dprintk(VIDC_HIGH, "V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n");
dprintk(VIDC_HIGH,
"event_notify->height = %d event_notify->width = %d\n",
event_notify->height,
event_notify->width);
event_fields_changed |= (inst->bit_depth !=
event_notify->bit_depth);
/* Check for change from hdr->non-hdr and vice versa */
if ((event_notify->colour_space == MSM_VIDC_BT2020 &&
inst->colour_space != MSM_VIDC_BT2020) ||
(event_notify->colour_space != MSM_VIDC_BT2020 &&
inst->colour_space == MSM_VIDC_BT2020))
event_fields_changed = true;
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
event_fields_changed |=
(f->fmt.pix_mp.height != event_notify->height);
event_fields_changed |=
(f->fmt.pix_mp.width != event_notify->width);
if (event_fields_changed) {
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
} else {
dprintk(VIDC_HIGH,
"No parameter change continue session\n");
rc = call_hfi_op(hdev, session_continue,
(void *)inst->session);
if (rc) {
dprintk(VIDC_ERR,
"failed to send session_continue\n");
}
goto err_bad_event;
}
break;
}
case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
break;
case HAL_EVENT_RELEASE_BUFFER_REFERENCE:
{
struct msm_vidc_buffer *mbuf;
u32 planes[VIDEO_MAX_PLANES] = {0};
dprintk(VIDC_LOW,
"%s: inst: %pK data_buffer: %x extradata_buffer: %x\n",
__func__, inst, event_notify->packet_buffer,
event_notify->extra_data_buffer);
planes[0] = event_notify->packet_buffer;
planes[1] = event_notify->extra_data_buffer;
mbuf = msm_comm_get_buffer_using_device_planes(inst,
OUTPUT_MPLANE, planes);
if (!mbuf || !kref_get_mbuf(inst, mbuf)) {
dprintk(VIDC_ERR,
"%s: data_addr %x, extradata_addr %x not found\n",
__func__, planes[0], planes[1]);
} else {
handle_release_buffer_reference(inst, mbuf);
kref_put_mbuf(mbuf);
}
goto err_bad_event;
}
default:
break;
}
/* Bit depth and pic struct changed event are combined into a single
* event (insufficient event) for the userspace. Currently bitdepth
* changes is only for HEVC and interlaced support is for all
* codecs except HEVC
* event data is now as follows:
* u32 *ptr = seq_changed_event.u.data;
* ptr[0] = height
* ptr[1] = width
* ptr[2] = bit depth
* ptr[3] = pic struct (progressive or interlaced)
* ptr[4] = colour space
* ptr[5] = crop_data(top)
* ptr[6] = crop_data(left)
* ptr[7] = crop_data(height)
* ptr[8] = crop_data(width)
* ptr[9] = profile
* ptr[10] = level
*/
inst->profile = event_notify->profile;
inst->level = event_notify->level;
inst->prop.crop_info.left =
event_notify->crop_data.left;
inst->prop.crop_info.top =
event_notify->crop_data.top;
inst->prop.crop_info.height =
event_notify->crop_data.height;
inst->prop.crop_info.width =
event_notify->crop_data.width;
/* HW returns progressive_only flag in pic_struct. */
inst->pic_struct =
event_notify->pic_struct ?
MSM_VIDC_PIC_STRUCT_PROGRESSIVE :
MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED;
inst->colour_space = event_notify->colour_space;
ptr = (u32 *)seq_changed_event.u.data;
ptr[0] = event_notify->height;
ptr[1] = event_notify->width;
ptr[2] = event_notify->bit_depth;
ptr[3] = event_notify->pic_struct;
ptr[4] = event_notify->colour_space;
ptr[5] = event_notify->crop_data.top;
ptr[6] = event_notify->crop_data.left;
ptr[7] = event_notify->crop_data.height;
ptr[8] = event_notify->crop_data.width;
codec = get_v4l2_codec(inst);
ptr[9] = msm_comm_get_v4l2_profile(codec,
event_notify->profile);
ptr[10] = msm_comm_get_v4l2_level(codec,
event_notify->level);
dprintk(VIDC_HIGH,
"Event payload: height = %u width = %u profile = %u level = %u\n",
event_notify->height, event_notify->width,
ptr[9], ptr[10]);
dprintk(VIDC_HIGH,
"Event payload: bit_depth = %u pic_struct = %u colour_space = %u\n",
event_notify->bit_depth, event_notify->pic_struct,
event_notify->colour_space);
dprintk(VIDC_HIGH,
"Event payload: CROP top = %u left = %u Height = %u Width = %u\n",
event_notify->crop_data.top,
event_notify->crop_data.left,
event_notify->crop_data.height,
event_notify->crop_data.width);
mutex_lock(&inst->lock);
inst->in_reconfig = true;
fmt = &inst->fmts[INPUT_PORT];
fmt->v4l2_fmt.fmt.pix_mp.height = event_notify->height;
fmt->v4l2_fmt.fmt.pix_mp.width = event_notify->width;
inst->bit_depth = event_notify->bit_depth;
fmt = &inst->fmts[OUTPUT_PORT];
fmt->v4l2_fmt.fmt.pix_mp.height = event_notify->height;
fmt->v4l2_fmt.fmt.pix_mp.width = event_notify->width;
extra_buff_count = msm_vidc_get_extra_buff_count(inst,
HAL_BUFFER_OUTPUT);
fmt->count_min = event_notify->capture_buf_count;
fmt->count_min_host = fmt->count_min + extra_buff_count;
dprintk(VIDC_HIGH, "%s: buffer[%d] count: min %d min_host %d\n",
__func__, HAL_BUFFER_OUTPUT, fmt->count_min,
fmt->count_min_host);
mutex_unlock(&inst->lock);
if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
dprintk(VIDC_HIGH, "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT\n");
}
rc = msm_vidc_check_session_supported(inst);
if (!rc) {
seq_changed_event.type = event;
v4l2_event_queue_fh(&inst->event_handler, &seq_changed_event);
} else if (rc == -ENOTSUPP) {
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED);
} else if (rc == -EBUSY) {
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_HW_OVERLOAD);
}
err_bad_event:
put_inst(inst);
}
static void handle_session_prop_info(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct getprop_buf *getprop;
struct msm_vidc_inst *inst;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for prop info\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
getprop = kzalloc(sizeof(*getprop), GFP_KERNEL);
if (!getprop) {
dprintk(VIDC_ERR, "%s: getprop kzalloc failed\n", __func__);
goto err_prop_info;
}
getprop->data = kmemdup((void *) (&response->data.property),
sizeof(union hal_get_property), GFP_KERNEL);
if (!getprop->data) {
dprintk(VIDC_ERR, "%s: kmemdup failed\n", __func__);
kfree(getprop);
goto err_prop_info;
}
mutex_lock(&inst->pending_getpropq.lock);
list_add_tail(&getprop->list, &inst->pending_getpropq.list);
mutex_unlock(&inst->pending_getpropq.lock);
signal_session_msg_receipt(cmd, inst);
err_prop_info:
put_inst(inst);
}
static void handle_load_resource_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for load resource\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
if (response->status) {
dprintk(VIDC_ERR,
"Load resource response from FW : %#x\n",
response->status);
msm_comm_generate_session_error(inst);
}
put_inst(inst);
}
static void handle_start_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (!response) {
dprintk(VIDC_ERR, "Failed to get valid response for start\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
signal_session_msg_receipt(cmd, inst);
put_inst(inst);
}
static void handle_stop_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (!response) {
dprintk(VIDC_ERR, "Failed to get valid response for stop\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
signal_session_msg_receipt(cmd, inst);
put_inst(inst);
}
static void handle_release_res_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for release resource\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
signal_session_msg_receipt(cmd, inst);
put_inst(inst);
}
void msm_comm_validate_output_buffers(struct msm_vidc_inst *inst)
{
struct internal_buf *binfo;
u32 buffers_owned_by_driver = 0;
struct msm_vidc_format *fmt;
fmt = &inst->fmts[OUTPUT_PORT];
mutex_lock(&inst->outputbufs.lock);
if (list_empty(&inst->outputbufs.list)) {
dprintk(VIDC_HIGH, "%s: no OUTPUT buffers allocated\n",
__func__);
mutex_unlock(&inst->outputbufs.lock);
return;
}
list_for_each_entry(binfo, &inst->outputbufs.list, list) {
if (binfo->buffer_ownership != DRIVER) {
dprintk(VIDC_HIGH,
"This buffer is with FW %x\n",
binfo->smem.device_addr);
continue;
}
buffers_owned_by_driver++;
}
mutex_unlock(&inst->outputbufs.lock);
/* Only minimum number of DPBs are allocated */
if (buffers_owned_by_driver != fmt->count_min) {
dprintk(VIDC_ERR,
"OUTPUT Buffer count mismatch %d of %d\n",
buffers_owned_by_driver,
fmt->count_min);
msm_vidc_handle_hw_error(inst->core);
}
}
int msm_comm_queue_dpb_only_buffers(struct msm_vidc_inst *inst)
{
struct internal_buf *binfo, *extra_info;
struct hfi_device *hdev;
struct vidc_frame_data frame_data = {0};
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
extra_info = inst->dpb_extra_binfo;
mutex_lock(&inst->outputbufs.lock);
list_for_each_entry(binfo, &inst->outputbufs.list, list) {
if (binfo->buffer_ownership != DRIVER)
continue;
if (binfo->mark_remove)
continue;
frame_data.alloc_len = binfo->smem.size;
frame_data.filled_len = 0;
frame_data.offset = 0;
frame_data.device_addr = binfo->smem.device_addr;
frame_data.flags = 0;
frame_data.extradata_addr =
extra_info ? extra_info->smem.device_addr : 0;
frame_data.buffer_type = HAL_BUFFER_OUTPUT;
frame_data.extradata_size =
extra_info ? extra_info->smem.size : 0;
rc = call_hfi_op(hdev, session_ftb,
(void *) inst->session, &frame_data);
binfo->buffer_ownership = FIRMWARE;
}
mutex_unlock(&inst->outputbufs.lock);
return rc;
}
static void handle_session_flush(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
struct v4l2_event flush_event = {0};
u32 *ptr = NULL;
enum hal_flush flush_type;
int rc;
if (!response) {
dprintk(VIDC_ERR, "Failed to get valid response for flush\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
mutex_lock(&inst->flush_lock);
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY) {
if (!(get_v4l2_codec(inst) == V4L2_PIX_FMT_VP9 &&
inst->in_reconfig))
msm_comm_validate_output_buffers(inst);
if (!inst->in_reconfig) {
rc = msm_comm_queue_dpb_only_buffers(inst);
if (rc) {
dprintk(VIDC_ERR,
"Failed to queue output buffers: %d\n",
rc);
}
}
}
inst->in_flush = false;
flush_event.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
ptr = (u32 *)flush_event.u.data;
flush_type = response->data.flush_type;
switch (flush_type) {
case HAL_FLUSH_INPUT:
ptr[0] = V4L2_CMD_FLUSH_OUTPUT;
break;
case HAL_FLUSH_OUTPUT:
ptr[0] = V4L2_CMD_FLUSH_CAPTURE;
break;
case HAL_FLUSH_ALL:
ptr[0] |= V4L2_CMD_FLUSH_CAPTURE;
ptr[0] |= V4L2_CMD_FLUSH_OUTPUT;
break;
default:
dprintk(VIDC_ERR, "Invalid flush type received!");
goto exit;
}
dprintk(VIDC_HIGH,
"Notify flush complete, flush_type: %x\n", flush_type);
v4l2_event_queue_fh(&inst->event_handler, &flush_event);
exit:
mutex_unlock(&inst->flush_lock);
put_inst(inst);
}
static void handle_session_error(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct hfi_device *hdev = NULL;
struct msm_vidc_inst *inst = NULL;
int event = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for session error\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
hdev = inst->core->device;
dprintk(VIDC_ERR, "Session error received for inst %pK session %x\n",
inst, hash32_ptr(inst->session));
if (response->status == VIDC_ERR_MAX_CLIENTS) {
dprintk(VIDC_ERR, "Too many clients, rejecting %pK", inst);
event = V4L2_EVENT_MSM_VIDC_MAX_CLIENTS;
/*
* Clean the HFI session now. Since inst->state is moved to
* INVALID, forward thread doesn't know FW has valid session
* or not. This is the last place driver knows that there is
* no session in FW. Hence clean HFI session now.
*/
msm_comm_session_clean(inst);
} else if (response->status == VIDC_ERR_NOT_SUPPORTED) {
dprintk(VIDC_ERR, "Unsupported bitstream in %pK", inst);
event = V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED;
} else {
dprintk(VIDC_ERR, "Unknown session error (%d) for %pK\n",
response->status, inst);
event = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
}
/* change state before sending error to client */
change_inst_state(inst, MSM_VIDC_CORE_INVALID);
msm_vidc_queue_v4l2_event(inst, event);
put_inst(inst);
}
static void msm_comm_clean_notify_client(struct msm_vidc_core *core)
{
struct msm_vidc_inst *inst = NULL;
if (!core) {
dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
return;
}
dprintk(VIDC_ERR, "%s: Core %pK\n", __func__, core);
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
mutex_lock(&inst->lock);
inst->state = MSM_VIDC_CORE_INVALID;
mutex_unlock(&inst->lock);
dprintk(VIDC_ERR,
"%s Send sys error for inst %pK\n", __func__, inst);
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_SYS_ERROR);
}
mutex_unlock(&core->lock);
}
static void handle_sys_error(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_core *core = NULL;
struct hfi_device *hdev = NULL;
struct msm_vidc_inst *inst = NULL;
int rc = 0;
subsystem_crashed("venus");
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for sys error\n");
return;
}
core = get_vidc_core(response->device_id);
if (!core) {
dprintk(VIDC_ERR,
"Got SYS_ERR but unable to identify core\n");
return;
}
hdev = core->device;
mutex_lock(&core->lock);
if (core->state == VIDC_CORE_UNINIT) {
dprintk(VIDC_ERR,
"%s: Core %pK already moved to state %d\n",
__func__, core, core->state);
mutex_unlock(&core->lock);
return;
}
dprintk(VIDC_ERR, "SYS_ERROR received for core %pK\n", core);
msm_vidc_noc_error_info(core);
call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
list_for_each_entry(inst, &core->instances, list) {
dprintk(VIDC_ERR,
"%s: Send sys error for inst %pK\n", __func__, inst);
change_inst_state(inst, MSM_VIDC_CORE_INVALID);
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
if (!core->trigger_ssr)
msm_comm_print_inst_info(inst);
}
/* handle the hw error before core released to get full debug info */
msm_vidc_handle_hw_error(core);
if (response->status == VIDC_ERR_NOC_ERROR) {
dprintk(VIDC_ERR, "Got NOC error");
MSM_VIDC_ERROR(true);
}
dprintk(VIDC_ERR, "Calling core_release\n");
rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "core_release failed\n");
mutex_unlock(&core->lock);
return;
}
core->state = VIDC_CORE_UNINIT;
mutex_unlock(&core->lock);
dprintk(VIDC_ERR, "SYS_ERROR handled.\n");
}
void msm_comm_session_clean(struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev = NULL;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return;
}
if (!inst->session) {
dprintk(VIDC_HIGH, "%s: inst %pK session already cleaned\n",
__func__, inst);
return;
}
hdev = inst->core->device;
mutex_lock(&inst->lock);
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_clean,
(void *)inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Session clean failed :%pK\n", inst);
}
inst->session = NULL;
mutex_unlock(&inst->lock);
}
static void handle_session_close(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
if (!response) {
dprintk(VIDC_ERR,
"Failed to get valid response for session close\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
signal_session_msg_receipt(cmd, inst);
show_stats(inst);
put_inst(inst);
}
struct vb2_buffer *msm_comm_get_vb_using_vidc_buffer(
struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf)
{
u32 port = 0;
struct vb2_buffer *vb = NULL;
struct vb2_queue *q = NULL;
bool found = false;
if (mbuf->vvb.vb2_buf.type == OUTPUT_MPLANE) {
port = OUTPUT_PORT;
} else if (mbuf->vvb.vb2_buf.type == INPUT_MPLANE) {
port = INPUT_PORT;
} else {
dprintk(VIDC_ERR, "%s: invalid type %d\n",
__func__, mbuf->vvb.vb2_buf.type);
return NULL;
}
mutex_lock(&inst->bufq[port].lock);
found = false;
q = &inst->bufq[port].vb2_bufq;
if (!q->streaming) {
dprintk(VIDC_ERR, "port %d is not streaming", port);
goto unlock;
}
list_for_each_entry(vb, &q->queued_list, queued_entry) {
if (vb->state != VB2_BUF_STATE_ACTIVE)
continue;
if (msm_comm_compare_vb2_planes(inst, mbuf, vb)) {
found = true;
break;
}
}
unlock:
mutex_unlock(&inst->bufq[port].lock);
if (!found) {
print_vidc_buffer(VIDC_ERR, "vb2 not found for", inst, mbuf);
return NULL;
}
return vb;
}
int msm_comm_vb2_buffer_done(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
struct vb2_buffer *vb2;
struct vb2_v4l2_buffer *vbuf;
u32 i, port;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, inst, mbuf);
return -EINVAL;
}
if (mbuf->vvb.vb2_buf.type == OUTPUT_MPLANE)
port = OUTPUT_PORT;
else if (mbuf->vvb.vb2_buf.type == INPUT_MPLANE)
port = INPUT_PORT;
else
return -EINVAL;
vb2 = msm_comm_get_vb_using_vidc_buffer(inst, mbuf);
if (!vb2)
return -EINVAL;
/*
* access vb2 buffer under q->lock and if streaming only to
* ensure the buffer was not free'd by vb2 framework while
* we are accessing it here.
*/
mutex_lock(&inst->bufq[port].lock);
if (inst->bufq[port].vb2_bufq.streaming) {
vbuf = to_vb2_v4l2_buffer(vb2);
vbuf->flags = mbuf->vvb.flags;
vb2->timestamp = mbuf->vvb.vb2_buf.timestamp;
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) {
vb2->planes[i].bytesused =
mbuf->vvb.vb2_buf.planes[i].bytesused;
vb2->planes[i].data_offset =
mbuf->vvb.vb2_buf.planes[i].data_offset;
}
vb2_buffer_done(vb2, VB2_BUF_STATE_DONE);
} else {
dprintk(VIDC_ERR, "%s: port %d is not streaming\n",
__func__, port);
}
mutex_unlock(&inst->bufq[port].lock);
return 0;
}
bool heic_encode_session_supported(struct msm_vidc_inst *inst)
{
u32 slice_mode;
u32 idr_period = IDR_PERIOD;
u32 n_bframes;
u32 n_pframes;
struct v4l2_format *f;
slice_mode = msm_comm_g_ctrl_for_id(inst,
V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE);
n_bframes = msm_comm_g_ctrl_for_id(inst,
V4L2_CID_MPEG_VIDEO_B_FRAMES);
n_pframes = msm_comm_g_ctrl_for_id(inst,
V4L2_CID_MPEG_VIDEO_GOP_SIZE);
/*
* HEIC Encode is supported for Constant Quality RC mode only.
* All configurations below except grid_enable are required for any
* HEIC session including FWK tiled HEIC encode.
* grid_enable flag along with dimension check enables HW tiling.
*/
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
if (inst->session_type == MSM_VIDC_ENCODER &&
get_hal_codec(f->fmt.pix_mp.pixelformat) ==
HAL_VIDEO_CODEC_HEVC &&
inst->frame_quality >= MIN_FRAME_QUALITY &&
inst->frame_quality <= MAX_FRAME_QUALITY &&
slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE &&
idr_period == 1 &&
n_bframes == 0 &&
n_pframes == 0) {
if (inst->grid_enable > 0) {
if (f->fmt.pix_mp.width < HEIC_GRID_DIMENSION ||
f->fmt.pix_mp.height < HEIC_GRID_DIMENSION)
return false;
}
return true;
} else {
return false;
}
}
static bool is_eos_buffer(struct msm_vidc_inst *inst, u32 device_addr)
{
struct eos_buf *temp, *next;
bool found = false;
mutex_lock(&inst->eosbufs.lock);
list_for_each_entry_safe(temp, next, &inst->eosbufs.list, list) {
if (temp->smem.device_addr == device_addr) {
found = true;
list_del(&temp->list);
msm_comm_smem_free(inst, &temp->smem);
kfree(temp);
break;
}
}
mutex_unlock(&inst->eosbufs.lock);
return found;
}
static void handle_ebd(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
struct msm_vidc_buffer *mbuf;
struct vb2_buffer *vb;
struct msm_vidc_inst *inst;
struct vidc_hal_ebd *empty_buf_done;
u32 planes[VIDEO_MAX_PLANES] = {0};
struct v4l2_format *f;
if (!response) {
dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
/* If this is internal EOS buffer, handle it in driver */
if (is_eos_buffer(inst, empty_buf_done->packet_buffer)) {
dprintk(VIDC_HIGH, "Received EOS buffer 0x%x\n",
empty_buf_done->packet_buffer);
goto exit;
}
planes[0] = empty_buf_done->packet_buffer;
planes[1] = empty_buf_done->extra_data_buffer;
mbuf = msm_comm_get_buffer_using_device_planes(inst,
INPUT_MPLANE, planes);
if (!mbuf || !kref_get_mbuf(inst, mbuf)) {
dprintk(VIDC_ERR,
"%s: data_addr %x, extradata_addr %x not found\n",
__func__, planes[0], planes[1]);
goto exit;
}
mbuf->flags &= ~MSM_VIDC_FLAG_QUEUED;
vb = &mbuf->vvb.vb2_buf;
vb->planes[0].bytesused = response->input_done.filled_len;
if (vb->planes[0].bytesused > vb->planes[0].length)
dprintk(VIDC_LOW, "bytesused overflow length\n");
vb->planes[0].data_offset = response->input_done.offset;
if (vb->planes[0].data_offset > vb->planes[0].length)
dprintk(VIDC_LOW, "data_offset overflow length\n");
if (empty_buf_done->status == VIDC_ERR_NOT_SUPPORTED) {
dprintk(VIDC_LOW, "Failed : Unsupported input stream\n");
mbuf->vvb.flags |= V4L2_BUF_INPUT_UNSUPPORTED;
}
if (empty_buf_done->status == VIDC_ERR_BITSTREAM_ERR) {
dprintk(VIDC_LOW, "Failed : Corrupted input stream\n");
mbuf->vvb.flags |= V4L2_BUF_FLAG_DATA_CORRUPT;
}
if (empty_buf_done->flags & HAL_BUFFERFLAG_SYNCFRAME)
mbuf->vvb.flags |= V4L2_BUF_FLAG_KEYFRAME;
f = &inst->fmts[INPUT_PORT].v4l2_fmt;
if (f->fmt.pix_mp.num_planes > 1)
vb->planes[1].bytesused = vb->planes[1].length;
update_recon_stats(inst, &empty_buf_done->recon_stats);
msm_vidc_clear_freq_entry(inst, mbuf->smem[0].device_addr);
/*
* dma cache operations need to be performed before dma_unmap
* which is done inside msm_comm_put_vidc_buffer()
*/
msm_comm_dqbuf_cache_operations(inst, mbuf);
/*
* put_buffer should be done before vb2_buffer_done else
* client might queue the same buffer before it is unmapped
* in put_buffer.
*/
msm_comm_put_vidc_buffer(inst, mbuf);
msm_comm_vb2_buffer_done(inst, mbuf);
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
kref_put_mbuf(mbuf);
exit:
put_inst(inst);
}
static int handle_multi_stream_buffers(struct msm_vidc_inst *inst,
u32 dev_addr)
{
struct internal_buf *binfo;
struct msm_smem *smem;
bool found = false;
mutex_lock(&inst->outputbufs.lock);
list_for_each_entry(binfo, &inst->outputbufs.list, list) {
smem = &binfo->smem;
if (smem && dev_addr == smem->device_addr) {
if (binfo->buffer_ownership == DRIVER) {
dprintk(VIDC_ERR,
"FW returned same buffer: %x\n",
dev_addr);
break;
}
binfo->buffer_ownership = DRIVER;
found = true;
break;
}
}
mutex_unlock(&inst->outputbufs.lock);
if (!found) {
dprintk(VIDC_ERR,
"Failed to find output buffer in queued list: %x\n",
dev_addr);
}
return 0;
}
enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst)
{
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY)
return HAL_BUFFER_OUTPUT2;
else
return HAL_BUFFER_OUTPUT;
}
static void handle_fbd(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_data_done *response = data;
struct msm_vidc_buffer *mbuf;
struct msm_vidc_inst *inst;
struct vb2_buffer *vb;
struct vidc_hal_fbd *fill_buf_done;
enum hal_buffer buffer_type;
u64 time_usec = 0;
u32 planes[VIDEO_MAX_PLANES] = {0};
struct v4l2_format *f;
if (!response) {
dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
return;
}
inst = get_inst(get_vidc_core(response->device_id),
response->session_id);
if (!inst) {
dprintk(VIDC_ERR, "Got a response for an inactive session\n");
return;
}
fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
planes[0] = fill_buf_done->packet_buffer1;
planes[1] = fill_buf_done->extra_data_buffer;
buffer_type = msm_comm_get_hal_output_buffer(inst);
if (fill_buf_done->buffer_type == buffer_type) {
mbuf = msm_comm_get_buffer_using_device_planes(inst,
OUTPUT_MPLANE, planes);
if (!mbuf || !kref_get_mbuf(inst, mbuf)) {
dprintk(VIDC_ERR,
"%s: data_addr %x, extradata_addr %x not found\n",
__func__, planes[0], planes[1]);
goto exit;
}
} else {
if (handle_multi_stream_buffers(inst,
fill_buf_done->packet_buffer1))
dprintk(VIDC_ERR,
"Failed : Output buffer not found %pa\n",
&fill_buf_done->packet_buffer1);
goto exit;
}
mbuf->flags &= ~MSM_VIDC_FLAG_QUEUED;
vb = &mbuf->vvb.vb2_buf;
if (fill_buf_done->buffer_type == HAL_BUFFER_OUTPUT2 &&
fill_buf_done->flags1 & HAL_BUFFERFLAG_READONLY) {
dprintk(VIDC_ERR,
"%s: Read only buffer not allowed for OPB\n", __func__);
goto exit;
}
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME)
fill_buf_done->filled_len1 = 0;
vb->planes[0].bytesused = fill_buf_done->filled_len1;
if (vb->planes[0].bytesused > vb->planes[0].length)
dprintk(VIDC_LOW,
"fbd:Overflow bytesused = %d; length = %d\n",
vb->planes[0].bytesused,
vb->planes[0].length);
vb->planes[0].data_offset = fill_buf_done->offset1;
if (vb->planes[0].data_offset > vb->planes[0].length)
dprintk(VIDC_LOW,
"fbd:Overflow data_offset = %d; length = %d\n",
vb->planes[0].data_offset,
vb->planes[0].length);
time_usec = fill_buf_done->timestamp_hi;
time_usec = (time_usec << 32) | fill_buf_done->timestamp_lo;
vb->timestamp = (time_usec * NSEC_PER_USEC);
if (inst->session_type == MSM_VIDC_DECODER) {
msm_comm_store_mark_data(&inst->fbd_data, vb->index,
fill_buf_done->mark_data, fill_buf_done->mark_target);
}
if (inst->session_type == MSM_VIDC_ENCODER) {
msm_comm_store_filled_length(&inst->fbd_data, vb->index,
fill_buf_done->filled_len1);
}
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
if (f->fmt.pix_mp.num_planes > 1)
vb->planes[1].bytesused = vb->planes[1].length;
mbuf->vvb.flags = 0;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_READONLY)
mbuf->vvb.flags |= V4L2_BUF_FLAG_READONLY;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS)
mbuf->vvb.flags |= V4L2_BUF_FLAG_EOS;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG)
mbuf->vvb.flags |= V4L2_BUF_FLAG_CODECCONFIG;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME)
mbuf->vvb.flags |= V4L2_BUF_FLAG_KEYFRAME;
if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT)
mbuf->vvb.flags |= V4L2_BUF_FLAG_DATA_CORRUPT;
switch (fill_buf_done->picture_type) {
case HFI_PICTURE_TYPE_P:
mbuf->vvb.flags |= V4L2_BUF_FLAG_PFRAME;
break;
case HFI_PICTURE_TYPE_B:
mbuf->vvb.flags |= V4L2_BUF_FLAG_BFRAME;
break;
case HFI_FRAME_NOTCODED:
case HFI_UNUSED_PICT:
/* Do we need to care about these? */
case HFI_FRAME_YUV:
break;
default:
break;
}
/*
* dma cache operations need to be performed before dma_unmap
* which is done inside msm_comm_put_vidc_buffer()
*/
msm_comm_dqbuf_cache_operations(inst, mbuf);
/*
* put_buffer should be done before vb2_buffer_done else
* client might queue the same buffer before it is unmapped
* in put_buffer.
*/
msm_comm_put_vidc_buffer(inst, mbuf);
msm_comm_vb2_buffer_done(inst, mbuf);
msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
kref_put_mbuf(mbuf);
exit:
put_inst(inst);
}
void handle_cmd_response(enum hal_command_response cmd, void *data)
{
dprintk(VIDC_LOW, "Command response = %d\n", cmd);
switch (cmd) {
case HAL_SYS_INIT_DONE:
handle_sys_init_done(cmd, data);
break;
case HAL_SYS_RELEASE_RESOURCE_DONE:
handle_sys_release_res_done(cmd, data);
break;
case HAL_SESSION_INIT_DONE:
handle_session_init_done(cmd, data);
break;
case HAL_SESSION_PROPERTY_INFO:
handle_session_prop_info(cmd, data);
break;
case HAL_SESSION_LOAD_RESOURCE_DONE:
handle_load_resource_done(cmd, data);
break;
case HAL_SESSION_START_DONE:
handle_start_done(cmd, data);
break;
case HAL_SESSION_ETB_DONE:
handle_ebd(cmd, data);
break;
case HAL_SESSION_FTB_DONE:
handle_fbd(cmd, data);
break;
case HAL_SESSION_STOP_DONE:
handle_stop_done(cmd, data);
break;
case HAL_SESSION_RELEASE_RESOURCE_DONE:
handle_release_res_done(cmd, data);
break;
case HAL_SESSION_END_DONE:
case HAL_SESSION_ABORT_DONE:
handle_session_close(cmd, data);
break;
case HAL_SESSION_EVENT_CHANGE:
handle_event_change(cmd, data);
break;
case HAL_SESSION_FLUSH_DONE:
handle_session_flush(cmd, data);
break;
case HAL_SYS_WATCHDOG_TIMEOUT:
case HAL_SYS_ERROR:
handle_sys_error(cmd, data);
break;
case HAL_SESSION_ERROR:
handle_session_error(cmd, data);
break;
case HAL_SESSION_RELEASE_BUFFER_DONE:
handle_session_release_buf_done(cmd, data);
break;
case HAL_SESSION_REGISTER_BUFFER_DONE:
handle_session_register_buffer_done(cmd, data);
break;
case HAL_SESSION_UNREGISTER_BUFFER_DONE:
handle_session_unregister_buffer_done(cmd, data);
break;
default:
dprintk(VIDC_LOW, "response unhandled: %d\n", cmd);
break;
}
}
static inline enum msm_vidc_thermal_level msm_comm_vidc_thermal_level(int level)
{
switch (level) {
case 0:
return VIDC_THERMAL_NORMAL;
case 1:
return VIDC_THERMAL_LOW;
case 2:
return VIDC_THERMAL_HIGH;
default:
return VIDC_THERMAL_CRITICAL;
}
}
static bool is_core_turbo(struct msm_vidc_core *core, unsigned long freq)
{
unsigned int i = 0;
struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
u32 max_freq = 0;
allowed_clks_tbl = core->resources.allowed_clks_tbl;
for (i = 0; i < core->resources.allowed_clks_tbl_size; i++) {
if (max_freq < allowed_clks_tbl[i].clock_rate)
max_freq = allowed_clks_tbl[i].clock_rate;
}
return freq >= max_freq;
}
static bool is_thermal_permissible(struct msm_vidc_core *core)
{
enum msm_vidc_thermal_level tl;
unsigned long freq = 0;
bool is_turbo = false;
if (!core->resources.thermal_mitigable)
return true;
if (msm_vidc_thermal_mitigation_disabled) {
dprintk(VIDC_HIGH,
"Thermal mitigation not enabled. debugfs %d\n",
msm_vidc_thermal_mitigation_disabled);
return true;
}
tl = msm_comm_vidc_thermal_level(vidc_driver->thermal_level);
freq = core->curr_freq;
is_turbo = is_core_turbo(core, freq);
dprintk(VIDC_HIGH,
"Core freq %ld Thermal level %d Turbo mode %d\n",
freq, tl, is_turbo);
if (is_turbo && tl >= VIDC_THERMAL_LOW) {
dprintk(VIDC_ERR,
"Video session not allowed. Turbo mode %d Thermal level %d\n",
is_turbo, tl);
return false;
}
return true;
}
bool is_batching_allowed(struct msm_vidc_inst *inst)
{
u32 op_pixelformat, fps, maxmbs, maxfps;
if (!inst || !inst->core)
return false;
/* Enable decode batching based on below conditions */
op_pixelformat =
inst->fmts[OUTPUT_PORT].v4l2_fmt.fmt.pix_mp.pixelformat;
fps = inst->clk_data.frame_rate >> 16;
maxmbs = inst->capability.cap[CAP_BATCH_MAX_MB_PER_FRAME].max;
maxfps = inst->capability.cap[CAP_BATCH_MAX_FPS].max;
return (inst->decode_batching &&
is_decode_session(inst) &&
!is_thumbnail_session(inst) &&
!inst->clk_data.low_latency_mode &&
(op_pixelformat == V4L2_PIX_FMT_NV12_UBWC ||
op_pixelformat == V4L2_PIX_FMT_NV12_TP10_UBWC) &&
fps <= maxfps &&
msm_vidc_get_mbs_per_frame(inst) <= maxmbs);
}
static int msm_comm_session_abort(struct msm_vidc_inst *inst)
{
int rc = 0, abort_completion = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE);
dprintk(VIDC_ERR, "%s: inst %pK session %x\n", __func__,
inst, hash32_ptr(inst->session));
rc = call_hfi_op(hdev, session_abort, (void *)inst->session);
if (rc) {
dprintk(VIDC_ERR,
"%s session_abort failed rc: %d\n", __func__, rc);
goto exit;
}
rc = wait_for_completion_timeout(
&inst->completions[abort_completion],
msecs_to_jiffies(
inst->core->resources.msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: inst %pK session %x abort timed out\n",
__func__, inst, hash32_ptr(inst->session));
msm_comm_generate_sys_error(inst);
rc = -EBUSY;
} else {
rc = 0;
}
exit:
return rc;
}
static void handle_thermal_event(struct msm_vidc_core *core)
{
int rc = 0;
struct msm_vidc_inst *inst;
if (!core || !core->device) {
dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
return;
}
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
if (!inst->session)
continue;
mutex_unlock(&core->lock);
if (inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_CLOSE_DONE) {
dprintk(VIDC_ERR, "%s: abort inst %pK\n",
__func__, inst);
rc = msm_comm_session_abort(inst);
if (rc) {
dprintk(VIDC_ERR,
"%s session_abort failed rc: %d\n",
__func__, rc);
goto err_sess_abort;
}
change_inst_state(inst, MSM_VIDC_CORE_INVALID);
dprintk(VIDC_ERR,
"%s Send sys error for inst %pK\n",
__func__, inst);
msm_vidc_queue_v4l2_event(inst,
V4L2_EVENT_MSM_VIDC_SYS_ERROR);
} else {
msm_comm_generate_session_error(inst);
}
mutex_lock(&core->lock);
}
mutex_unlock(&core->lock);
return;
err_sess_abort:
msm_comm_clean_notify_client(core);
}
void msm_comm_handle_thermal_event(void)
{
struct msm_vidc_core *core;
list_for_each_entry(core, &vidc_driver->cores, list) {
if (!is_thermal_permissible(core)) {
dprintk(VIDC_ERR,
"Thermal level critical, stop all active sessions!\n");
handle_thermal_event(core);
}
}
}
int msm_comm_check_core_init(struct msm_vidc_core *core)
{
int rc = 0;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT_DONE) {
dprintk(VIDC_HIGH, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto exit;
}
dprintk(VIDC_HIGH, "Waiting for SYS_INIT_DONE\n");
rc = wait_for_completion_timeout(
&core->completions[SYS_MSG_INDEX(HAL_SYS_INIT_DONE)],
msecs_to_jiffies(core->resources.msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: Wait interrupted or timed out: %d\n",
__func__, SYS_MSG_INDEX(HAL_SYS_INIT_DONE));
rc = -EIO;
goto exit;
} else {
core->state = VIDC_CORE_INIT_DONE;
rc = 0;
}
dprintk(VIDC_HIGH, "SYS_INIT_DONE!!!\n");
exit:
mutex_unlock(&core->lock);
return rc;
}
static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
{
int rc = 0;
rc = msm_comm_check_core_init(inst->core);
if (rc) {
dprintk(VIDC_ERR, "%s - failed to initialize core\n", __func__);
msm_comm_generate_sys_error(inst);
return rc;
}
change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE);
return rc;
}
static int msm_comm_init_core(struct msm_vidc_inst *inst)
{
int rc, i;
struct hfi_device *hdev;
struct msm_vidc_core *core;
if (!inst || !inst->core || !inst->core->device)
return -EINVAL;
core = inst->core;
hdev = core->device;
mutex_lock(&core->lock);
if (core->state >= VIDC_CORE_INIT) {
dprintk(VIDC_HIGH, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_inited;
}
dprintk(VIDC_HIGH, "%s: core %pK\n", __func__, core);
rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR, "Failed to init core, id = %d\n",
core->id);
goto fail_core_init;
}
/* initialize core while firmware processing SYS_INIT cmd */
core->state = VIDC_CORE_INIT;
core->smmu_fault_handled = false;
core->trigger_ssr = false;
core->resources.max_inst_count = MAX_SUPPORTED_INSTANCES;
core->resources.max_secure_inst_count =
core->resources.max_secure_inst_count ?
core->resources.max_secure_inst_count :
core->resources.max_inst_count;
dprintk(VIDC_HIGH, "%s: codecs count %d, max inst count %d\n",
__func__, core->resources.codecs_count,
core->resources.max_inst_count);
if (!core->resources.codecs || !core->resources.codecs_count) {
dprintk(VIDC_ERR, "%s: invalid codecs\n", __func__);
rc = -EINVAL;
goto fail_core_init;
}
if (!core->capabilities) {
core->capabilities = kcalloc(core->resources.codecs_count,
sizeof(struct msm_vidc_capability), GFP_KERNEL);
if (!core->capabilities) {
dprintk(VIDC_ERR,
"%s: failed to allocate capabilities\n",
__func__);
rc = -ENOMEM;
goto fail_core_init;
}
} else {
dprintk(VIDC_ERR,
"%s: capabilities memory is expected to be freed\n",
__func__);
}
for (i = 0; i < core->resources.codecs_count; i++) {
core->capabilities[i].domain =
core->resources.codecs[i].domain;
core->capabilities[i].codec =
core->resources.codecs[i].codec;
}
rc = msm_vidc_capabilities(core);
if (rc) {
dprintk(VIDC_ERR,
"%s: default capabilities failed\n", __func__);
kfree(core->capabilities);
core->capabilities = NULL;
goto fail_core_init;
}
dprintk(VIDC_HIGH, "%s: done\n", __func__);
core_already_inited:
change_inst_state(inst, MSM_VIDC_CORE_INIT);
mutex_unlock(&core->lock);
rc = msm_comm_scale_clocks_and_bus(inst);
return rc;
fail_core_init:
core->state = VIDC_CORE_UNINIT;
mutex_unlock(&core->lock);
return rc;
}
static int msm_vidc_deinit_core(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
core = inst->core;
hdev = core->device;
mutex_lock(&core->lock);
if (core->state == VIDC_CORE_UNINIT) {
dprintk(VIDC_HIGH, "Video core: %d is already in state: %d\n",
core->id, core->state);
goto core_already_uninited;
}
mutex_unlock(&core->lock);
msm_comm_scale_clocks_and_bus(inst);
mutex_lock(&core->lock);
if (!core->resources.never_unload_fw) {
cancel_delayed_work(&core->fw_unload_work);
/*
* Delay unloading of firmware. This is useful
* in avoiding firmware download delays in cases where we
* will have a burst of back to back video playback sessions
* e.g. thumbnail generation.
*/
schedule_delayed_work(&core->fw_unload_work,
msecs_to_jiffies(core->state == VIDC_CORE_INIT_DONE ?
core->resources.msm_vidc_firmware_unload_delay : 0));
dprintk(VIDC_HIGH, "firmware unload delayed by %u ms\n",
core->state == VIDC_CORE_INIT_DONE ?
core->resources.msm_vidc_firmware_unload_delay : 0);
}
core_already_uninited:
change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
mutex_unlock(&core->lock);
return 0;
}
int msm_comm_force_cleanup(struct msm_vidc_inst *inst)
{
msm_comm_kill_session(inst);
return msm_vidc_deinit_core(inst);
}
static int msm_comm_session_init_done(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc;
dprintk(VIDC_HIGH, "inst %pK: waiting for session init done\n", inst);
rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
HAL_SESSION_INIT_DONE);
if (rc) {
dprintk(VIDC_ERR, "Session init failed for inst %pK\n", inst);
msm_comm_generate_sys_error(inst);
return rc;
}
return rc;
}
static int msm_comm_session_init(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc = 0;
int fourcc = 0;
struct hfi_device *hdev;
struct v4l2_format *f;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_OPEN)) {
dprintk(VIDC_HIGH, "inst: %pK is already in state: %d\n",
inst, inst->state);
goto exit;
}
if (inst->session_type == MSM_VIDC_DECODER) {
f = &inst->fmts[INPUT_PORT].v4l2_fmt;
fourcc = f->fmt.pix_mp.pixelformat;
} else if (inst->session_type == MSM_VIDC_ENCODER) {
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
fourcc = f->fmt.pix_mp.pixelformat;
} else if (inst->session_type == MSM_VIDC_CVP) {
fourcc = V4L2_PIX_FMT_CVP;
} else {
dprintk(VIDC_ERR, "Invalid session\n");
return -EINVAL;
}
rc = msm_comm_init_clocks_and_bus_data(inst);
if (rc) {
dprintk(VIDC_ERR, "Failed to initialize clocks and bus data\n");
goto exit;
}
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_init, hdev->hfi_device_data,
inst, get_hal_domain(inst->session_type),
get_hal_codec(fourcc),
&inst->session);
if (rc || !inst->session) {
dprintk(VIDC_ERR,
"Failed to call session init for: %pK, %pK, %d, %d\n",
inst->core->device, inst,
inst->session_type, fourcc);
rc = -EINVAL;
goto exit;
}
rc = msm_vidc_init_buffer_count(inst);
if (rc) {
dprintk(VIDC_ERR, "Failed to initialize buff counts\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_OPEN);
exit:
return rc;
}
static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
{
struct msm_vidc_inst *temp;
int op_rate = 0;
struct v4l2_format *out_f;
struct v4l2_format *inp_f;
dprintk(VIDC_ERR, "Running instances:\n");
dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%4s|%4s\n",
"type", "w", "h", "fps", "opr", "prop");
mutex_lock(&core->lock);
list_for_each_entry(temp, &core->instances, list) {
out_f = &temp->fmts[OUTPUT_PORT].v4l2_fmt;
inp_f = &temp->fmts[INPUT_PORT].v4l2_fmt;
if (temp->state >= MSM_VIDC_OPEN_DONE &&
temp->state < MSM_VIDC_STOP_DONE) {
char properties[4] = "";
if (is_thumbnail_session(temp))
strlcat(properties, "N", sizeof(properties));
if (is_turbo_session(temp))
strlcat(properties, "T", sizeof(properties));
if (is_realtime_session(temp))
strlcat(properties, "R", sizeof(properties));
if (temp->clk_data.operating_rate)
op_rate = temp->clk_data.operating_rate >> 16;
else
op_rate = temp->clk_data.frame_rate >> 16;
dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4d|%4s\n",
temp->session_type,
max(out_f->fmt.pix_mp.width,
inp_f->fmt.pix_mp.width),
max(out_f->fmt.pix_mp.height,
inp_f->fmt.pix_mp.height),
temp->clk_data.frame_rate >> 16,
op_rate, properties);
}
}
mutex_unlock(&core->lock);
}
static int msm_vidc_load_resources(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
int num_mbs_per_sec = 0, max_load_adj = 0;
struct msm_vidc_core *core;
enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD |
LOAD_CALC_IGNORE_THUMBNAIL_LOAD |
LOAD_CALC_IGNORE_NON_REALTIME_LOAD;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"%s: inst %pK is in invalid state\n", __func__, inst);
return -EINVAL;
}
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
dprintk(VIDC_HIGH, "inst: %pK is already in state: %d\n",
inst, inst->state);
goto exit;
}
core = inst->core;
num_mbs_per_sec =
msm_comm_get_load(core, MSM_VIDC_DECODER, quirks) +
msm_comm_get_load(core, MSM_VIDC_ENCODER, quirks);
max_load_adj = core->resources.max_load +
inst->capability.cap[CAP_MBS_PER_FRAME].max;
if (num_mbs_per_sec > max_load_adj) {
dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
num_mbs_per_sec, max_load_adj);
msm_vidc_print_running_insts(core);
msm_comm_kill_session(inst);
return -EBUSY;
}
hdev = core->device;
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_load_res, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send load resources\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_LOAD_RESOURCES);
exit:
return rc;
}
static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"%s: inst %pK is in invalid\n", __func__, inst);
return -EINVAL;
}
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) {
dprintk(VIDC_HIGH,
"inst: %pK is already in state: %d\n",
inst, inst->state);
goto exit;
}
hdev = inst->core->device;
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_start, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send start\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_START);
exit:
return rc;
}
static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"%s: inst %pK is in invalid state\n", __func__, inst);
return -EINVAL;
}
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) {
dprintk(VIDC_HIGH,
"inst: %pK is already in state: %d\n",
inst, inst->state);
goto exit;
}
hdev = inst->core->device;
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_stop, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR, "%s: inst %pK session_stop failed\n",
__func__, inst);
goto exit;
}
change_inst_state(inst, MSM_VIDC_STOP);
exit:
return rc;
}
static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"%s: inst %pK is in invalid state\n", __func__, inst);
return -EINVAL;
}
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) {
dprintk(VIDC_HIGH,
"inst: %pK is already in state: %d\n",
inst, inst->state);
goto exit;
}
hdev = inst->core->device;
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_release_res, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send release resources\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
exit:
return rc;
}
static int msm_comm_session_close(int flipped_state,
struct msm_vidc_inst *inst)
{
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return -EINVAL;
}
if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) {
dprintk(VIDC_HIGH,
"inst: %pK is already in state: %d\n",
inst, inst->state);
goto exit;
}
hdev = inst->core->device;
dprintk(VIDC_HIGH, "%s: inst %pK\n", __func__, inst);
rc = call_hfi_op(hdev, session_end, (void *) inst->session);
if (rc) {
dprintk(VIDC_ERR,
"Failed to send close\n");
goto exit;
}
change_inst_state(inst, MSM_VIDC_CLOSE);
exit:
return rc;
}
int msm_comm_suspend(int core_id)
{
struct hfi_device *hdev;
struct msm_vidc_core *core;
int rc = 0;
core = get_vidc_core(core_id);
if (!core) {
dprintk(VIDC_ERR,
"%s: Failed to find core for core_id = %d\n",
__func__, core_id);
return -EINVAL;
}
hdev = (struct hfi_device *)core->device;
if (!hdev) {
dprintk(VIDC_ERR, "%s Invalid device handle\n", __func__);
return -EINVAL;
}
rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data);
if (rc)
dprintk(VIDC_ERR, "Failed to suspend\n");
return rc;
}
static int get_flipped_state(int present_state,
int desired_state)
{
int flipped_state = present_state;
if (flipped_state < MSM_VIDC_STOP
&& desired_state > MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
} else if (flipped_state > MSM_VIDC_STOP
&& desired_state < MSM_VIDC_STOP) {
flipped_state = MSM_VIDC_STOP -
(flipped_state - MSM_VIDC_STOP + 1);
flipped_state &= 0xFFFE;
flipped_state = flipped_state - 1;
}
return flipped_state;
}
struct hal_buffer_requirements *get_buff_req_buffer(
struct msm_vidc_inst *inst, enum hal_buffer buffer_type)
{
int i;
for (i = 0; i < HAL_BUFFER_MAX; i++) {
if (inst->buff_req.buffer[i].buffer_type == buffer_type)
return &inst->buff_req.buffer[i];
}
dprintk(VIDC_ERR, "Failed to get buff req for : %x", buffer_type);
return NULL;
}
u32 msm_comm_convert_color_fmt(u32 v4l2_fmt)
{
switch (v4l2_fmt) {
case V4L2_PIX_FMT_NV12:
return COLOR_FMT_NV12;
case V4L2_PIX_FMT_NV12_512:
return COLOR_FMT_NV12_512;
case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS:
return COLOR_FMT_P010;
case V4L2_PIX_FMT_NV12_UBWC:
return COLOR_FMT_NV12_UBWC;
case V4L2_PIX_FMT_NV12_TP10_UBWC:
return COLOR_FMT_NV12_BPP10_UBWC;
default:
dprintk(VIDC_ERR,
"Invalid v4l2 color fmt FMT : %x, Set default(NV12)",
v4l2_fmt);
return COLOR_FMT_NV12;
}
}
static u32 get_hfi_buffer(int hal_buffer)
{
u32 buffer;
switch (hal_buffer) {
case HAL_BUFFER_INPUT:
buffer = HFI_BUFFER_INPUT;
break;
case HAL_BUFFER_OUTPUT:
buffer = HFI_BUFFER_OUTPUT;
break;
case HAL_BUFFER_OUTPUT2:
buffer = HFI_BUFFER_OUTPUT2;
break;
case HAL_BUFFER_EXTRADATA_INPUT:
buffer = HFI_BUFFER_EXTRADATA_INPUT;
break;
case HAL_BUFFER_EXTRADATA_OUTPUT:
buffer = HFI_BUFFER_EXTRADATA_OUTPUT;
break;
case HAL_BUFFER_EXTRADATA_OUTPUT2:
buffer = HFI_BUFFER_EXTRADATA_OUTPUT2;
break;
case HAL_BUFFER_INTERNAL_SCRATCH:
buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH;
break;
case HAL_BUFFER_INTERNAL_SCRATCH_1:
buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH_1;
break;
case HAL_BUFFER_INTERNAL_SCRATCH_2:
buffer = HFI_BUFFER_COMMON_INTERNAL_SCRATCH_2;
break;
case HAL_BUFFER_INTERNAL_PERSIST:
buffer = HFI_BUFFER_INTERNAL_PERSIST;
break;
case HAL_BUFFER_INTERNAL_PERSIST_1:
buffer = HFI_BUFFER_INTERNAL_PERSIST_1;
break;
default:
dprintk(VIDC_ERR, "Invalid buffer: %#x\n",
hal_buffer);
buffer = 0;
break;
}
return buffer;
}
static int set_dpb_only_buffers(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type)
{
int rc = 0;
struct internal_buf *binfo = NULL;
u32 smem_flags = SMEM_UNCACHED, buffer_size, num_buffers, hfi_fmt;
struct msm_vidc_format *fmt;
unsigned int i;
struct hfi_device *hdev;
struct hfi_buffer_size_minimum b;
struct v4l2_format *f;
hdev = inst->core->device;
fmt = &inst->fmts[OUTPUT_PORT];
/* For DPB buffers, Always use min count */
num_buffers = fmt->count_min;
hfi_fmt = msm_comm_convert_color_fmt(inst->clk_data.dpb_fourcc);
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
buffer_size = VENUS_BUFFER_SIZE(hfi_fmt, f->fmt.pix_mp.width,
f->fmt.pix_mp.height);
dprintk(VIDC_HIGH,
"output: num = %d, size = %d\n",
num_buffers,
buffer_size);
b.buffer_type = get_hfi_buffer(buffer_type);
if (!b.buffer_type)
return -EINVAL;
b.buffer_size = buffer_size;
rc = call_hfi_op(hdev, session_set_property,
inst->session, HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM,
&b, sizeof(b));
if (f->fmt.pix_mp.num_planes == 1 ||
!f->fmt.pix_mp.plane_fmt[1].sizeimage) {
dprintk(VIDC_HIGH,
"This extradata buffer not required, buffer_type: %x\n",
buffer_type);
} else {
dprintk(VIDC_HIGH,
"extradata: num = 1, size = %d\n",
f->fmt.pix_mp.plane_fmt[1].sizeimage);
inst->dpb_extra_binfo = NULL;
inst->dpb_extra_binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!inst->dpb_extra_binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
rc = msm_comm_smem_alloc(inst,
f->fmt.pix_mp.plane_fmt[1].sizeimage, 1, smem_flags,
buffer_type, 0, &inst->dpb_extra_binfo->smem);
if (rc) {
dprintk(VIDC_ERR,
"Failed to allocate output memory\n");
goto err_no_mem;
}
}
if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
if (buffer_size) {
for (i = 0; i < num_buffers; i++) {
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
rc = msm_comm_smem_alloc(inst,
buffer_size, 1, smem_flags,
buffer_type, 0, &binfo->smem);
if (rc) {
dprintk(VIDC_ERR,
"Failed to allocate output memory\n");
goto err_no_mem;
}
binfo->buffer_type = buffer_type;
binfo->buffer_ownership = DRIVER;
dprintk(VIDC_HIGH, "Output buffer address: %#x\n",
binfo->smem.device_addr);
if (inst->buffer_mode_set[OUTPUT_PORT] ==
HAL_BUFFER_MODE_STATIC) {
struct vidc_buffer_addr_info buffer_info = {0};
buffer_info.buffer_size = buffer_size;
buffer_info.buffer_type = buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr =
binfo->smem.device_addr;
buffer_info.extradata_addr =
inst->dpb_extra_binfo->smem.device_addr;
buffer_info.extradata_size =
inst->dpb_extra_binfo->smem.size;
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
"%s : session_set_buffers failed\n",
__func__);
goto fail_set_buffers;
}
}
mutex_lock(&inst->outputbufs.lock);
list_add_tail(&binfo->list, &inst->outputbufs.list);
mutex_unlock(&inst->outputbufs.lock);
}
}
return rc;
fail_set_buffers:
msm_comm_smem_free(inst, &binfo->smem);
err_no_mem:
kfree(binfo);
fail_kzalloc:
return rc;
}
static inline char *get_buffer_name(enum hal_buffer buffer_type)
{
switch (buffer_type) {
case HAL_BUFFER_INPUT: return "input";
case HAL_BUFFER_OUTPUT: return "output";
case HAL_BUFFER_OUTPUT2: return "output_2";
case HAL_BUFFER_EXTRADATA_INPUT: return "input_extra";
case HAL_BUFFER_EXTRADATA_OUTPUT: return "output_extra";
case HAL_BUFFER_EXTRADATA_OUTPUT2: return "output2_extra";
case HAL_BUFFER_INTERNAL_SCRATCH: return "scratch";
case HAL_BUFFER_INTERNAL_SCRATCH_1: return "scratch_1";
case HAL_BUFFER_INTERNAL_SCRATCH_2: return "scratch_2";
case HAL_BUFFER_INTERNAL_PERSIST: return "persist";
case HAL_BUFFER_INTERNAL_PERSIST_1: return "persist_1";
case HAL_BUFFER_INTERNAL_CMD_QUEUE: return "queue";
default: return "????";
}
}
static int set_internal_buf_on_fw(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type,
struct msm_smem *handle, bool reuse)
{
struct vidc_buffer_addr_info buffer_info;
struct hfi_device *hdev;
int rc = 0;
if (!inst || !inst->core || !inst->core->device || !handle) {
dprintk(VIDC_ERR, "%s - invalid params\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
dprintk(VIDC_HIGH, "%s %s buffer : %x\n",
reuse ? "Reusing" : "Allocated",
get_buffer_name(buffer_type),
buffer_info.align_device_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
"vidc_hal_session_set_buffers failed\n");
return rc;
}
return 0;
}
static bool reuse_internal_buffers(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type, struct msm_vidc_list *buf_list)
{
struct internal_buf *buf;
int rc = 0;
bool reused = false;
if (!inst || !buf_list) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return false;
}
mutex_lock(&buf_list->lock);
list_for_each_entry(buf, &buf_list->list, list) {
if (buf->buffer_type != buffer_type)
continue;
/*
* Persist buffer size won't change with resolution. If they
* are in queue means that they are already allocated and
* given to HW. HW can use them without reallocation. These
* buffers are not released as part of port reconfig. So
* driver no need to set them again.
*/
if (buffer_type != HAL_BUFFER_INTERNAL_PERSIST
&& buffer_type != HAL_BUFFER_INTERNAL_PERSIST_1) {
rc = set_internal_buf_on_fw(inst, buffer_type,
&buf->smem, true);
if (rc) {
dprintk(VIDC_ERR,
"%s: session_set_buffers failed\n",
__func__);
reused = false;
break;
}
}
reused = true;
dprintk(VIDC_HIGH,
"Re-using internal buffer type : %d\n", buffer_type);
}
mutex_unlock(&buf_list->lock);
return reused;
}
static int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst,
struct hal_buffer_requirements *internal_bufreq,
struct msm_vidc_list *buf_list)
{
struct internal_buf *binfo;
u32 smem_flags = SMEM_UNCACHED;
int rc = 0;
unsigned int i = 0;
if (!inst || !internal_bufreq || !buf_list)
return -EINVAL;
if (!internal_bufreq->buffer_size)
return 0;
if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
for (i = 0; i < internal_bufreq->buffer_count_actual; i++) {
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
rc = msm_comm_smem_alloc(inst, internal_bufreq->buffer_size,
1, smem_flags, internal_bufreq->buffer_type,
0, &binfo->smem);
if (rc) {
dprintk(VIDC_ERR,
"Failed to allocate scratch memory\n");
goto err_no_mem;
}
binfo->buffer_type = internal_bufreq->buffer_type;
rc = set_internal_buf_on_fw(inst, internal_bufreq->buffer_type,
&binfo->smem, false);
if (rc)
goto fail_set_buffers;
mutex_lock(&buf_list->lock);
list_add_tail(&binfo->list, &buf_list->list);
mutex_unlock(&buf_list->lock);
}
return rc;
fail_set_buffers:
msm_comm_smem_free(inst, &binfo->smem);
err_no_mem:
kfree(binfo);
fail_kzalloc:
return rc;
}
static int set_internal_buffers(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type, struct msm_vidc_list *buf_list)
{
struct hal_buffer_requirements *internal_buf;
internal_buf = get_buff_req_buffer(inst, buffer_type);
if (!internal_buf) {
dprintk(VIDC_HIGH,
"This internal buffer not required, buffer_type: %x\n",
buffer_type);
return 0;
}
dprintk(VIDC_HIGH, "Buffer type %s: num = %d, size = %d\n",
get_buffer_name(buffer_type),
internal_buf->buffer_count_actual, internal_buf->buffer_size);
/*
* Try reusing existing internal buffers first.
* If it's not possible to reuse, allocate new buffers.
*/
if (reuse_internal_buffers(inst, buffer_type, buf_list))
return 0;
return allocate_and_set_internal_bufs(inst, internal_buf,
buf_list);
}
int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
{
int rc = 0;
int flipped_state;
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid params %pK", __func__, inst);
return -EINVAL;
}
dprintk(VIDC_HIGH,
"Trying to move inst: %pK (%#x) from: %#x to %#x\n",
inst, hash32_ptr(inst->session), inst->state, state);
mutex_lock(&inst->sync_lock);
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR, "%s: inst %pK is in invalid\n",
__func__, inst);
rc = -EINVAL;
goto exit;
}
flipped_state = get_flipped_state(inst->state, state);
dprintk(VIDC_HIGH,
"inst: %pK (%#x) flipped_state = %#x\n",
inst, hash32_ptr(inst->session), flipped_state);
switch (flipped_state) {
case MSM_VIDC_CORE_UNINIT_DONE:
case MSM_VIDC_CORE_INIT:
rc = msm_comm_init_core(inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_CORE_INIT_DONE:
rc = msm_comm_init_core_done(inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_OPEN:
rc = msm_comm_session_init(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_OPEN_DONE:
rc = msm_comm_session_init_done(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_LOAD_RESOURCES:
rc = msm_vidc_load_resources(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_LOAD_RESOURCES_DONE:
case MSM_VIDC_START:
rc = msm_vidc_start(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_START_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_START_DONE,
HAL_SESSION_START_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_STOP:
rc = msm_vidc_stop(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_STOP_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_STOP_DONE,
HAL_SESSION_STOP_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
dprintk(VIDC_HIGH, "Moving to Stop Done state\n");
case MSM_VIDC_RELEASE_RESOURCES:
rc = msm_vidc_release_res(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_RELEASE_RESOURCES_DONE:
rc = wait_for_state(inst, flipped_state,
MSM_VIDC_RELEASE_RESOURCES_DONE,
HAL_SESSION_RELEASE_RESOURCE_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
dprintk(VIDC_HIGH,
"Moving to release resources done state\n");
case MSM_VIDC_CLOSE:
rc = msm_comm_session_close(flipped_state, inst);
if (rc || state <= get_flipped_state(inst->state, state))
break;
case MSM_VIDC_CLOSE_DONE:
rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
HAL_SESSION_END_DONE);
if (rc || state <= get_flipped_state(inst->state, state))
break;
msm_comm_session_clean(inst);
case MSM_VIDC_CORE_UNINIT:
case MSM_VIDC_CORE_INVALID:
dprintk(VIDC_HIGH, "Sending core uninit\n");
rc = msm_vidc_deinit_core(inst);
if (rc || state == get_flipped_state(inst->state, state))
break;
default:
dprintk(VIDC_ERR, "State not recognized\n");
rc = -EINVAL;
break;
}
exit:
mutex_unlock(&inst->sync_lock);
if (rc) {
dprintk(VIDC_ERR,
"Failed to move from state: %d to %d\n",
inst->state, state);
msm_comm_kill_session(inst);
} else {
trace_msm_vidc_common_state_change((void *)inst,
inst->state, state);
}
return rc;
}
int msm_vidc_send_pending_eos_buffers(struct msm_vidc_inst *inst)
{
struct vidc_frame_data data = {0};
struct hfi_device *hdev;
struct eos_buf *binfo = NULL, *temp = NULL;
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
return -EINVAL;
}
mutex_lock(&inst->eosbufs.lock);
list_for_each_entry_safe(binfo, temp, &inst->eosbufs.list, list) {
data.alloc_len = binfo->smem.size;
data.device_addr = binfo->smem.device_addr;
data.clnt_data = data.device_addr;
data.buffer_type = HAL_BUFFER_INPUT;
data.filled_len = 0;
data.offset = 0;
data.flags = HAL_BUFFERFLAG_EOS;
data.timestamp = 0;
data.extradata_addr = data.device_addr;
data.extradata_size = 0;
dprintk(VIDC_HIGH, "Queueing EOS buffer 0x%x\n",
data.device_addr);
hdev = inst->core->device;
rc = call_hfi_op(hdev, session_etb, inst->session,
&data);
}
mutex_unlock(&inst->eosbufs.lock);
return rc;
}
int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd)
{
struct msm_vidc_inst *inst = instance;
struct v4l2_decoder_cmd *dec = NULL;
struct v4l2_encoder_cmd *enc = NULL;
struct msm_vidc_core *core;
int which_cmd = 0, flags = 0, rc = 0;
if (!inst || !inst->core || !cmd) {
dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return -EINVAL;
}
core = inst->core;
if (inst->session_type == MSM_VIDC_ENCODER) {
enc = (struct v4l2_encoder_cmd *)cmd;
which_cmd = enc->cmd;
flags = enc->flags;
} else if (inst->session_type == MSM_VIDC_DECODER) {
dec = (struct v4l2_decoder_cmd *)cmd;
which_cmd = dec->cmd;
flags = dec->flags;
}
switch (which_cmd) {
case V4L2_CMD_FLUSH:
rc = msm_comm_flush(inst, flags);
if (rc) {
dprintk(VIDC_ERR,
"Failed to flush buffers: %d\n", rc);
}
break;
case V4L2_CMD_SESSION_CONTINUE:
{
rc = msm_comm_session_continue(inst);
break;
}
/* This case also for V4L2_ENC_CMD_STOP */
case V4L2_DEC_CMD_STOP:
{
struct eos_buf *binfo = NULL;
u32 smem_flags = SMEM_UNCACHED;
if (inst->state != MSM_VIDC_START_DONE) {
dprintk(VIDC_HIGH,
"Inst = %pK is not ready for EOS\n", inst);
break;
}
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "%s: Out of memory\n", __func__);
rc = -ENOMEM;
break;
}
if (inst->flags & VIDC_SECURE)
smem_flags |= SMEM_SECURE;
rc = msm_comm_smem_alloc(inst,
SZ_4K, 1, smem_flags,
HAL_BUFFER_INPUT, 0, &binfo->smem);
if (rc) {
kfree(binfo);
dprintk(VIDC_ERR,
"Failed to allocate output memory\n");
rc = -ENOMEM;
break;
}
mutex_lock(&inst->eosbufs.lock);
list_add_tail(&binfo->list, &inst->eosbufs.list);
mutex_unlock(&inst->eosbufs.lock);
rc = msm_vidc_send_pending_eos_buffers(inst);
if (rc) {
dprintk(VIDC_ERR,
"Failed pending_eos_buffers sending\n");
list_del(&binfo->list);
kfree(binfo);
break;
}
break;
}
default:
dprintk(VIDC_ERR, "Unknown Command %d\n", which_cmd);
rc = -ENOTSUPP;
break;
}
return rc;
}
static int msm_comm_preprocess(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return -EINVAL;
}
/* preprocessing is allowed for encoder input buffer only */
if (!is_encode_session(inst) || mbuf->vvb.vb2_buf.type != INPUT_MPLANE)
return 0;
/* preprocessing is done using CVP module only */
if (!is_vidc_cvp_enabled(inst))
return 0;
rc = msm_vidc_cvp_preprocess(inst, mbuf);
if (rc) {
dprintk(VIDC_ERR, "%s: cvp preprocess failed\n",
__func__);
return rc;
}
return rc;
}
static void populate_frame_data(struct vidc_frame_data *data,
struct msm_vidc_buffer *mbuf, struct msm_vidc_inst *inst)
{
u64 time_usec;
struct v4l2_format *f = NULL;
struct vb2_buffer *vb;
struct vb2_v4l2_buffer *vbuf;
if (!inst || !mbuf || !data) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK %pK\n",
__func__, inst, mbuf, data);
return;
}
vb = &mbuf->vvb.vb2_buf;
vbuf = to_vb2_v4l2_buffer(vb);
time_usec = vb->timestamp;
do_div(time_usec, NSEC_PER_USEC);
data->alloc_len = vb->planes[0].length;
data->device_addr = mbuf->smem[0].device_addr;
data->timestamp = time_usec;
data->flags = 0;
data->clnt_data = data->device_addr;
if (vb->type == INPUT_MPLANE) {
data->buffer_type = HAL_BUFFER_INPUT;
data->filled_len = vb->planes[0].bytesused;
data->offset = vb->planes[0].data_offset;
if (vbuf->flags & V4L2_BUF_FLAG_EOS)
data->flags |= HAL_BUFFERFLAG_EOS;
if (vbuf->flags & V4L2_BUF_FLAG_CODECCONFIG)
data->flags |= HAL_BUFFERFLAG_CODECCONFIG;
if (inst->session_type == MSM_VIDC_DECODER) {
msm_comm_fetch_mark_data(&inst->etb_data, vb->index,
&data->mark_data, &data->mark_target);
}
f = &inst->fmts[INPUT_PORT].v4l2_fmt;
} else if (vb->type == OUTPUT_MPLANE) {
data->buffer_type = msm_comm_get_hal_output_buffer(inst);
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
}
if (f && f->fmt.pix_mp.num_planes > 1) {
data->extradata_addr = mbuf->smem[1].device_addr;
data->extradata_size = vb->planes[1].length;
data->flags |= HAL_BUFFERFLAG_EXTRADATA;
}
}
enum hal_buffer get_hal_buffer_type(unsigned int type,
unsigned int plane_num)
{
if (type == INPUT_MPLANE) {
if (plane_num == 0)
return HAL_BUFFER_INPUT;
else
return HAL_BUFFER_EXTRADATA_INPUT;
} else if (type == OUTPUT_MPLANE) {
if (plane_num == 0)
return HAL_BUFFER_OUTPUT;
else
return HAL_BUFFER_EXTRADATA_OUTPUT;
} else {
return -EINVAL;
}
}
int msm_comm_num_queued_bufs(struct msm_vidc_inst *inst, u32 type)
{
int count = 0;
struct msm_vidc_buffer *mbuf;
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return 0;
}
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
if (mbuf->vvb.vb2_buf.type != type)
continue;
if (!(mbuf->flags & MSM_VIDC_FLAG_QUEUED))
continue;
count++;
}
mutex_unlock(&inst->registeredbufs.lock);
return count;
}
static int num_pending_qbufs(struct msm_vidc_inst *inst, u32 type)
{
int count = 0;
struct msm_vidc_buffer *mbuf;
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return 0;
}
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
if (mbuf->vvb.vb2_buf.type != type)
continue;
/* Count only deferred buffers */
if (!(mbuf->flags & MSM_VIDC_FLAG_DEFERRED))
continue;
count++;
}
mutex_unlock(&inst->registeredbufs.lock);
return count;
}
static int msm_comm_qbuf_to_hfi(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
struct hfi_device *hdev;
enum msm_vidc_debugfs_event e;
struct vidc_frame_data frame_data = {0};
if (!inst || !inst->core || !inst->core->device || !mbuf) {
dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
populate_frame_data(&frame_data, mbuf, inst);
/* mbuf is not deferred anymore */
mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED;
if (mbuf->vvb.vb2_buf.type == INPUT_MPLANE) {
e = MSM_VIDC_DEBUGFS_EVENT_ETB;
rc = call_hfi_op(hdev, session_etb, inst->session, &frame_data);
} else if (mbuf->vvb.vb2_buf.type == OUTPUT_MPLANE) {
e = MSM_VIDC_DEBUGFS_EVENT_FTB;
rc = call_hfi_op(hdev, session_ftb, inst->session, &frame_data);
} else {
dprintk(VIDC_ERR, "%s: invalid qbuf type %d:\n", __func__,
mbuf->vvb.vb2_buf.type);
rc = -EINVAL;
}
if (rc) {
dprintk(VIDC_ERR, "%s: Failed to qbuf: %d\n", __func__, rc);
goto err_bad_input;
}
mbuf->flags |= MSM_VIDC_FLAG_QUEUED;
msm_vidc_debugfs_update(inst, e);
err_bad_input:
return rc;
}
static int msm_comm_qbuf_in_rbr(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__);
return -EINVAL;
}
rc = msm_comm_scale_clocks_and_bus(inst);
if (rc)
dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__);
print_vidc_buffer(VIDC_HIGH, "qbuf in rbr", inst, mbuf);
rc = msm_comm_qbuf_to_hfi(inst, mbuf);
if (rc)
dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", __func__, rc);
return rc;
}
int msm_comm_qbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf)
{
int rc = 0;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__);
return -EINVAL;
}
if (inst->state != MSM_VIDC_START_DONE) {
mbuf->flags |= MSM_VIDC_FLAG_DEFERRED;
print_vidc_buffer(VIDC_HIGH, "qbuf deferred", inst, mbuf);
return 0;
}
rc = msm_comm_preprocess(inst, mbuf);
if (rc)
return rc;
rc = msm_comm_scale_clocks_and_bus(inst);
if (rc)
dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__);
print_vidc_buffer(VIDC_HIGH, "qbuf", inst, mbuf);
rc = msm_comm_qbuf_to_hfi(inst, mbuf);
if (rc)
dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n", __func__, rc);
return rc;
}
int msm_comm_qbufs(struct msm_vidc_inst *inst)
{
int rc = 0;
struct msm_vidc_buffer *mbuf;
bool found;
if (!inst) {
dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
return -EINVAL;
}
if (inst->state != MSM_VIDC_START_DONE) {
dprintk(VIDC_HIGH, "%s: inst not in start state: %d\n",
__func__, inst->state);
return 0;
}
do {
mutex_lock(&inst->registeredbufs.lock);
found = false;
list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
/* Queue only deferred buffers */
if (mbuf->flags & MSM_VIDC_FLAG_DEFERRED) {
found = true;
break;
}
}
mutex_unlock(&inst->registeredbufs.lock);
if (!found) {
dprintk(VIDC_HIGH,
"%s: no more deferred qbufs\n", __func__);
break;
}
/* do not call msm_comm_qbuf() under registerbufs lock */
if (!kref_get_mbuf(inst, mbuf)) {
dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__);
rc = -EINVAL;
break;
}
rc = msm_comm_qbuf(inst, mbuf);
kref_put_mbuf(mbuf);
if (rc) {
dprintk(VIDC_ERR, "%s: failed qbuf\n", __func__);
break;
}
} while (found);
return rc;
}
/*
* msm_comm_qbuf_decode_batch - count the buffers which are not queued to
* firmware yet (count includes rbr pending buffers too) and
* queue the buffers at once if full batch count reached.
* Don't queue rbr pending buffers as they would be queued
* when rbr event arrived from firmware.
*/
int msm_comm_qbuf_decode_batch(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
u32 count = 0;
struct msm_vidc_buffer *buf;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR, "%s: inst is in bad state\n", __func__);
return -EINVAL;
}
if (inst->state != MSM_VIDC_START_DONE) {
mbuf->flags |= MSM_VIDC_FLAG_DEFERRED;
print_vidc_buffer(VIDC_HIGH, "qbuf deferred", inst, mbuf);
return 0;
}
/*
* Don't defer buffers initially to avoid startup latency increase
* due to batching
*/
if (inst->clk_data.buffer_counter > SKIP_BATCH_WINDOW) {
count = num_pending_qbufs(inst, OUTPUT_MPLANE);
if (count < inst->batch.size) {
print_vidc_buffer(VIDC_HIGH,
"batch-qbuf deferred", inst, mbuf);
return 0;
}
}
rc = msm_comm_scale_clocks_and_bus(inst);
if (rc)
dprintk(VIDC_ERR, "%s: scale clocks failed\n", __func__);
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry(buf, &inst->registeredbufs.list, list) {
/* Don't queue if buffer is not CAPTURE_MPLANE */
if (buf->vvb.vb2_buf.type != OUTPUT_MPLANE)
goto loop_end;
/* Don't queue if buffer is not a deferred buffer */
if (!(buf->flags & MSM_VIDC_FLAG_DEFERRED))
goto loop_end;
/* Don't queue if RBR event is pending on this buffer */
if (buf->flags & MSM_VIDC_FLAG_RBR_PENDING)
goto loop_end;
print_vidc_buffer(VIDC_HIGH, "batch-qbuf", inst, buf);
rc = msm_comm_qbuf_to_hfi(inst, buf);
if (rc) {
dprintk(VIDC_ERR, "%s: Failed qbuf to hfi: %d\n",
__func__, rc);
break;
}
loop_end:
/* Queue pending buffers till the current buffer only */
if (buf == mbuf)
break;
}
mutex_unlock(&inst->registeredbufs.lock);
return rc;
}
int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
{
int rc = -EINVAL, i = 0;
union hal_get_property hprop;
memset(&hprop, 0x0, sizeof(hprop));
/*
* First check if we can calculate bufffer sizes.
* If we can calculate then we do it within the driver.
* If we cannot then we get buffer requirements from firmware.
*/
if (inst->buffer_size_calculators) {
rc = inst->buffer_size_calculators(inst);
if (rc)
dprintk(VIDC_ERR,
"Failed calculating internal buffer sizes: %d", rc);
}
/*
* Fallback to get buffreq from firmware if internal calculation
* is not done or if it fails
*/
if (rc) {
rc = msm_comm_try_get_buff_req(inst, &hprop);
if (rc) {
dprintk(VIDC_ERR,
"Failed getting buffer requirements: %d", rc);
return rc;
}
for (i = 0; i < HAL_BUFFER_MAX; i++) {
struct hal_buffer_requirements req;
struct hal_buffer_requirements *curr_req;
req = hprop.buf_req.buffer[i];
/*
* Firmware buffer requirements are needed for internal
* buffers only and all other buffer requirements are
* calculated in driver.
*/
curr_req = get_buff_req_buffer(inst, req.buffer_type);
if (!curr_req)
return -EINVAL;
if (req.buffer_type == HAL_BUFFER_INTERNAL_SCRATCH ||
req.buffer_type ==
HAL_BUFFER_INTERNAL_SCRATCH_1 ||
req.buffer_type ==
HAL_BUFFER_INTERNAL_SCRATCH_2 ||
req.buffer_type ==
HAL_BUFFER_INTERNAL_PERSIST ||
req.buffer_type ==
HAL_BUFFER_INTERNAL_PERSIST_1 ||
req.buffer_type == HAL_BUFFER_INTERNAL_RECON) {
memcpy(curr_req, &req,
sizeof(struct hal_buffer_requirements));
}
}
}
dprintk(VIDC_HIGH, "Buffer requirements :\n");
dprintk(VIDC_HIGH, "%15s %8s %8s %8s %8s %8s\n",
"buffer type", "count", "mincount_host", "mincount_fw", "size",
"alignment");
for (i = 0; i < HAL_BUFFER_MAX; i++) {
struct hal_buffer_requirements req = inst->buff_req.buffer[i];
if (req.buffer_type != HAL_BUFFER_NONE) {
dprintk(VIDC_HIGH, "%15s %8d %8d %8d %8d %8d\n",
get_buffer_name(req.buffer_type),
req.buffer_count_actual,
req.buffer_count_min_host,
req.buffer_count_min, req.buffer_size,
req.buffer_alignment);
}
}
return rc;
}
int msm_comm_try_get_buff_req(struct msm_vidc_inst *inst,
union hal_get_property *hprop)
{
int rc = 0;
struct hfi_device *hdev;
struct getprop_buf *buf;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
mutex_lock(&inst->sync_lock);
if (inst->state < MSM_VIDC_OPEN_DONE ||
inst->state >= MSM_VIDC_CLOSE) {
/* No need to check inst->state == MSM_VIDC_INVALID since
* INVALID is > CLOSE_DONE. When core went to INVALID state,
* we put all the active instances in INVALID. So > CLOSE_DONE
* is enough check to have.
*/
dprintk(VIDC_ERR,
"In Wrong state to call Buf Req: Inst %pK or Core %pK\n",
inst, inst->core);
rc = -EAGAIN;
mutex_unlock(&inst->sync_lock);
goto exit;
}
mutex_unlock(&inst->sync_lock);
rc = call_hfi_op(hdev, session_get_buf_req, inst->session);
if (rc) {
dprintk(VIDC_ERR, "Can't query hardware for property: %d\n",
rc);
goto exit;
}
rc = wait_for_completion_timeout(&inst->completions[
SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)],
msecs_to_jiffies(
inst->core->resources.msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR,
"%s: Wait interrupted or timed out [%pK]: %d\n",
__func__, inst,
SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO));
msm_comm_kill_session(inst);
rc = -ETIMEDOUT;
goto exit;
} else {
/* wait_for_completion_timeout returns jiffies before expiry */
rc = 0;
}
mutex_lock(&inst->pending_getpropq.lock);
if (!list_empty(&inst->pending_getpropq.list)) {
buf = list_first_entry(&inst->pending_getpropq.list,
struct getprop_buf, list);
*hprop = *(union hal_get_property *)buf->data;
kfree(buf->data);
list_del(&buf->list);
kfree(buf);
} else {
dprintk(VIDC_ERR, "%s getprop list empty\n", __func__);
rc = -EINVAL;
}
mutex_unlock(&inst->pending_getpropq.lock);
exit:
return rc;
}
int msm_comm_release_dpb_only_buffers(struct msm_vidc_inst *inst,
bool force_release)
{
struct msm_smem *handle;
struct internal_buf *buf, *dummy;
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %pK\n", inst);
return -EINVAL;
}
mutex_lock(&inst->outputbufs.lock);
if (list_empty(&inst->outputbufs.list)) {
dprintk(VIDC_HIGH, "%s - No OUTPUT buffers allocated\n",
__func__);
mutex_unlock(&inst->outputbufs.lock);
return 0;
}
mutex_unlock(&inst->outputbufs.lock);
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %pK\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
return -EINVAL;
}
mutex_lock(&inst->outputbufs.lock);
list_for_each_entry_safe(buf, dummy, &inst->outputbufs.list, list) {
handle = &buf->smem;
if ((buf->buffer_ownership == FIRMWARE) && !force_release) {
dprintk(VIDC_HIGH, "DPB is with f/w. Can't free it\n");
/*
* mark this buffer to avoid sending it to video h/w
* again, this buffer belongs to old resolution and
* it will be removed when video h/w returns it.
*/
buf->mark_remove = true;
continue;
}
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
if (inst->buffer_mode_set[OUTPUT_PORT] ==
HAL_BUFFER_MODE_STATIC) {
buffer_info.response_required = false;
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
"Rel output buf fail:%x, %d\n",
buffer_info.align_device_addr,
buffer_info.buffer_size);
}
}
list_del(&buf->list);
msm_comm_smem_free(inst, &buf->smem);
kfree(buf);
}
if (inst->dpb_extra_binfo) {
msm_comm_smem_free(inst, &inst->dpb_extra_binfo->smem);
kfree(inst->dpb_extra_binfo);
inst->dpb_extra_binfo = NULL;
}
mutex_unlock(&inst->outputbufs.lock);
return rc;
}
static enum hal_buffer scratch_buf_sufficient(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type)
{
struct hal_buffer_requirements *bufreq = NULL;
struct internal_buf *buf;
int count = 0;
if (!inst) {
dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
goto not_sufficient;
}
bufreq = get_buff_req_buffer(inst, buffer_type);
if (!bufreq)
goto not_sufficient;
/* Check if current scratch buffers are sufficient */
mutex_lock(&inst->scratchbufs.lock);
list_for_each_entry(buf, &inst->scratchbufs.list, list) {
if (buf->buffer_type == buffer_type &&
buf->smem.size >= bufreq->buffer_size)
count++;
}
mutex_unlock(&inst->scratchbufs.lock);
if (count != bufreq->buffer_count_actual)
goto not_sufficient;
dprintk(VIDC_HIGH,
"Existing scratch buffer is sufficient for buffer type %#x\n",
buffer_type);
return buffer_type;
not_sufficient:
return HAL_BUFFER_NONE;
}
int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst,
bool check_for_reuse)
{
struct msm_smem *handle;
struct internal_buf *buf, *dummy;
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
enum hal_buffer sufficiency = HAL_BUFFER_NONE;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %pK\n", inst);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %pK\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
return -EINVAL;
}
if (check_for_reuse) {
sufficiency |= scratch_buf_sufficient(inst,
HAL_BUFFER_INTERNAL_SCRATCH);
sufficiency |= scratch_buf_sufficient(inst,
HAL_BUFFER_INTERNAL_SCRATCH_1);
sufficiency |= scratch_buf_sufficient(inst,
HAL_BUFFER_INTERNAL_SCRATCH_2);
}
mutex_lock(&inst->scratchbufs.lock);
list_for_each_entry_safe(buf, dummy, &inst->scratchbufs.list, list) {
handle = &buf->smem;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
buffer_info.response_required = true;
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (!rc) {
mutex_unlock(&inst->scratchbufs.lock);
rc = wait_for_sess_signal_receipt(inst,
HAL_SESSION_RELEASE_BUFFER_DONE);
if (rc)
dprintk(VIDC_ERR,
"%s: wait for signal failed, rc %d\n",
__func__, rc);
mutex_lock(&inst->scratchbufs.lock);
} else {
dprintk(VIDC_ERR,
"Rel scrtch buf fail:%x, %d\n",
buffer_info.align_device_addr,
buffer_info.buffer_size);
}
/*If scratch buffers can be reused, do not free the buffers*/
if (sufficiency & buf->buffer_type)
continue;
list_del(&buf->list);
msm_comm_smem_free(inst, handle);
kfree(buf);
}
mutex_unlock(&inst->scratchbufs.lock);
return rc;
}
void msm_comm_release_eos_buffers(struct msm_vidc_inst *inst)
{
struct eos_buf *buf, *next;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %pK\n", inst);
return;
}
mutex_lock(&inst->eosbufs.lock);
list_for_each_entry_safe(buf, next, &inst->eosbufs.list, list) {
list_del(&buf->list);
msm_comm_smem_free(inst, &buf->smem);
kfree(buf);
}
INIT_LIST_HEAD(&inst->eosbufs.list);
mutex_unlock(&inst->eosbufs.lock);
}
int msm_comm_release_recon_buffers(struct msm_vidc_inst *inst)
{
struct recon_buf *buf, *next;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %pK\n", inst);
return -EINVAL;
}
mutex_lock(&inst->reconbufs.lock);
list_for_each_entry_safe(buf, next, &inst->reconbufs.list, list) {
list_del(&buf->list);
kfree(buf);
}
INIT_LIST_HEAD(&inst->reconbufs.list);
mutex_unlock(&inst->reconbufs.lock);
return 0;
}
int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst)
{
struct msm_smem *handle;
struct list_head *ptr, *next;
struct internal_buf *buf;
struct vidc_buffer_addr_info buffer_info;
int rc = 0;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst) {
dprintk(VIDC_ERR,
"Invalid instance pointer = %pK\n", inst);
return -EINVAL;
}
core = inst->core;
if (!core) {
dprintk(VIDC_ERR,
"Invalid core pointer = %pK\n", core);
return -EINVAL;
}
hdev = core->device;
if (!hdev) {
dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
return -EINVAL;
}
mutex_lock(&inst->persistbufs.lock);
list_for_each_safe(ptr, next, &inst->persistbufs.list) {
buf = list_entry(ptr, struct internal_buf, list);
handle = &buf->smem;
buffer_info.buffer_size = handle->size;
buffer_info.buffer_type = buf->buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
buffer_info.response_required = true;
rc = call_hfi_op(hdev, session_release_buffers,
(void *)inst->session, &buffer_info);
if (!rc) {
mutex_unlock(&inst->persistbufs.lock);
rc = wait_for_sess_signal_receipt(inst,
HAL_SESSION_RELEASE_BUFFER_DONE);
if (rc)
dprintk(VIDC_ERR,
"%s: wait for signal failed, rc %d\n",
__func__, rc);
mutex_lock(&inst->persistbufs.lock);
} else {
dprintk(VIDC_ERR,
"Rel prst buf fail:%x, %d\n",
buffer_info.align_device_addr,
buffer_info.buffer_size);
}
list_del(&buf->list);
msm_comm_smem_free(inst, handle);
kfree(buf);
}
mutex_unlock(&inst->persistbufs.lock);
return rc;
}
int msm_comm_set_buffer_count(struct msm_vidc_inst *inst,
int host_count, int act_count, enum hal_buffer type)
{
int rc = 0;
struct hfi_device *hdev;
struct hfi_buffer_count_actual buf_count;
hdev = inst->core->device;
buf_count.buffer_type = get_hfi_buffer(type);
buf_count.buffer_count_actual = act_count;
buf_count.buffer_count_min_host = host_count;
dprintk(VIDC_HIGH, "%s: %x : hal_buffer %d min_host %d actual %d\n",
__func__, hash32_ptr(inst->session), type,
host_count, act_count);
rc = call_hfi_op(hdev, session_set_property,
inst->session, HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
&buf_count, sizeof(buf_count));
if (rc)
dprintk(VIDC_ERR,
"Failed to set actual buffer count %d for buffer type %d\n",
act_count, type);
return rc;
}
int msm_comm_set_dpb_only_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
bool force_release = true;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (get_v4l2_codec(inst) == V4L2_PIX_FMT_VP9)
force_release = false;
if (msm_comm_release_dpb_only_buffers(inst, force_release))
dprintk(VIDC_ERR, "Failed to release output buffers\n");
rc = set_dpb_only_buffers(inst, HAL_BUFFER_OUTPUT);
if (rc)
goto error;
return rc;
error:
msm_comm_release_dpb_only_buffers(inst, true);
return rc;
}
int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (msm_comm_release_scratch_buffers(inst, true))
dprintk(VIDC_ERR, "Failed to release scratch buffers\n");
rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH,
&inst->scratchbufs);
if (rc)
goto error;
rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1,
&inst->scratchbufs);
if (rc)
goto error;
rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2,
&inst->scratchbufs);
if (rc)
goto error;
return rc;
error:
msm_comm_release_scratch_buffers(inst, false);
return rc;
}
int msm_comm_set_recon_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
unsigned int i = 0;
struct hal_buffer_requirements *internal_buf;
struct recon_buf *binfo;
struct msm_vidc_list *buf_list = &inst->reconbufs;
if (!inst) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->session_type != MSM_VIDC_ENCODER) {
dprintk(VIDC_HIGH, "Recon buffs not req for decoder/cvp\n");
return 0;
}
internal_buf = get_buff_req_buffer(inst,
HAL_BUFFER_INTERNAL_RECON);
if (!internal_buf || !internal_buf->buffer_count_actual) {
dprintk(VIDC_HIGH, "Inst : %pK Recon buffers not required\n",
inst);
return 0;
}
msm_comm_release_recon_buffers(inst);
for (i = 0; i < internal_buf->buffer_count_actual; i++) {
binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
if (!binfo) {
dprintk(VIDC_ERR, "Out of memory\n");
rc = -ENOMEM;
goto fail_kzalloc;
}
binfo->buffer_index = i;
mutex_lock(&buf_list->lock);
list_add_tail(&binfo->list, &buf_list->list);
mutex_unlock(&buf_list->lock);
}
fail_kzalloc:
return rc;
}
int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST,
&inst->persistbufs);
if (rc)
goto error;
rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1,
&inst->persistbufs);
if (rc)
goto error;
return rc;
error:
msm_comm_release_persist_buffers(inst);
return rc;
}
static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
{
struct list_head *ptr, *next;
enum vidc_ports ports[] = {INPUT_PORT, OUTPUT_PORT};
int c = 0;
/* before flush ensure venus released all buffers */
msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
for (c = 0; c < ARRAY_SIZE(ports); ++c) {
enum vidc_ports port = ports[c];
mutex_lock(&inst->bufq[port].lock);
list_for_each_safe(ptr, next,
&inst->bufq[port].vb2_bufq.queued_list) {
struct vb2_buffer *vb = container_of(ptr,
struct vb2_buffer, queued_entry);
if (vb->state == VB2_BUF_STATE_ACTIVE) {
vb->planes[0].bytesused = 0;
print_vb2_buffer(VIDC_ERR, "flush in invalid",
inst, vb);
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
} else {
dprintk(VIDC_ERR,
"%s VB is in state %d not in ACTIVE state\n"
, __func__, vb->state);
}
}
mutex_unlock(&inst->bufq[port].lock);
}
msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
}
int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
{
unsigned int i = 0;
int rc = 0;
bool ip_flush = false;
bool op_flush = false;
struct msm_vidc_buffer *mbuf, *next;
struct msm_vidc_core *core;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR,
"Invalid params, inst %pK\n", inst);
return -EINVAL;
}
if (inst->state < MSM_VIDC_OPEN_DONE) {
dprintk(VIDC_ERR,
"Invalid state to call flush, inst %pK, state %#x\n",
inst, inst->state);
return -EINVAL;
}
core = inst->core;
hdev = core->device;
ip_flush = flags & V4L2_CMD_FLUSH_OUTPUT;
op_flush = flags & V4L2_CMD_FLUSH_CAPTURE;
if (ip_flush && !op_flush) {
dprintk(VIDC_ERR,
"Input only flush not supported, making it flush all\n");
op_flush = true;
return 0;
}
msm_clock_data_reset(inst);
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
"Core %pK and inst %pK are in bad state\n",
core, inst);
msm_comm_flush_in_invalid_state(inst);
return 0;
}
mutex_lock(&inst->flush_lock);
/* enable in flush */
inst->in_flush = true;
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry_safe(mbuf, next, &inst->registeredbufs.list, list) {
/* don't flush input buffers if input flush is not requested */
if (!ip_flush && mbuf->vvb.vb2_buf.type == INPUT_MPLANE)
continue;
/* flush only deferred or rbr pending buffers */
if (!(mbuf->flags & MSM_VIDC_FLAG_DEFERRED ||
mbuf->flags & MSM_VIDC_FLAG_RBR_PENDING))
continue;
/*
* flush buffers which are queued by client already,
* the refcount will be two or more for those buffers.
*/
if (!(mbuf->smem[0].refcount >= 2))
continue;
print_vidc_buffer(VIDC_HIGH, "flush buf", inst, mbuf);
msm_comm_flush_vidc_buffer(inst, mbuf);
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) {
if (inst->smem_ops->smem_unmap_dma_buf(inst,
&mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"dqbuf: unmap failed.", inst, mbuf);
if (inst->smem_ops->smem_unmap_dma_buf(inst,
&mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"dqbuf: unmap failed..", inst, mbuf);
}
if (!mbuf->smem[0].refcount) {
list_del(&mbuf->list);
kref_put_mbuf(mbuf);
} else {
/* buffer is no more a deferred buffer */
mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED;
}
}
mutex_unlock(&inst->registeredbufs.lock);
hdev = inst->core->device;
if (ip_flush) {
dprintk(VIDC_HIGH, "Send flush on all ports to firmware\n");
rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_ALL);
} else {
dprintk(VIDC_HIGH, "Send flush on output port to firmware\n");
rc = call_hfi_op(hdev, session_flush, inst->session,
HAL_FLUSH_OUTPUT);
}
mutex_unlock(&inst->flush_lock);
if (rc) {
dprintk(VIDC_ERR,
"Sending flush to firmware failed, flush out all buffers\n");
msm_comm_flush_in_invalid_state(inst);
/* disable in_flush */
inst->in_flush = false;
}
return rc;
}
int msm_vidc_noc_error_info(struct msm_vidc_core *core)
{
struct hfi_device *hdev;
if (!core || !core->device) {
dprintk(VIDC_ERR, "%s: Invalid parameters: %pK\n",
__func__, core);
return -EINVAL;
}
if (!core->resources.non_fatal_pagefaults)
return 0;
if (!core->smmu_fault_handled)
return 0;
hdev = core->device;
call_hfi_op(hdev, noc_error_info, hdev->hfi_device_data);
return 0;
}
int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
enum hal_ssr_trigger_type type)
{
if (!core) {
dprintk(VIDC_ERR, "%s: Invalid parameters\n", __func__);
return -EINVAL;
}
core->ssr_type = type;
schedule_work(&core->ssr_work);
return 0;
}
void msm_vidc_ssr_handler(struct work_struct *work)
{
int rc;
struct msm_vidc_core *core;
struct hfi_device *hdev;
core = container_of(work, struct msm_vidc_core, ssr_work);
if (!core || !core->device) {
dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
return;
}
hdev = core->device;
mutex_lock(&core->lock);
if (core->state == VIDC_CORE_INIT_DONE) {
dprintk(VIDC_ERR, "%s: ssr type %d\n", __func__,
core->ssr_type);
/*
* In current implementation user-initiated SSR triggers
* a fatal error from hardware. However, there is no way
* to know if fatal error is due to SSR or not. Handle
* user SSR as non-fatal.
*/
core->trigger_ssr = true;
rc = call_hfi_op(hdev, core_trigger_ssr,
hdev->hfi_device_data, core->ssr_type);
if (rc) {
dprintk(VIDC_ERR, "%s: trigger_ssr failed\n",
__func__);
core->trigger_ssr = false;
}
} else {
dprintk(VIDC_ERR, "%s: video core %pK not initialized\n",
__func__, core);
}
mutex_unlock(&core->lock);
}
static int msm_vidc_check_mbpf_supported(struct msm_vidc_inst *inst)
{
u32 mbpf = 0;
struct msm_vidc_core *core;
struct msm_vidc_inst *temp;
if (!inst || !inst->core) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return -EINVAL;
}
core = inst->core;
if (!core->resources.max_mbpf) {
dprintk(VIDC_HIGH, "%s: max mbpf not available\n",
__func__);
return 0;
}
mutex_lock(&core->lock);
list_for_each_entry(temp, &core->instances, list) {
/* ignore invalid session */
if (temp->state == MSM_VIDC_CORE_INVALID)
continue;
/* ignore thumbnail session */
if (is_thumbnail_session(temp))
continue;
mbpf += NUM_MBS_PER_FRAME(
temp->fmts[INPUT_PORT].v4l2_fmt.fmt.pix_mp.height,
temp->fmts[INPUT_PORT].v4l2_fmt.fmt.pix_mp.width);
}
mutex_unlock(&core->lock);
if (mbpf > core->resources.max_mbpf) {
msm_vidc_print_running_insts(inst->core);
return -EBUSY;
}
return 0;
}
static int msm_vidc_check_mbps_supported(struct msm_vidc_inst *inst)
{
int num_mbs_per_sec = 0, max_load_adj = 0;
enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD |
LOAD_CALC_IGNORE_THUMBNAIL_LOAD |
LOAD_CALC_IGNORE_NON_REALTIME_LOAD;
if (inst->state == MSM_VIDC_OPEN_DONE) {
max_load_adj = inst->core->resources.max_load;
num_mbs_per_sec = msm_comm_get_load(inst->core,
MSM_VIDC_DECODER, quirks);
num_mbs_per_sec += msm_comm_get_load(inst->core,
MSM_VIDC_ENCODER, quirks);
if (num_mbs_per_sec > max_load_adj) {
dprintk(VIDC_ERR,
"H/W is overloaded. needed: %d max: %d\n",
num_mbs_per_sec,
max_load_adj);
msm_vidc_print_running_insts(inst->core);
return -EBUSY;
}
}
return 0;
}
int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst)
{
u32 x_min, x_max, y_min, y_max;
u32 input_height, input_width, output_height, output_width;
struct v4l2_format *f;
if (inst->grid_enable > 0) {
dprintk(VIDC_HIGH, "Skip scaling check for HEIC\n");
return 0;
}
f = &inst->fmts[INPUT_PORT].v4l2_fmt;
input_height = f->fmt.pix_mp.height;
input_width = f->fmt.pix_mp.width;
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
output_height = f->fmt.pix_mp.height;
output_width = f->fmt.pix_mp.width;
if (!input_height || !input_width || !output_height || !output_width) {
dprintk(VIDC_ERR,
"Invalid : Input height = %d width = %d",
input_height, input_width);
dprintk(VIDC_ERR,
" output height = %d width = %d\n",
output_height, output_width);
return -ENOTSUPP;
}
if (!inst->capability.cap[CAP_SCALE_X].min ||
!inst->capability.cap[CAP_SCALE_X].max ||
!inst->capability.cap[CAP_SCALE_Y].min ||
!inst->capability.cap[CAP_SCALE_Y].max) {
if (input_width * input_height !=
output_width * output_height) {
dprintk(VIDC_ERR,
"%s: scaling is not supported (%dx%d != %dx%d)\n",
__func__, input_width, input_height,
output_width, output_height);
return -ENOTSUPP;
}
dprintk(VIDC_HIGH, "%s: supported WxH = %dx%d\n",
__func__, input_width, input_height);
return 0;
}
x_min = (1<<16)/inst->capability.cap[CAP_SCALE_X].min;
y_min = (1<<16)/inst->capability.cap[CAP_SCALE_Y].min;
x_max = inst->capability.cap[CAP_SCALE_X].max >> 16;
y_max = inst->capability.cap[CAP_SCALE_Y].max >> 16;
if (input_height > output_height) {
if (input_height > x_min * output_height) {
dprintk(VIDC_ERR,
"Unsupported height min height %d vs %d\n",
input_height / x_min, output_height);
return -ENOTSUPP;
}
} else {
if (output_height > x_max * input_height) {
dprintk(VIDC_ERR,
"Unsupported height max height %d vs %d\n",
x_max * input_height, output_height);
return -ENOTSUPP;
}
}
if (input_width > output_width) {
if (input_width > y_min * output_width) {
dprintk(VIDC_ERR,
"Unsupported width min width %d vs %d\n",
input_width / y_min, output_width);
return -ENOTSUPP;
}
} else {
if (output_width > y_max * input_width) {
dprintk(VIDC_ERR,
"Unsupported width max width %d vs %d\n",
y_max * input_width, output_width);
return -ENOTSUPP;
}
}
return 0;
}
int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
{
struct msm_vidc_capability *capability;
int rc = 0;
struct hfi_device *hdev;
struct msm_vidc_core *core;
u32 output_height, output_width, input_height, input_width;
u32 width_min, width_max, height_min, height_max;
u32 mbpf_max;
struct v4l2_format *f;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s: Invalid parameter\n", __func__);
return -EINVAL;
}
capability = &inst->capability;
hdev = inst->core->device;
core = inst->core;
rc = msm_vidc_check_mbps_supported(inst);
if (rc) {
dprintk(VIDC_ERR,
"%s: Hardware is overloaded\n", __func__);
return rc;
}
rc = msm_vidc_check_mbpf_supported(inst);
if (rc)
return rc;
if (!is_thermal_permissible(core)) {
dprintk(VIDC_ERR,
"Thermal level critical, stop all active sessions!\n");
return -ENOTSUPP;
}
if (is_secure_session(inst)) {
width_min = capability->cap[CAP_SECURE_FRAME_WIDTH].min;
width_max = capability->cap[CAP_SECURE_FRAME_WIDTH].max;
height_min = capability->cap[CAP_SECURE_FRAME_HEIGHT].min;
height_max = capability->cap[CAP_SECURE_FRAME_HEIGHT].max;
mbpf_max = capability->cap[CAP_SECURE_MBS_PER_FRAME].max;
} else {
width_min = capability->cap[CAP_FRAME_WIDTH].min;
width_max = capability->cap[CAP_FRAME_WIDTH].max;
height_min = capability->cap[CAP_FRAME_HEIGHT].min;
height_max = capability->cap[CAP_FRAME_HEIGHT].max;
mbpf_max = capability->cap[CAP_MBS_PER_FRAME].max;
}
f = &inst->fmts[OUTPUT_PORT].v4l2_fmt;
output_height = f->fmt.pix_mp.height;
output_width = f->fmt.pix_mp.width;
f = &inst->fmts[INPUT_PORT].v4l2_fmt;
input_height = f->fmt.pix_mp.height;
input_width = f->fmt.pix_mp.width;
if (inst->session_type == MSM_VIDC_ENCODER && (input_width % 2 != 0 ||
input_height % 2 != 0 || output_width % 2 != 0 ||
output_height % 2 != 0)) {
dprintk(VIDC_ERR,
"Height and Width should be even numbers for NV12\n");
dprintk(VIDC_ERR,
"Input WxH = (%u)x(%u), Output WxH = (%u)x(%u)\n",
input_width, input_height,
output_width, output_height);
rc = -ENOTSUPP;
}
output_height = ALIGN(output_height, 16);
output_width = ALIGN(output_width, 16);
if (!rc) {
if (output_width < width_min ||
output_height < height_min) {
dprintk(VIDC_ERR,
"Unsupported WxH = (%u)x(%u), min supported is - (%u)x(%u)\n",
output_width, output_height,
width_min, height_min);
rc = -ENOTSUPP;
}
if (!rc && output_width > width_max) {
dprintk(VIDC_ERR,
"Unsupported width = %u supported max width = %u\n",
output_width, width_max);
rc = -ENOTSUPP;
}
if (!rc && output_height * output_width >
width_max * height_max) {
dprintk(VIDC_ERR,
"Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n",
output_width, output_height,
width_max, height_max);
rc = -ENOTSUPP;
}
if (!rc && NUM_MBS_PER_FRAME(input_width, input_height) >
mbpf_max) {
dprintk(VIDC_ERR, "Unsupported mbpf %d, max %d\n",
NUM_MBS_PER_FRAME(input_width, input_height),
mbpf_max);
rc = -ENOTSUPP;
}
}
if (rc) {
dprintk(VIDC_ERR,
"%s: Resolution unsupported\n", __func__);
}
return rc;
}
void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
{
enum hal_command_response cmd = HAL_SESSION_ERROR;
struct msm_vidc_cb_cmd_done response = {0};
if (!inst || !inst->core) {
dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return;
}
dprintk(VIDC_ERR, "%s: inst %pK\n", __func__, inst);
response.session_id = inst;
response.status = VIDC_ERR_FAIL;
handle_session_error(cmd, (void *)&response);
}
void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
{
struct msm_vidc_core *core;
enum hal_command_response cmd = HAL_SYS_ERROR;
struct msm_vidc_cb_cmd_done response = {0};
if (!inst || !inst->core) {
dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return;
}
dprintk(VIDC_ERR, "%s: inst %pK\n", __func__, inst);
core = inst->core;
response.device_id = (u32) core->id;
handle_sys_error(cmd, (void *) &response);
}
int msm_comm_kill_session(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return -EINVAL;
} else if (!inst->session) {
dprintk(VIDC_ERR, "%s: no session to kill for inst %pK\n",
__func__, inst);
return 0;
}
dprintk(VIDC_ERR, "%s: inst %pK, session %x state %d\n", __func__,
inst, hash32_ptr(inst->session), inst->state);
/*
* We're internally forcibly killing the session, if fw is aware of
* the session send session_abort to firmware to clean up and release
* the session, else just kill the session inside the driver.
*/
if ((inst->state >= MSM_VIDC_OPEN_DONE &&
inst->state < MSM_VIDC_CLOSE_DONE) ||
inst->state == MSM_VIDC_CORE_INVALID) {
rc = msm_comm_session_abort(inst);
if (rc) {
dprintk(VIDC_ERR,
"%s: inst %pK session %x abort failed\n",
__func__, inst, hash32_ptr(inst->session));
change_inst_state(inst, MSM_VIDC_CORE_INVALID);
}
}
change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
msm_comm_session_clean(inst);
dprintk(VIDC_ERR, "%s: inst %pK session %x handled\n", __func__,
inst, hash32_ptr(inst->session));
return rc;
}
int msm_comm_smem_alloc(struct msm_vidc_inst *inst,
size_t size, u32 align, u32 flags, enum hal_buffer buffer_type,
int map_kernel, struct msm_smem *smem)
{
int rc = 0;
if (!inst || !inst->core) {
dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst);
return -EINVAL;
}
rc = msm_smem_alloc(size, align, flags, buffer_type, map_kernel,
&(inst->core->resources), inst->session_type,
smem);
return rc;
}
void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem)
{
if (!inst || !inst->core || !mem) {
dprintk(VIDC_ERR,
"%s: invalid params: %pK %pK\n", __func__, inst, mem);
return;
}
msm_smem_free(mem);
}
void msm_vidc_fw_unload_handler(struct work_struct *work)
{
struct msm_vidc_core *core = NULL;
struct hfi_device *hdev = NULL;
int rc = 0;
core = container_of(work, struct msm_vidc_core, fw_unload_work.work);
if (!core || !core->device) {
dprintk(VIDC_ERR, "%s - invalid work or core handle\n",
__func__);
return;
}
hdev = core->device;
mutex_lock(&core->lock);
if (list_empty(&core->instances) &&
core->state != VIDC_CORE_UNINIT) {
if (core->state > VIDC_CORE_INIT) {
dprintk(VIDC_HIGH, "Calling vidc_hal_core_release\n");
rc = call_hfi_op(hdev, core_release,
hdev->hfi_device_data);
if (rc) {
dprintk(VIDC_ERR,
"Failed to release core, id = %d\n",
core->id);
mutex_unlock(&core->lock);
return;
}
}
core->state = VIDC_CORE_UNINIT;
kfree(core->capabilities);
core->capabilities = NULL;
}
mutex_unlock(&core->lock);
}
int msm_comm_set_color_format(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type, int fourcc)
{
struct hfi_uncompressed_format_select hfi_fmt = {0};
u32 format = HFI_COLOR_FORMAT_NV12_UBWC;
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
format = msm_comm_get_hfi_uncompressed(fourcc);
hfi_fmt.buffer_type = get_hfi_buffer(buffer_type);
hfi_fmt.format = format;
rc = call_hfi_op(hdev, session_set_property, inst->session,
HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT, &hfi_fmt,
sizeof(hfi_fmt));
if (rc)
dprintk(VIDC_ERR,
"Failed to set input color format\n");
else
dprintk(VIDC_HIGH, "Setting uncompressed colorformat to %#x\n",
format);
return rc;
}
void msm_comm_print_inst_info(struct msm_vidc_inst *inst)
{
struct msm_vidc_buffer *mbuf;
struct internal_buf *buf;
bool is_decode = false;
enum vidc_ports port;
bool is_secure = false;
struct v4l2_format *f;
if (!inst) {
dprintk(VIDC_ERR, "%s - invalid param %pK\n",
__func__, inst);
return;
}
is_decode = inst->session_type == MSM_VIDC_DECODER;
port = is_decode ? INPUT_PORT : OUTPUT_PORT;
is_secure = inst->flags & VIDC_SECURE;
f = &inst->fmts[port].v4l2_fmt;
dprintk(VIDC_ERR,
"%s session, %s, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n",
is_decode ? "Decode" : "Encode",
is_secure ? "Secure" : "Non-Secure",
inst->fmts[port].name,
f->fmt.pix_mp.height, f->fmt.pix_mp.width,
inst->clk_data.frame_rate >> 16, inst->prop.bitrate,
!inst->bit_depth ? "8" : "10");
dprintk(VIDC_ERR,
"---Buffer details for inst: %pK of type: %d---\n",
inst, inst->session_type);
mutex_lock(&inst->registeredbufs.lock);
dprintk(VIDC_ERR, "registered buffer list:\n");
list_for_each_entry(mbuf, &inst->registeredbufs.list, list)
print_vidc_buffer(VIDC_ERR, "buf", inst, mbuf);
mutex_unlock(&inst->registeredbufs.lock);
mutex_lock(&inst->scratchbufs.lock);
dprintk(VIDC_ERR, "scratch buffer list:\n");
list_for_each_entry(buf, &inst->scratchbufs.list, list)
dprintk(VIDC_ERR, "type: %d addr: %x size: %u\n",
buf->buffer_type, buf->smem.device_addr,
buf->smem.size);
mutex_unlock(&inst->scratchbufs.lock);
mutex_lock(&inst->persistbufs.lock);
dprintk(VIDC_ERR, "persist buffer list:\n");
list_for_each_entry(buf, &inst->persistbufs.list, list)
dprintk(VIDC_ERR, "type: %d addr: %x size: %u\n",
buf->buffer_type, buf->smem.device_addr,
buf->smem.size);
mutex_unlock(&inst->persistbufs.lock);
mutex_lock(&inst->outputbufs.lock);
dprintk(VIDC_ERR, "dpb buffer list:\n");
list_for_each_entry(buf, &inst->outputbufs.list, list)
dprintk(VIDC_ERR, "type: %d addr: %x size: %u\n",
buf->buffer_type, buf->smem.device_addr,
buf->smem.size);
mutex_unlock(&inst->outputbufs.lock);
}
int msm_comm_session_continue(void *instance)
{
struct msm_vidc_inst *inst = instance;
int rc = 0;
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device)
return -EINVAL;
hdev = inst->core->device;
mutex_lock(&inst->lock);
if (inst->state >= MSM_VIDC_RELEASE_RESOURCES_DONE ||
inst->state < MSM_VIDC_START_DONE) {
dprintk(VIDC_HIGH,
"Inst %pK : Not in valid state to call %s\n",
inst, __func__);
goto sess_continue_fail;
}
if (inst->session_type == MSM_VIDC_DECODER && inst->in_reconfig) {
dprintk(VIDC_HIGH, "send session_continue\n");
rc = call_hfi_op(hdev, session_continue,
(void *)inst->session);
if (rc) {
dprintk(VIDC_ERR,
"failed to send session_continue\n");
rc = -EINVAL;
goto sess_continue_fail;
}
inst->in_reconfig = false;
if (msm_comm_get_stream_output_mode(inst) ==
HAL_VIDEO_DECODER_SECONDARY) {
rc = msm_comm_queue_dpb_only_buffers(inst);
if (rc) {
dprintk(VIDC_ERR,
"Failed to queue output buffers: %d\n",
rc);
goto sess_continue_fail;
}
}
} else if (inst->session_type == MSM_VIDC_ENCODER) {
dprintk(VIDC_HIGH,
"session_continue not supported for encoder");
} else {
dprintk(VIDC_ERR,
"session_continue called in wrong state for decoder");
}
sess_continue_fail:
mutex_unlock(&inst->lock);
return rc;
}
void print_vidc_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
struct vb2_buffer *vb2 = NULL;
if (!(tag & msm_vidc_debug) || !inst || !mbuf)
return;
vb2 = &mbuf->vvb.vb2_buf;
if (vb2->num_planes == 1)
dprintk(tag,
"%s: %s: %x : idx %2d fd %d off %d daddr %x size %d filled %d flags 0x%x ts %lld refcnt %d mflags 0x%x\n",
str, vb2->type == INPUT_MPLANE ?
"OUTPUT" : "CAPTURE", hash32_ptr(inst->session),
vb2->index, vb2->planes[0].m.fd,
vb2->planes[0].data_offset, mbuf->smem[0].device_addr,
vb2->planes[0].length, vb2->planes[0].bytesused,
mbuf->vvb.flags, mbuf->vvb.vb2_buf.timestamp,
mbuf->smem[0].refcount, mbuf->flags);
else
dprintk(tag,
"%s: %s: %x : idx %2d fd %d off %d daddr %x size %d filled %d flags 0x%x ts %lld refcnt %d mflags 0x%x, extradata: fd %d off %d daddr %x size %d filled %d refcnt %d\n",
str, vb2->type == INPUT_MPLANE ?
"OUTPUT" : "CAPTURE", hash32_ptr(inst->session),
vb2->index, vb2->planes[0].m.fd,
vb2->planes[0].data_offset, mbuf->smem[0].device_addr,
vb2->planes[0].length, vb2->planes[0].bytesused,
mbuf->vvb.flags, mbuf->vvb.vb2_buf.timestamp,
mbuf->smem[0].refcount, mbuf->flags,
vb2->planes[1].m.fd, vb2->planes[1].data_offset,
mbuf->smem[1].device_addr, vb2->planes[1].length,
vb2->planes[1].bytesused, mbuf->smem[1].refcount);
}
void print_vb2_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst,
struct vb2_buffer *vb2)
{
if (!(tag & msm_vidc_debug) || !inst || !vb2)
return;
if (vb2->num_planes == 1)
dprintk(tag,
"%s: %s: %x : idx %2d fd %d off %d size %d filled %d\n",
str, vb2->type == INPUT_MPLANE ?
"OUTPUT" : "CAPTURE", hash32_ptr(inst->session),
vb2->index, vb2->planes[0].m.fd,
vb2->planes[0].data_offset, vb2->planes[0].length,
vb2->planes[0].bytesused);
else
dprintk(tag,
"%s: %s: %x : idx %2d fd %d off %d size %d filled %d, extradata: fd %d off %d size %d filled %d\n",
str, vb2->type == INPUT_MPLANE ?
"OUTPUT" : "CAPTURE", hash32_ptr(inst->session),
vb2->index, vb2->planes[0].m.fd,
vb2->planes[0].data_offset, vb2->planes[0].length,
vb2->planes[0].bytesused, vb2->planes[1].m.fd,
vb2->planes[1].data_offset, vb2->planes[1].length,
vb2->planes[1].bytesused);
}
void print_v4l2_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst,
struct v4l2_buffer *v4l2)
{
if (!(tag & msm_vidc_debug) || !inst || !v4l2)
return;
if (v4l2->length == 1)
dprintk(tag,
"%s: %s: %x : idx %2d fd %d off %d size %d filled %d\n",
str, v4l2->type == INPUT_MPLANE ?
"OUTPUT" : "CAPTURE", hash32_ptr(inst->session),
v4l2->index, v4l2->m.planes[0].m.fd,
v4l2->m.planes[0].data_offset,
v4l2->m.planes[0].length,
v4l2->m.planes[0].bytesused);
else
dprintk(tag,
"%s: %s: %x : idx %2d fd %d off %d size %d filled %d, extradata: fd %d off %d size %d filled %d\n",
str, v4l2->type == INPUT_MPLANE ?
"OUTPUT" : "CAPTURE", hash32_ptr(inst->session),
v4l2->index, v4l2->m.planes[0].m.fd,
v4l2->m.planes[0].data_offset,
v4l2->m.planes[0].length,
v4l2->m.planes[0].bytesused,
v4l2->m.planes[1].m.fd,
v4l2->m.planes[1].data_offset,
v4l2->m.planes[1].length,
v4l2->m.planes[1].bytesused);
}
bool msm_comm_compare_vb2_plane(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf, struct vb2_buffer *vb2, u32 i)
{
struct vb2_buffer *vb;
if (!inst || !mbuf || !vb2) {
dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n",
__func__, inst, mbuf, vb2);
return false;
}
vb = &mbuf->vvb.vb2_buf;
if (vb->planes[i].m.fd == vb2->planes[i].m.fd &&
vb->planes[i].length == vb2->planes[i].length) {
return true;
}
return false;
}
bool msm_comm_compare_vb2_planes(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf, struct vb2_buffer *vb2)
{
unsigned int i = 0;
struct vb2_buffer *vb;
if (!inst || !mbuf || !vb2) {
dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n",
__func__, inst, mbuf, vb2);
return false;
}
vb = &mbuf->vvb.vb2_buf;
if (vb->num_planes != vb2->num_planes)
return false;
for (i = 0; i < vb->num_planes; i++) {
if (!msm_comm_compare_vb2_plane(inst, mbuf, vb2, i))
return false;
}
return true;
}
bool msm_comm_compare_dma_plane(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf, unsigned long *dma_planes, u32 i)
{
if (!inst || !mbuf || !dma_planes) {
dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n",
__func__, inst, mbuf, dma_planes);
return false;
}
if ((unsigned long)mbuf->smem[i].dma_buf == dma_planes[i])
return true;
return false;
}
bool msm_comm_compare_dma_planes(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf, unsigned long *dma_planes)
{
unsigned int i = 0;
struct vb2_buffer *vb;
if (!inst || !mbuf || !dma_planes) {
dprintk(VIDC_ERR, "%s: invalid params, %pK %pK %pK\n",
__func__, inst, mbuf, dma_planes);
return false;
}
vb = &mbuf->vvb.vb2_buf;
for (i = 0; i < vb->num_planes; i++) {
if (!msm_comm_compare_dma_plane(inst, mbuf, dma_planes, i))
return false;
}
return true;
}
bool msm_comm_compare_device_plane(struct msm_vidc_buffer *mbuf,
u32 type, u32 *planes, u32 i)
{
if (!mbuf || !planes) {
dprintk(VIDC_ERR, "%s: invalid params, %pK %pK\n",
__func__, mbuf, planes);
return false;
}
if (mbuf->vvb.vb2_buf.type == type &&
mbuf->smem[i].device_addr == planes[i])
return true;
return false;
}
bool msm_comm_compare_device_planes(struct msm_vidc_buffer *mbuf,
u32 type, u32 *planes)
{
unsigned int i = 0;
if (!mbuf || !planes)
return false;
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) {
if (!msm_comm_compare_device_plane(mbuf, type, planes, i))
return false;
}
return true;
}
struct msm_vidc_buffer *msm_comm_get_buffer_using_device_planes(
struct msm_vidc_inst *inst, u32 type, u32 *planes)
{
struct msm_vidc_buffer *mbuf;
bool found = false;
mutex_lock(&inst->registeredbufs.lock);
found = false;
list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
if (msm_comm_compare_device_planes(mbuf, type, planes)) {
found = true;
break;
}
}
mutex_unlock(&inst->registeredbufs.lock);
if (!found) {
dprintk(VIDC_ERR,
"%s: data_addr %x, extradata_addr %x not found\n",
__func__, planes[0], planes[1]);
mbuf = NULL;
}
return mbuf;
}
int msm_comm_flush_vidc_buffer(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
struct vb2_buffer *vb;
u32 port;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, inst, mbuf);
return -EINVAL;
}
vb = msm_comm_get_vb_using_vidc_buffer(inst, mbuf);
if (!vb) {
print_vidc_buffer(VIDC_ERR,
"vb not found for buf", inst, mbuf);
return -EINVAL;
}
if (mbuf->vvb.vb2_buf.type == OUTPUT_MPLANE)
port = OUTPUT_PORT;
else if (mbuf->vvb.vb2_buf.type == INPUT_MPLANE)
port = INPUT_PORT;
else
return -EINVAL;
mutex_lock(&inst->bufq[port].lock);
if (inst->bufq[port].vb2_bufq.streaming) {
vb->planes[0].bytesused = 0;
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
} else {
dprintk(VIDC_ERR, "%s: port %d is not streaming\n",
__func__, port);
}
mutex_unlock(&inst->bufq[port].lock);
return 0;
}
int msm_comm_qbuf_cache_operations(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
unsigned int i;
struct vb2_buffer *vb;
bool skip;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, inst, mbuf);
return -EINVAL;
}
vb = &mbuf->vvb.vb2_buf;
for (i = 0; i < vb->num_planes; i++) {
unsigned long offset, size;
enum smem_cache_ops cache_op;
skip = true;
if (inst->session_type == MSM_VIDC_DECODER) {
if (vb->type == INPUT_MPLANE) {
if (!i) { /* bitstream */
skip = false;
offset = vb->planes[i].data_offset;
size = vb->planes[i].bytesused;
cache_op = SMEM_CACHE_CLEAN_INVALIDATE;
}
} else if (vb->type == OUTPUT_MPLANE) {
if (!i) { /* yuv */
skip = false;
offset = 0;
size = vb->planes[i].length;
cache_op = SMEM_CACHE_INVALIDATE;
}
}
} else if (inst->session_type == MSM_VIDC_ENCODER) {
if (vb->type == INPUT_MPLANE) {
if (!i) { /* yuv */
skip = false;
offset = vb->planes[i].data_offset;
size = vb->planes[i].bytesused;
cache_op = SMEM_CACHE_CLEAN_INVALIDATE;
}
} else if (vb->type == OUTPUT_MPLANE) {
if (!i) { /* bitstream */
u32 size_u32;
skip = false;
offset = 0;
size_u32 = vb->planes[i].length;
msm_comm_fetch_filled_length(
&inst->fbd_data, vb->index,
&size_u32);
size = size_u32;
cache_op = SMEM_CACHE_INVALIDATE;
}
}
}
if (!skip) {
rc = msm_smem_cache_operations(mbuf->smem[i].dma_buf,
cache_op, offset, size);
if (rc)
print_vidc_buffer(VIDC_ERR,
"qbuf cache ops failed", inst, mbuf);
}
}
return rc;
}
int msm_comm_dqbuf_cache_operations(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
unsigned int i;
struct vb2_buffer *vb;
bool skip;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, inst, mbuf);
return -EINVAL;
}
vb = &mbuf->vvb.vb2_buf;
for (i = 0; i < vb->num_planes; i++) {
unsigned long offset, size;
enum smem_cache_ops cache_op;
skip = true;
if (inst->session_type == MSM_VIDC_DECODER) {
if (vb->type == INPUT_MPLANE) {
/* bitstream and extradata */
/* we do not need cache operations */
} else if (vb->type == OUTPUT_MPLANE) {
if (!i) { /* yuv */
skip = false;
offset = vb->planes[i].data_offset;
size = vb->planes[i].bytesused;
cache_op = SMEM_CACHE_INVALIDATE;
}
}
} else if (inst->session_type == MSM_VIDC_ENCODER) {
if (vb->type == INPUT_MPLANE) {
/* yuv and extradata */
/* we do not need cache operations */
} else if (vb->type == OUTPUT_MPLANE) {
if (!i) { /* bitstream */
skip = false;
/*
* Include vp8e header bytes as well
* by making offset equal to zero
*/
offset = 0;
size = vb->planes[i].bytesused +
vb->planes[i].data_offset;
cache_op = SMEM_CACHE_INVALIDATE;
}
}
}
if (!skip) {
rc = msm_smem_cache_operations(mbuf->smem[i].dma_buf,
cache_op, offset, size);
if (rc)
print_vidc_buffer(VIDC_ERR,
"dqbuf cache ops failed", inst, mbuf);
}
}
return rc;
}
struct msm_vidc_buffer *msm_comm_get_vidc_buffer(struct msm_vidc_inst *inst,
struct vb2_buffer *vb2)
{
int rc = 0;
struct vb2_v4l2_buffer *vbuf;
struct vb2_buffer *vb;
unsigned long dma_planes[VB2_MAX_PLANES] = {0};
struct msm_vidc_buffer *mbuf;
bool found = false;
unsigned int i;
if (!inst || !vb2) {
dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
return NULL;
}
for (i = 0; i < vb2->num_planes; i++) {
/*
* always compare dma_buf addresses which is guaranteed
* to be same across the processes (duplicate fds).
*/
dma_planes[i] = (unsigned long)msm_smem_get_dma_buf(
vb2->planes[i].m.fd);
if (!dma_planes[i])
return NULL;
msm_smem_put_dma_buf((struct dma_buf *)dma_planes[i]);
}
mutex_lock(&inst->registeredbufs.lock);
/*
* for encoder input, client may queue the same buffer with different
* fd before driver returned old buffer to the client. This buffer
* should be treated as new buffer Search the list with fd so that
* it will be treated as new msm_vidc_buffer.
*/
if (is_encode_session(inst) && vb2->type == INPUT_MPLANE) {
list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
if (msm_comm_compare_vb2_planes(inst, mbuf, vb2)) {
found = true;
break;
}
}
} else {
list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
if (msm_comm_compare_dma_planes(inst, mbuf,
dma_planes)) {
found = true;
break;
}
}
}
if (!found) {
/* this is new vb2_buffer */
mbuf = kzalloc(sizeof(struct msm_vidc_buffer), GFP_KERNEL);
if (!mbuf) {
dprintk(VIDC_ERR, "%s: alloc msm_vidc_buffer failed\n",
__func__);
rc = -ENOMEM;
goto exit;
}
kref_init(&mbuf->kref);
}
/* Initially assume all the buffer are going to be deferred */
mbuf->flags |= MSM_VIDC_FLAG_DEFERRED;
vbuf = to_vb2_v4l2_buffer(vb2);
memcpy(&mbuf->vvb, vbuf, sizeof(struct vb2_v4l2_buffer));
vb = &mbuf->vvb.vb2_buf;
for (i = 0; i < vb->num_planes; i++) {
mbuf->smem[i].buffer_type = get_hal_buffer_type(vb->type, i);
mbuf->smem[i].fd = vb->planes[i].m.fd;
mbuf->smem[i].offset = vb->planes[i].data_offset;
mbuf->smem[i].size = vb->planes[i].length;
rc = inst->smem_ops->smem_map_dma_buf(inst, &mbuf->smem[i]);
if (rc) {
dprintk(VIDC_ERR, "%s: map failed.\n", __func__);
goto exit;
}
/* increase refcount as we get both fbd and rbr */
rc = inst->smem_ops->smem_map_dma_buf(inst, &mbuf->smem[i]);
if (rc) {
dprintk(VIDC_ERR, "%s: map failed..\n", __func__);
goto exit;
}
}
/* dma cache operations need to be performed after dma_map */
msm_comm_qbuf_cache_operations(inst, mbuf);
/* special handling for decoder */
if (inst->session_type == MSM_VIDC_DECODER) {
if (found) {
rc = -EEXIST;
} else {
bool found_plane0 = false;
struct msm_vidc_buffer *temp;
/*
* client might have queued same plane[0] but different
* plane[1] search plane[0] and if found don't queue the
* buffer, the buffer will be queued when rbr event
* arrived.
*/
list_for_each_entry(temp, &inst->registeredbufs.list,
list) {
if (msm_comm_compare_dma_plane(inst, temp,
dma_planes, 0)) {
found_plane0 = true;
break;
}
}
if (found_plane0)
rc = -EEXIST;
}
if (rc == -EEXIST) {
print_vidc_buffer(VIDC_HIGH,
"existing qbuf", inst, mbuf);
/* enable RBR pending */
mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING;
}
}
/* add the new buffer to list */
if (!found)
list_add_tail(&mbuf->list, &inst->registeredbufs.list);
mutex_unlock(&inst->registeredbufs.lock);
/*
* Return mbuf if decode batching is enabled as this buffer
* may trigger queuing full batch to firmware, also this buffer
* will not be queued to firmware while full batch queuing,
* it will be queued when rbr event arrived from firmware.
*/
if (rc == -EEXIST && !inst->batch.enable)
return ERR_PTR(rc);
return mbuf;
exit:
dprintk(VIDC_ERR, "%s: rc %d\n", __func__, rc);
msm_comm_unmap_vidc_buffer(inst, mbuf);
if (!found)
kref_put_mbuf(mbuf);
mutex_unlock(&inst->registeredbufs.lock);
return ERR_PTR(rc);
}
void msm_comm_put_vidc_buffer(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
struct msm_vidc_buffer *temp;
bool found = false;
unsigned int i = 0;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, inst, mbuf);
return;
}
mutex_lock(&inst->registeredbufs.lock);
/* check if mbuf was not removed by any chance */
list_for_each_entry(temp, &inst->registeredbufs.list, list) {
if (msm_comm_compare_vb2_planes(inst, mbuf,
&temp->vvb.vb2_buf)) {
found = true;
break;
}
}
if (!found) {
print_vidc_buffer(VIDC_ERR, "buf was removed", inst, mbuf);
goto unlock;
}
print_vidc_buffer(VIDC_HIGH, "dqbuf", inst, mbuf);
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) {
if (inst->smem_ops->smem_unmap_dma_buf(inst, &mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"dqbuf: unmap failed.", inst, mbuf);
if (!(mbuf->vvb.flags & V4L2_BUF_FLAG_READONLY)) {
/* rbr won't come for this buffer */
if (inst->smem_ops->smem_unmap_dma_buf(inst,
&mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"dqbuf: unmap failed..", inst, mbuf);
} else {
/* RBR event expected */
mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING;
}
}
/*
* remove the entry if plane[0].refcount is zero else
* don't remove as client queued same buffer that's why
* plane[0].refcount is not zero
*/
if (!mbuf->smem[0].refcount) {
list_del(&mbuf->list);
kref_put_mbuf(mbuf);
}
unlock:
mutex_unlock(&inst->registeredbufs.lock);
}
void handle_release_buffer_reference(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
struct msm_vidc_buffer *temp;
bool found = false;
unsigned int i = 0;
u32 planes[VIDEO_MAX_PLANES] = {0};
mutex_lock(&inst->flush_lock);
mutex_lock(&inst->registeredbufs.lock);
found = false;
/* check if mbuf was not removed by any chance */
list_for_each_entry(temp, &inst->registeredbufs.list, list) {
if (msm_comm_compare_vb2_planes(inst, mbuf,
&temp->vvb.vb2_buf)) {
found = true;
break;
}
}
if (found) {
/* save device_addr */
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++)
planes[i] = mbuf->smem[i].device_addr;
/* send RBR event to client */
msm_vidc_queue_rbr_event(inst,
mbuf->vvb.vb2_buf.planes[0].m.fd,
mbuf->vvb.vb2_buf.planes[0].data_offset);
/* clear RBR_PENDING flag */
mbuf->flags &= ~MSM_VIDC_FLAG_RBR_PENDING;
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) {
if (inst->smem_ops->smem_unmap_dma_buf(inst,
&mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"rbr unmap failed.", inst, mbuf);
}
/* refcount is not zero if client queued the same buffer */
if (!mbuf->smem[0].refcount) {
list_del(&mbuf->list);
kref_put_mbuf(mbuf);
mbuf = NULL;
}
} else {
print_vidc_buffer(VIDC_ERR, "mbuf not found", inst, mbuf);
goto unlock;
}
/*
* 1. client might have pushed same planes in which case mbuf will be
* same and refcounts are positive and buffer wouldn't have been
* removed from the registeredbufs list.
* 2. client might have pushed same planes[0] but different planes[1]
* in which case mbuf will be different.
* 3. in either case we can search mbuf->smem[0].device_addr in the list
* and if found queue it to video hw (if not flushing).
*/
found = false;
list_for_each_entry(temp, &inst->registeredbufs.list, list) {
if (msm_comm_compare_device_plane(temp,
OUTPUT_MPLANE, planes, 0)) {
mbuf = temp;
found = true;
break;
}
}
if (!found)
goto unlock;
/* buffer found means client queued the buffer already */
if (inst->in_reconfig || inst->in_flush) {
print_vidc_buffer(VIDC_HIGH, "rbr flush buf", inst, mbuf);
msm_comm_flush_vidc_buffer(inst, mbuf);
msm_comm_unmap_vidc_buffer(inst, mbuf);
/* remove from list */
list_del(&mbuf->list);
kref_put_mbuf(mbuf);
/* don't queue the buffer */
found = false;
}
/* clear required flags as the buffer is going to be queued */
if (found) {
mbuf->flags &= ~MSM_VIDC_FLAG_DEFERRED;
mbuf->flags &= ~MSM_VIDC_FLAG_RBR_PENDING;
}
unlock:
mutex_unlock(&inst->registeredbufs.lock);
if (found) {
rc = msm_comm_qbuf_in_rbr(inst, mbuf);
if (rc)
print_vidc_buffer(VIDC_ERR,
"rbr qbuf failed", inst, mbuf);
}
mutex_unlock(&inst->flush_lock);
}
int msm_comm_unmap_vidc_buffer(struct msm_vidc_inst *inst,
struct msm_vidc_buffer *mbuf)
{
int rc = 0;
unsigned int i;
if (!inst || !mbuf) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, inst, mbuf);
return -EINVAL;
}
if (mbuf->vvb.vb2_buf.num_planes > VIDEO_MAX_PLANES) {
dprintk(VIDC_ERR, "%s: invalid num_planes %d\n", __func__,
mbuf->vvb.vb2_buf.num_planes);
return -EINVAL;
}
for (i = 0; i < mbuf->vvb.vb2_buf.num_planes; i++) {
u32 refcount = mbuf->smem[i].refcount;
while (refcount) {
if (inst->smem_ops->smem_unmap_dma_buf(inst,
&mbuf->smem[i]))
print_vidc_buffer(VIDC_ERR,
"unmap failed for buf", inst, mbuf);
refcount--;
}
}
return rc;
}
static void kref_free_mbuf(struct kref *kref)
{
struct msm_vidc_buffer *mbuf = container_of(kref,
struct msm_vidc_buffer, kref);
kfree(mbuf);
}
void kref_put_mbuf(struct msm_vidc_buffer *mbuf)
{
if (!mbuf)
return;
kref_put(&mbuf->kref, kref_free_mbuf);
}
bool kref_get_mbuf(struct msm_vidc_inst *inst, struct msm_vidc_buffer *mbuf)
{
struct msm_vidc_buffer *temp;
bool matches = false;
bool ret = false;
if (!inst || !mbuf)
return false;
mutex_lock(&inst->registeredbufs.lock);
list_for_each_entry(temp, &inst->registeredbufs.list, list) {
if (temp == mbuf) {
matches = true;
break;
}
}
ret = (matches && kref_get_unless_zero(&mbuf->kref)) ? true : false;
mutex_unlock(&inst->registeredbufs.lock);
return ret;
}
void msm_comm_store_filled_length(struct msm_vidc_list *data_list,
u32 index, u32 filled_length)
{
struct msm_vidc_buf_data *pdata = NULL;
bool found = false;
if (!data_list) {
dprintk(VIDC_ERR, "%s: invalid params %pK\n",
__func__, data_list);
return;
}
mutex_lock(&data_list->lock);
list_for_each_entry(pdata, &data_list->list, list) {
if (pdata->index == index) {
pdata->filled_length = filled_length;
found = true;
break;
}
}
if (!found) {
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dprintk(VIDC_ERR, "%s: malloc failure.\n", __func__);
goto exit;
}
pdata->index = index;
pdata->filled_length = filled_length;
list_add_tail(&pdata->list, &data_list->list);
}
exit:
mutex_unlock(&data_list->lock);
}
void msm_comm_fetch_filled_length(struct msm_vidc_list *data_list,
u32 index, u32 *filled_length)
{
struct msm_vidc_buf_data *pdata = NULL;
if (!data_list || !filled_length) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK\n",
__func__, data_list, filled_length);
return;
}
mutex_lock(&data_list->lock);
list_for_each_entry(pdata, &data_list->list, list) {
if (pdata->index == index) {
*filled_length = pdata->filled_length;
break;
}
}
mutex_unlock(&data_list->lock);
}
void msm_comm_store_mark_data(struct msm_vidc_list *data_list,
u32 index, u32 mark_data, u32 mark_target)
{
struct msm_vidc_buf_data *pdata = NULL;
bool found = false;
if (!data_list) {
dprintk(VIDC_ERR, "%s: invalid params %pK\n",
__func__, data_list);
return;
}
mutex_lock(&data_list->lock);
list_for_each_entry(pdata, &data_list->list, list) {
if (pdata->index == index) {
pdata->mark_data = mark_data;
pdata->mark_target = mark_target;
found = true;
break;
}
}
if (!found) {
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dprintk(VIDC_ERR, "%s: malloc failure.\n", __func__);
goto exit;
}
pdata->index = index;
pdata->mark_data = mark_data;
pdata->mark_target = mark_target;
list_add_tail(&pdata->list, &data_list->list);
}
exit:
mutex_unlock(&data_list->lock);
}
void msm_comm_fetch_mark_data(struct msm_vidc_list *data_list,
u32 index, u32 *mark_data, u32 *mark_target)
{
struct msm_vidc_buf_data *pdata = NULL;
if (!data_list || !mark_data || !mark_target) {
dprintk(VIDC_ERR, "%s: invalid params %pK %pK %pK\n",
__func__, data_list, mark_data, mark_target);
return;
}
*mark_data = *mark_target = 0;
mutex_lock(&data_list->lock);
list_for_each_entry(pdata, &data_list->list, list) {
if (pdata->index == index) {
*mark_data = pdata->mark_data;
*mark_target = pdata->mark_target;
/* clear after fetch */
pdata->mark_data = pdata->mark_target = 0;
break;
}
}
mutex_unlock(&data_list->lock);
}
int msm_comm_release_mark_data(struct msm_vidc_inst *inst)
{
struct msm_vidc_buf_data *pdata, *next;
if (!inst) {
dprintk(VIDC_ERR, "%s: invalid params %pK\n",
__func__, inst);
return -EINVAL;
}
mutex_lock(&inst->etb_data.lock);
list_for_each_entry_safe(pdata, next, &inst->etb_data.list, list) {
list_del(&pdata->list);
kfree(pdata);
}
mutex_unlock(&inst->etb_data.lock);
mutex_lock(&inst->fbd_data.lock);
list_for_each_entry_safe(pdata, next, &inst->fbd_data.list, list) {
list_del(&pdata->list);
kfree(pdata);
}
mutex_unlock(&inst->fbd_data.lock);
return 0;
}
int msm_comm_set_color_format_constraints(struct msm_vidc_inst *inst,
enum hal_buffer buffer_type,
struct msm_vidc_format_constraint *pix_constraint)
{
struct hfi_uncompressed_plane_actual_constraints_info
*pconstraint = NULL;
u32 num_planes = 2;
u32 size = 0;
int rc = 0;
struct hfi_device *hdev;
u32 hfi_fmt;
if (!inst || !inst->core || !inst->core->device) {
dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
size = 2 * sizeof(u32)
+ num_planes
* sizeof(struct hfi_uncompressed_plane_constraints);
pconstraint = kzalloc(size, GFP_KERNEL);
if (!pconstraint) {
dprintk(VIDC_ERR, "No memory cannot alloc constrain\n");
rc = -ENOMEM;
goto exit;
}
hfi_fmt = msm_comm_convert_color_fmt(pix_constraint->fourcc);
pconstraint->buffer_type = get_hfi_buffer(buffer_type);
pconstraint->num_planes = pix_constraint->num_planes;
//set Y plan constraints
dprintk(VIDC_HIGH, "Set Y plan constraints.\n");
pconstraint->rg_plane_format[0].stride_multiples =
VENUS_Y_STRIDE(hfi_fmt, 1);
pconstraint->rg_plane_format[0].max_stride =
pix_constraint->y_max_stride;
pconstraint->rg_plane_format[0].min_plane_buffer_height_multiple =
VENUS_Y_SCANLINES(hfi_fmt, 1);
pconstraint->rg_plane_format[0].buffer_alignment =
pix_constraint->y_buffer_alignment;
//set UV plan constraints
dprintk(VIDC_HIGH, "Set UV plan constraints.\n");
pconstraint->rg_plane_format[1].stride_multiples =
VENUS_UV_STRIDE(hfi_fmt, 1);
pconstraint->rg_plane_format[1].max_stride =
pix_constraint->uv_max_stride;
pconstraint->rg_plane_format[1].min_plane_buffer_height_multiple =
VENUS_UV_SCANLINES(hfi_fmt, 1);
pconstraint->rg_plane_format[1].buffer_alignment =
pix_constraint->uv_buffer_alignment;
rc = call_hfi_op(hdev,
session_set_property,
inst->session,
HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
pconstraint,
size);
if (rc)
dprintk(VIDC_ERR,
"Failed to set input color format constraint\n");
else
dprintk(VIDC_HIGH, "Set color format constraint success\n");
exit:
if (!pconstraint)
kfree(pconstraint);
return rc;
}
int msm_comm_set_index_extradata(struct msm_vidc_inst *inst,
uint32_t extradata_id, uint32_t value)
{
int rc = 0;
struct hfi_index_extradata_config extradata;
struct hfi_device *hdev;
hdev = inst->core->device;
extradata.index_extra_data_id = extradata_id;
extradata.enable = value;
rc = call_hfi_op(hdev, session_set_property, (void *)
inst->session, HFI_PROPERTY_PARAM_INDEX_EXTRADATA, &extradata,
sizeof(extradata));
return rc;
}
int msm_comm_set_extradata(struct msm_vidc_inst *inst,
uint32_t extradata_id, uint32_t value)
{
int rc = 0;
struct hfi_index_extradata_config extradata;
struct hfi_device *hdev;
hdev = inst->core->device;
extradata.index_extra_data_id = extradata_id;
extradata.enable = value;
rc = call_hfi_op(hdev, session_set_property, (void *)
inst->session, extradata_id, &extradata,
sizeof(extradata));
return rc;
}
bool msm_comm_check_for_inst_overload(struct msm_vidc_core *core)
{
u32 instance_count = 0;
u32 secure_instance_count = 0;
struct msm_vidc_inst *inst = NULL;
bool overload = false;
mutex_lock(&core->lock);
list_for_each_entry(inst, &core->instances, list) {
instance_count++;
if (inst->flags & VIDC_SECURE)
secure_instance_count++;
}
mutex_unlock(&core->lock);
if (instance_count > core->resources.max_inst_count ||
secure_instance_count > core->resources.max_secure_inst_count) {
overload = true;
dprintk(VIDC_ERR,
"%s: inst_count:%u max_inst:%u sec_inst_count:%u max_sec_inst:%u\n",
__func__, instance_count,
core->resources.max_inst_count, secure_instance_count,
core->resources.max_secure_inst_count);
}
return overload;
}