From 6e7d1f9d7242b6e1a5d5c63a39903f31b8608964 Mon Sep 17 00:00:00 2001 From: Vasantha Balla Date: Mon, 6 Jul 2020 02:51:36 +0530 Subject: [PATCH] vidc_3x: Add snapshot of video driver This is snapshot of the video driver as of msm-4.14(vidc_3x) commit 9dc1a255ba77c95 (" Fix qbuf error in gralloc buffers encoding"). Change-Id: I146b66cb0e0ce44d8414efaa8748c2de87b9fc74 Signed-off-by: Vasantha Balla --- .../configs/vendor/sdm660-perf_defconfig | 2 + arch/arm64/configs/vendor/sdm660_defconfig | 2 + drivers/media/platform/msm/Kconfig | 1 + drivers/media/platform/msm/Makefile | 1 + drivers/media/platform/msm/vidc_3x/Kconfig | 14 + drivers/media/platform/msm/vidc_3x/Makefile | 19 + .../platform/msm/vidc_3x/governors/Kconfig | 7 + .../platform/msm/vidc_3x/governors/Makefile | 6 + .../msm/vidc_3x/governors/fixedpoint.h | 74 + .../msm/vidc_3x/governors/msm_vidc_dyn_gov.c | 1036 ++++ .../vidc_3x/governors/msm_vidc_table_gov.c | 380 ++ .../platform/msm/vidc_3x/hfi_packetization.c | 2568 ++++++++ .../platform/msm/vidc_3x/hfi_packetization.h | 103 + .../msm/vidc_3x/hfi_response_handler.c | 2106 +++++++ drivers/media/platform/msm/vidc_3x/msm_smem.c | 652 ++ .../platform/msm/vidc_3x/msm_v4l2_vidc.c | 835 +++ drivers/media/platform/msm/vidc_3x/msm_vdec.c | 2820 +++++++++ drivers/media/platform/msm/vidc_3x/msm_vdec.h | 38 + drivers/media/platform/msm/vidc_3x/msm_venc.c | 4659 ++++++++++++++ drivers/media/platform/msm/vidc_3x/msm_venc.h | 38 + drivers/media/platform/msm/vidc_3x/msm_vidc.c | 1498 +++++ .../platform/msm/vidc_3x/msm_vidc_common.c | 5325 +++++++++++++++++ .../platform/msm/vidc_3x/msm_vidc_common.h | 104 + .../platform/msm/vidc_3x/msm_vidc_dcvs.c | 704 +++ .../platform/msm/vidc_3x/msm_vidc_dcvs.h | 42 + .../platform/msm/vidc_3x/msm_vidc_debug.c | 540 ++ .../platform/msm/vidc_3x/msm_vidc_debug.h | 178 + .../platform/msm/vidc_3x/msm_vidc_internal.h | 413 ++ .../platform/msm/vidc_3x/msm_vidc_res_parse.c | 1617 +++++ .../platform/msm/vidc_3x/msm_vidc_res_parse.h | 35 + .../platform/msm/vidc_3x/msm_vidc_resources.h | 202 + .../media/platform/msm/vidc_3x/venus_boot.c | 471 ++ .../media/platform/msm/vidc_3x/venus_boot.h | 23 + .../media/platform/msm/vidc_3x/venus_hfi.c | 4663 +++++++++++++++ .../media/platform/msm/vidc_3x/venus_hfi.h | 266 + drivers/media/platform/msm/vidc_3x/vidc_hfi.c | 73 + drivers/media/platform/msm/vidc_3x/vidc_hfi.h | 935 +++ .../media/platform/msm/vidc_3x/vidc_hfi_api.h | 1551 +++++ .../platform/msm/vidc_3x/vidc_hfi_helper.h | 1185 ++++ .../media/platform/msm/vidc_3x/vidc_hfi_io.h | 196 + .../media/platform/msm/vidc_3x/vmem/Kconfig | 4 + .../media/platform/msm/vidc_3x/vmem/Makefile | 3 + .../media/platform/msm/vidc_3x/vmem/vmem.c | 699 +++ .../media/platform/msm/vidc_3x/vmem/vmem.h | 36 + .../platform/msm/vidc_3x/vmem/vmem_debugfs.c | 83 + .../platform/msm/vidc_3x/vmem/vmem_debugfs.h | 23 + gen_headers_arm.bp | 1 + gen_headers_arm64.bp | 1 + include/media/msm_vidc.h | 137 + include/trace/events/msm_vidc.h | 406 ++ include/uapi/linux/v4l2-controls.h | 600 +- include/uapi/linux/videodev2.h | 39 +- include/uapi/media/msm_vidc.h | 446 ++ 53 files changed, 37856 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/msm/vidc_3x/Kconfig create mode 100644 drivers/media/platform/msm/vidc_3x/Makefile create mode 100644 drivers/media/platform/msm/vidc_3x/governors/Kconfig create mode 100644 drivers/media/platform/msm/vidc_3x/governors/Makefile create mode 100644 drivers/media/platform/msm/vidc_3x/governors/fixedpoint.h create mode 100644 drivers/media/platform/msm/vidc_3x/governors/msm_vidc_dyn_gov.c create mode 100644 drivers/media/platform/msm/vidc_3x/governors/msm_vidc_table_gov.c create mode 100644 drivers/media/platform/msm/vidc_3x/hfi_packetization.c create mode 100644 drivers/media/platform/msm/vidc_3x/hfi_packetization.h create mode 100644 drivers/media/platform/msm/vidc_3x/hfi_response_handler.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_smem.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vdec.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vdec.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_venc.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_venc.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_common.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_common.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_debug.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_debug.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.h create mode 100644 drivers/media/platform/msm/vidc_3x/msm_vidc_resources.h create mode 100644 drivers/media/platform/msm/vidc_3x/venus_boot.c create mode 100644 drivers/media/platform/msm/vidc_3x/venus_boot.h create mode 100644 drivers/media/platform/msm/vidc_3x/venus_hfi.c create mode 100644 drivers/media/platform/msm/vidc_3x/venus_hfi.h create mode 100644 drivers/media/platform/msm/vidc_3x/vidc_hfi.c create mode 100644 drivers/media/platform/msm/vidc_3x/vidc_hfi.h create mode 100644 drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h create mode 100644 drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h create mode 100644 drivers/media/platform/msm/vidc_3x/vidc_hfi_io.h create mode 100644 drivers/media/platform/msm/vidc_3x/vmem/Kconfig create mode 100644 drivers/media/platform/msm/vidc_3x/vmem/Makefile create mode 100644 drivers/media/platform/msm/vidc_3x/vmem/vmem.c create mode 100644 drivers/media/platform/msm/vidc_3x/vmem/vmem.h create mode 100644 drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.c create mode 100644 drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.h create mode 100644 include/media/msm_vidc.h create mode 100644 include/trace/events/msm_vidc.h create mode 100644 include/uapi/media/msm_vidc.h diff --git a/arch/arm64/configs/vendor/sdm660-perf_defconfig b/arch/arm64/configs/vendor/sdm660-perf_defconfig index 21670d922c8b..f6180300609a 100644 --- a/arch/arm64/configs/vendor/sdm660-perf_defconfig +++ b/arch/arm64/configs/vendor/sdm660-perf_defconfig @@ -410,6 +410,8 @@ CONFIG_VIDEO_FIXED_MINOR_RANGES=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=y CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_VIDC_3X_GOVERNORS=y +CONFIG_MSM_VIDC_3X_V4L2=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m CONFIG_FB=y diff --git a/arch/arm64/configs/vendor/sdm660_defconfig b/arch/arm64/configs/vendor/sdm660_defconfig index 943fd2e85d66..e5689cc36b45 100644 --- a/arch/arm64/configs/vendor/sdm660_defconfig +++ b/arch/arm64/configs/vendor/sdm660_defconfig @@ -411,6 +411,8 @@ CONFIG_VIDEO_FIXED_MINOR_RANGES=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=y CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_VIDC_3X_GOVERNORS=y +CONFIG_MSM_VIDC_3X_V4L2=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m CONFIG_FB=y diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig index 5760a61e62c2..d1f55d9b5678 100644 --- a/drivers/media/platform/msm/Kconfig +++ b/drivers/media/platform/msm/Kconfig @@ -9,6 +9,7 @@ menuconfig SPECTRA_CAMERA Enabling this adds support for the camera driver stack including sensor, IFE and postprocessing drivers. +source "drivers/media/platform/msm/vidc_3x/Kconfig" source "drivers/media/platform/msm/cvp/Kconfig" source "drivers/media/platform/msm/npu/Kconfig" source "drivers/media/platform/msm/synx/Kconfig" diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile index e82c706e3790..79da991c8414 100644 --- a/drivers/media/platform/msm/Makefile +++ b/drivers/media/platform/msm/Makefile @@ -4,6 +4,7 @@ # Makefile for the qti specific video device drivers # based on V4L2. # +obj-$(CONFIG_MSM_VIDC_3X_V4L2) += vidc_3x/ obj-$(CONFIG_MSM_CVP_V4L2) += cvp/ obj-$(CONFIG_MSM_NPU) += npu/ obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx/ diff --git a/drivers/media/platform/msm/vidc_3x/Kconfig b/drivers/media/platform/msm/vidc_3x/Kconfig new file mode 100644 index 000000000000..3c02230be076 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# VIDEO CORE +# +source "drivers/media/platform/msm/vidc_3x/governors/Kconfig" + +menuconfig MSM_VIDC_3X_V4L2 + tristate "Qualcomm Technologies, Inc. MSM V4L2 3X based video driver" + depends on ARCH_QCOM && VIDEO_V4L2 + select VIDEOBUF2_CORE + help + Enable support of MSM V4L2 3X based video driver for + Qualcomm Technologies, Inc. + diff --git a/drivers/media/platform/msm/vidc_3x/Makefile b/drivers/media/platform/msm/vidc_3x/Makefile new file mode 100644 index 000000000000..c80682441f37 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MSM_VIDC_3X_V4L2) := msm_v4l2_vidc.o \ + msm_vidc_common.o \ + msm_vidc.o \ + msm_vdec.o \ + msm_venc.o \ + msm_smem.o \ + msm_vidc_debug.o \ + msm_vidc_res_parse.o \ + venus_hfi.o \ + hfi_response_handler.o \ + hfi_packetization.o \ + vidc_hfi.o \ + venus_boot.o \ + msm_vidc_dcvs.o + +obj-$(CONFIG_MSM_VIDC_3X_V4L2) += governors/ + +obj-$(CONFIG_MSM_VIDC_VMEM) += vmem/ diff --git a/drivers/media/platform/msm/vidc_3x/governors/Kconfig b/drivers/media/platform/msm/vidc_3x/governors/Kconfig new file mode 100644 index 000000000000..9731389ddb43 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/governors/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig MSM_VIDC_3X_GOVERNORS + tristate "Clock and bandwidth governors for QTI MSM V4L2 based video driver" + depends on MSM_VIDC_3X_V4L2 && PM_DEVFREQ + help + Chooses a set of devfreq governors aimed at providing accurate bandwidth + or clock frequency values for MSM V4L2 video driver. diff --git a/drivers/media/platform/msm/vidc_3x/governors/Makefile b/drivers/media/platform/msm/vidc_3x/governors/Makefile new file mode 100644 index 000000000000..6924008d640e --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/governors/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +ccflags-y := -I$(srctree)/drivers/devfreq/ \ + -I$(srctree)/drivers/media/platform/msm/vidc_3x/ + +obj-$(CONFIG_MSM_VIDC_3X_GOVERNORS) := msm_vidc_dyn_gov.o \ + msm_vidc_table_gov.o diff --git a/drivers/media/platform/msm/vidc_3x/governors/fixedpoint.h b/drivers/media/platform/msm/vidc_3x/governors/fixedpoint.h new file mode 100644 index 000000000000..433d1c5b4876 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/governors/fixedpoint.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015, 2019-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifdef _FIXP_ARITH_H +#error "This implementation is meant to override fixp-arith.h, don't use both" +#endif + +#ifndef __FP_H__ +#define __FP_H__ + +/* + * Normally would typedef'ed, but checkpatch doesn't like typedef. + * Also should be normally typedef'ed to intmax_t but that doesn't seem to be + * available in the kernel + */ +#define fp_t size_t + +/* (Arbitrarily) make the first 25% of the bits to be the fractional bits */ +#define FP_FRACTIONAL_BITS ((sizeof(fp_t) * 8) / 4) + +#define FP(__i, __f_n, __f_d) \ + ((((fp_t)(__i)) << FP_FRACTIONAL_BITS) + \ + (((__f_n) << FP_FRACTIONAL_BITS) / (__f_d))) + +#define FP_INT(__i) FP(__i, 0, 1) +#define FP_ONE FP_INT(1) +#define FP_ZERO FP_INT(0) + +static inline size_t fp_frac_base(void) +{ + return GENMASK(FP_FRACTIONAL_BITS - 1, 0); +} + +static inline size_t fp_frac(fp_t a) +{ + return a & GENMASK(FP_FRACTIONAL_BITS - 1, 0); +} + +static inline size_t fp_int(fp_t a) +{ + return a >> FP_FRACTIONAL_BITS; +} + +static inline size_t fp_round(fp_t a) +{ + /* is the fractional part >= frac_max / 2? */ + bool round_up = fp_frac(a) >= fp_frac_base() / 2; + + return fp_int(a) + round_up; +} + +static inline fp_t fp_mult(fp_t a, fp_t b) +{ + return (a * b) >> FP_FRACTIONAL_BITS; +} + + +static inline fp_t fp_div(fp_t a, fp_t b) +{ + return (a << FP_FRACTIONAL_BITS) / b; +} + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/governors/msm_vidc_dyn_gov.c b/drivers/media/platform/msm/vidc_3x/governors/msm_vidc_dyn_gov.c new file mode 100644 index 000000000000..6cec9a7ca295 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/governors/msm_vidc_dyn_gov.c @@ -0,0 +1,1036 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015, 2017-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "fixedpoint.h" +#include "../msm_vidc_internal.h" +#include "../msm_vidc_debug.h" +#include "../vidc_hfi_api.h" + + +enum vidc_bus_type { + PERF, + DDR, + LLCC, +}; +enum scenario { + SCENARIO_WORST, + SCENARIO_SUSTAINED_WORST, + SCENARIO_AVERAGE, + SCENARIO_MAX, +}; + +/* + * Minimum dimensions for which to calculate bandwidth. + * This means that anything bandwidth(0, 0) == + * bandwidth(BASELINE_DIMENSIONS.width, BASELINE_DIMENSIONS.height) + */ +const struct { + int height, width; +} BASELINE_DIMENSIONS = { + .width = 1280, + .height = 720, +}; + +/* + * These are hardcoded AB values that the governor votes for in certain + * situations, where a certain bus frequency is desired. It isn't exactly + * scalable since different platforms have different bus widths, but we'll + * deal with that in the future. + */ +const unsigned long NOMINAL_BW_MBPS = 6000 /* ideally 320 Mhz */, + SVS_BW_MBPS = 2000 /* ideally 100 Mhz */; + +/* converts Mbps to bps (the "b" part can be bits or bytes based on context) */ +#define kbps(__mbps) ((__mbps) * 1000) +#define bps(__mbps) (kbps(__mbps) * 1000) + +#define GENERATE_SCENARIO_PROFILE(__average, __worst) { \ + [SCENARIO_AVERAGE] = (__average), \ + [SCENARIO_WORST] = (__worst), \ + [SCENARIO_SUSTAINED_WORST] = (__worst), \ +} + +#define GENERATE_COMPRESSION_PROFILE(__bpp, __average, __worst) { \ + .bpp = __bpp, \ + .ratio = GENERATE_SCENARIO_PROFILE(__average, __worst), \ +} + +/* + * The below table is a structural representation of the following table: + * Resolution | Bitrate | Compression Ratio | + * ............|............|.........................................| + * Width Height|Average High|Avg_8bpc Worst_8bpc Avg_10bpc Worst_10bpc| + * 1280 720| 7 14| 1.69 1.28 1.49 1.23| + * 1920 1080| 20 40| 1.69 1.28 1.49 1.23| + * 2560 1440| 32 64| 2.2 1.26 1.97 1.22| + * 3840 2160| 42 84| 2.2 1.26 1.97 1.22| + * 4096 2160| 44 88| 2.2 1.26 1.97 1.22| + * 4096 2304| 48 96| 2.2 1.26 1.97 1.22| + */ +#define COMPRESSION_RATIO_MAX 2 +static struct lut { + int frame_size; /* width x height */ + unsigned long bitrate[SCENARIO_MAX]; + struct { + int bpp; + fp_t ratio[SCENARIO_MAX]; + } compression_ratio[COMPRESSION_RATIO_MAX]; +} const LUT[] = { + { + .frame_size = 1280 * 720, + .bitrate = GENERATE_SCENARIO_PROFILE(7, 14), + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 69, 100), + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 49, 100), + FP(1, 23, 100)), + } + }, + { + .frame_size = 1920 * 1088, + .bitrate = GENERATE_SCENARIO_PROFILE(20, 40), + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(1, 69, 100), + FP(1, 28, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 49, 100), + FP(1, 23, 100)), + } + }, + { + .frame_size = 2560 * 1440, + .bitrate = GENERATE_SCENARIO_PROFILE(32, 64), + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(2, 20, 100), + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 97, 100), + FP(1, 22, 100)), + } + }, + { + .frame_size = 3840 * 2160, + .bitrate = GENERATE_SCENARIO_PROFILE(42, 84), + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(2, 20, 100), + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 97, 100), + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2160, + .bitrate = GENERATE_SCENARIO_PROFILE(44, 88), + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(2, 20, 100), + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 97, 100), + FP(1, 22, 100)), + } + }, + { + .frame_size = 4096 * 2304, + .bitrate = GENERATE_SCENARIO_PROFILE(48, 96), + .compression_ratio = { + GENERATE_COMPRESSION_PROFILE(8, + FP(2, 20, 100), + FP(1, 26, 100)), + GENERATE_COMPRESSION_PROFILE(10, + FP(1, 97, 100), + FP(1, 22, 100)), + } + }, +}; +static u32 get_type_frm_name(char *name) +{ + if (!strcmp(name, "venus-ddr")) + return DDR; + else + return PERF; +} + +static struct lut const *__lut(int width, int height) +{ + int frame_size = height * width, c = 0; + + do { + if (LUT[c].frame_size >= frame_size) + return &LUT[c]; + } while (++c < ARRAY_SIZE(LUT)); + + return &LUT[ARRAY_SIZE(LUT) - 1]; +} + +static fp_t __compression_ratio(struct lut const *entry, int bpp, + enum scenario s) +{ + int c = 0; + + for (c = 0; c < COMPRESSION_RATIO_MAX; ++c) { + if (entry->compression_ratio[c].bpp == bpp) + return entry->compression_ratio[c].ratio[s]; + } + + WARN(true, "Shouldn't be here, LUT possibly corrupted?\n"); + return FP_ZERO; /* impossible */ +} + +#define DUMP_HEADER_MAGIC 0xdeadbeef +#define DUMP_FP_FMT "%FP" /* special format for fp_t */ +struct dump { + char *key; + char *format; + size_t val; +}; + +static void __dump(struct dump dump[], int len) +{ + int c = 0; + + for (c = 0; c < len; ++c) { + char format_line[128] = "", formatted_line[128] = ""; + + if (dump[c].val == DUMP_HEADER_MAGIC) { + snprintf(formatted_line, sizeof(formatted_line), "%s\n", + dump[c].key); + } else { + bool fp_format = !strcmp(dump[c].format, DUMP_FP_FMT); + + if (!fp_format) { + snprintf(format_line, sizeof(format_line), + " %-35s: %s\n", dump[c].key, + dump[c].format); + snprintf(formatted_line, sizeof(formatted_line), + format_line, dump[c].val); + } else { + size_t integer_part, fractional_part; + + integer_part = fp_int(dump[c].val); + fractional_part = fp_frac(dump[c].val); + snprintf(formatted_line, sizeof(formatted_line), + " %-35s: %zd + %zd/%zd\n", + dump[c].key, integer_part, + fractional_part, + fp_frac_base()); + + + } + } + + dprintk(VIDC_DBG, "%s", formatted_line); + } +} + +static unsigned long __calculate_vpe(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + return 0; +} + +static bool __ubwc(enum hal_uncompressed_format f) +{ + switch (f) { + case HAL_COLOR_FORMAT_NV12_UBWC: + case HAL_COLOR_FORMAT_NV12_TP10_UBWC: + return true; + default: + return false; + } +} + +static int __bpp(enum hal_uncompressed_format f) +{ + switch (f) { + case HAL_COLOR_FORMAT_NV12: + case HAL_COLOR_FORMAT_NV21: + case HAL_COLOR_FORMAT_NV12_UBWC: + return 8; + case HAL_COLOR_FORMAT_NV12_TP10_UBWC: + return 10; + default: + dprintk(VIDC_ERR, + "What's this? We don't support this colorformat (%x)", + f); + return INT_MAX; + } +} + +static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + /* + * XXX: Don't fool around with any of the hardcoded numbers unless you + * know /exactly/ what you're doing. Many of these numbers are + * measured heuristics and hardcoded numbers taken from the firmware. + */ + /* Decoder parameters */ + enum scenario scenario; + int width, height, lcu_size, dpb_bpp, opb_bpp, fps; + bool unified_dpb_opb, dpb_compression_enabled, opb_compression_enabled; + fp_t dpb_opb_scaling_ratio, dpb_compression_factor, + opb_compression_factor, qsmmu_bw_overhead_factor; + int vmem_size; /* in kB */ + + /* Derived parameters */ + int lcu_per_frame, tnbr_per_lcu_10bpc, tnbr_per_lcu_8bpc, tnbr_per_lcu, + colocated_bytes_per_lcu, vmem_line_buffer, vmem_chroma_cache, + vmem_luma_cache, vmem_chroma_luma_cache; + unsigned long bitrate; + fp_t bins_to_bit_factor, dpb_write_factor, ten_bpc_packing_factor, + ten_bpc_bpp_factor, vsp_read_factor, vsp_write_factor, + ocmem_usage_lcu_factor, ref_ocmem_bw_factor_read, + ref_ocmem_bw_factor_write, bw_for_1x_8bpc, dpb_bw_for_1x, + motion_vector_complexity, row_cache_penalty, opb_bw; + + /* Output parameters */ + struct { + fp_t vsp_read, vsp_write, collocated_read, collocated_write, + line_buffer_read, line_buffer_write, recon_read, + recon_write, opb_read, opb_write, dpb_read, dpb_write, + total; + } ddr, vmem; + + unsigned long ret = 0; + + /* Decoder parameters setup */ + scenario = SCENARIO_WORST; + + width = max(d->width, BASELINE_DIMENSIONS.width); + height = max(d->height, BASELINE_DIMENSIONS.height); + + lcu_size = 32; + + dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX; + opb_bpp = d->num_formats >= 2 ? __bpp(d->color_formats[1]) : dpb_bpp; + + fps = d->fps; + + unified_dpb_opb = d->num_formats == 1; + + dpb_opb_scaling_ratio = FP_ONE; + + dpb_compression_enabled = d->num_formats >= 1 && + __ubwc(d->color_formats[0]); + opb_compression_enabled = d->num_formats >= 2 && + __ubwc(d->color_formats[1]); + + dpb_compression_factor = !dpb_compression_enabled ? FP_ONE : + __compression_ratio(__lut(width, height), dpb_bpp, scenario); + + opb_compression_factor = !opb_compression_enabled ? FP_ONE : + __compression_ratio(__lut(width, height), opb_bpp, scenario); + + vmem_size = 512; /* in kB */ + + /* Derived parameters setup */ + lcu_per_frame = DIV_ROUND_UP(width, lcu_size) * + DIV_ROUND_UP(height, lcu_size); + + bitrate = __lut(width, height)->bitrate[scenario]; + + bins_to_bit_factor = FP(1, 60, 100); + + dpb_write_factor = scenario == SCENARIO_AVERAGE ? + FP_ONE : FP(1, 5, 100); + + ten_bpc_packing_factor = FP(1, 67, 1000); + ten_bpc_bpp_factor = FP(1, 1, 4); + + vsp_read_factor = bins_to_bit_factor + FP_INT(2); + vsp_write_factor = bins_to_bit_factor; + + tnbr_per_lcu_10bpc = lcu_size == 16 ? 384 + 192 : + lcu_size == 32 ? 640 + 256 : + 1280 + 384; + tnbr_per_lcu_8bpc = lcu_size == 16 ? 256 + 192 : + lcu_size == 32 ? 512 + 256 : + 1024 + 384; + tnbr_per_lcu = dpb_bpp == 10 ? tnbr_per_lcu_10bpc : tnbr_per_lcu_8bpc; + + colocated_bytes_per_lcu = lcu_size == 16 ? 16 : + lcu_size == 32 ? 64 : 256; + + ocmem_usage_lcu_factor = lcu_size == 16 ? FP(1, 8, 10) : + lcu_size == 32 ? FP(1, 2, 10) : + FP_ONE; + ref_ocmem_bw_factor_read = vmem_size < 296 ? FP_ZERO : + vmem_size < 648 ? FP(0, 1, 4) : + FP(0, 55, 100); + ref_ocmem_bw_factor_write = vmem_size < 296 ? FP_ZERO : + vmem_size < 648 ? FP(0, 7, 10) : + FP(1, 4, 10); + + /* Prelim b/w calculation */ + bw_for_1x_8bpc = fp_mult(FP_INT(width * height * fps), + fp_mult(FP(1, 50, 100), dpb_write_factor)); + bw_for_1x_8bpc = fp_div(bw_for_1x_8bpc, FP_INT(bps(1))); + + dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc : + fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor, + ten_bpc_bpp_factor)); + /* VMEM adjustments */ + vmem_line_buffer = tnbr_per_lcu * DIV_ROUND_UP(width, lcu_size) / 1024; + vmem_chroma_cache = dpb_bpp == 10 ? 176 : 128; + vmem_luma_cache = dpb_bpp == 10 ? 353 : 256; + vmem_chroma_luma_cache = vmem_chroma_cache + vmem_luma_cache; + + motion_vector_complexity = scenario == SCENARIO_AVERAGE ? + FP(2, 66, 100) : FP_INT(4); + + row_cache_penalty = FP_ZERO; + if (vmem_size < vmem_line_buffer + vmem_chroma_cache) + row_cache_penalty = fp_mult(FP(0, 5, 100), + motion_vector_complexity); + else if (vmem_size < vmem_line_buffer + vmem_luma_cache) + row_cache_penalty = fp_mult(FP(0, 7, 100), + motion_vector_complexity); + else if (vmem_size < vmem_line_buffer + vmem_chroma_cache + + vmem_luma_cache) + row_cache_penalty = fp_mult(FP(0, 3, 100), + motion_vector_complexity); + else + row_cache_penalty = FP_ZERO; + + + opb_bw = unified_dpb_opb ? FP_ZERO : + fp_div(fp_div(bw_for_1x_8bpc, dpb_opb_scaling_ratio), + opb_compression_factor); + + /* B/W breakdown on a per buffer type basis for VMEM */ + vmem.vsp_read = FP_ZERO; + vmem.vsp_write = FP_ZERO; + + vmem.collocated_read = FP_ZERO; + vmem.collocated_write = FP_ZERO; + + vmem.line_buffer_read = FP_INT(tnbr_per_lcu * + lcu_per_frame * fps / bps(1)); + vmem.line_buffer_write = vmem.line_buffer_read; + + vmem.recon_read = FP_ZERO; + vmem.recon_write = FP_ZERO; + + vmem.opb_read = FP_ZERO; + vmem.opb_write = FP_ZERO; + + vmem.dpb_read = fp_mult(ocmem_usage_lcu_factor, fp_mult( + ref_ocmem_bw_factor_read, + dpb_bw_for_1x)); + vmem.dpb_write = fp_mult(ocmem_usage_lcu_factor, fp_mult( + ref_ocmem_bw_factor_write, + dpb_bw_for_1x)); + + vmem.total = vmem.vsp_read + vmem.vsp_write + + vmem.collocated_read + vmem.collocated_write + + vmem.line_buffer_read + vmem.line_buffer_write + + vmem.recon_read + vmem.recon_write + + vmem.opb_read + vmem.opb_write + + vmem.dpb_read + vmem.dpb_write; + + /* + * Attempt to force VMEM to a certain frequency for 4K + */ + if (width * height * fps >= 3840 * 2160 * 60) + vmem.total = FP_INT(NOMINAL_BW_MBPS); + else if (width * height * fps >= 3840 * 2160 * 30) + vmem.total = FP_INT(SVS_BW_MBPS); + + /* ........................................ for DDR */ + ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate), + vsp_read_factor), FP_INT(8)); + ddr.vsp_write = fp_div(fp_mult(FP_INT(bitrate), + vsp_write_factor), FP_INT(8)); + + ddr.collocated_read = FP_INT(lcu_per_frame * + colocated_bytes_per_lcu * fps / bps(1)); + ddr.collocated_write = FP_INT(lcu_per_frame * + colocated_bytes_per_lcu * fps / bps(1)); + + ddr.line_buffer_read = vmem_size ? FP_ZERO : vmem.line_buffer_read; + ddr.line_buffer_write = vmem_size ? FP_ZERO : vmem.line_buffer_write; + + ddr.recon_read = FP_ZERO; + ddr.recon_write = fp_div(dpb_bw_for_1x, dpb_compression_factor); + + ddr.opb_read = FP_ZERO; + ddr.opb_write = opb_bw; + + ddr.dpb_read = fp_div(fp_mult(dpb_bw_for_1x, + motion_vector_complexity + row_cache_penalty), + dpb_compression_factor); + ddr.dpb_write = FP_ZERO; + + ddr.total = ddr.vsp_read + ddr.vsp_write + + ddr.collocated_read + ddr.collocated_write + + ddr.line_buffer_read + ddr.line_buffer_write + + ddr.recon_read + ddr.recon_write + + ddr.opb_read + ddr.opb_write + + ddr.dpb_read + ddr.dpb_write; + + qsmmu_bw_overhead_factor = FP(1, 3, 100); + ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); + + /* Dump all the variables for easier debugging */ + if (msm_vidc_debug & VIDC_DBG) { + struct dump dump[] = { + {"DECODER PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"content", "%d", scenario}, + {"LCU size", "%d", lcu_size}, + {"DPB bitdepth", "%d", dpb_bpp}, + {"frame rate", "%d", fps}, + {"DPB/OPB unified", "%d", unified_dpb_opb}, + {"DPB/OPB downscaling ratio", DUMP_FP_FMT, + dpb_opb_scaling_ratio}, + {"DPB compression", "%d", dpb_compression_enabled}, + {"OPB compression", "%d", opb_compression_enabled}, + {"DPB compression factor", DUMP_FP_FMT, + dpb_compression_factor}, + {"OPB compression factor", DUMP_FP_FMT, + opb_compression_factor}, + {"VMEM size", "%dkB", vmem_size}, + {"frame width", "%d", width}, + {"frame height", "%d", height}, + + {"DERIVED PARAMETERS (1)", "", DUMP_HEADER_MAGIC}, + {"LCUs/frame", "%d", lcu_per_frame}, + {"bitrate (Mbit/sec)", "%d", bitrate}, + {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"DPB write factor", DUMP_FP_FMT, dpb_write_factor}, + {"10bpc packing factor", DUMP_FP_FMT, + ten_bpc_packing_factor}, + {"10bpc,BPP factor", DUMP_FP_FMT, ten_bpc_bpp_factor}, + {"VSP read factor", DUMP_FP_FMT, vsp_read_factor}, + {"VSP write factor", DUMP_FP_FMT, vsp_write_factor}, + {"TNBR/LCU_10bpc", "%d", tnbr_per_lcu_10bpc}, + {"TNBR/LCU_8bpc", "%d", tnbr_per_lcu_8bpc}, + {"TNBR/LCU", "%d", tnbr_per_lcu}, + {"colocated bytes/LCU", "%d", colocated_bytes_per_lcu}, + {"OCMEM usage LCU factor", DUMP_FP_FMT, + ocmem_usage_lcu_factor}, + {"ref OCMEM b/w factor (read)", DUMP_FP_FMT, + ref_ocmem_bw_factor_read}, + {"ref OCMEM b/w factor (write)", DUMP_FP_FMT, + ref_ocmem_bw_factor_write}, + {"B/W for 1x (NV12 8bpc)", DUMP_FP_FMT, bw_for_1x_8bpc}, + {"DPB B/W For 1x (NV12)", DUMP_FP_FMT, dpb_bw_for_1x}, + + {"VMEM", "", DUMP_HEADER_MAGIC}, + {"line buffer", "%d", vmem_line_buffer}, + {"chroma cache", "%d", vmem_chroma_cache}, + {"luma cache", "%d", vmem_luma_cache}, + {"luma & chroma cache", "%d", vmem_chroma_luma_cache}, + + {"DERIVED PARAMETERS (2)", "", DUMP_HEADER_MAGIC}, + {"MV complexity", DUMP_FP_FMT, motion_vector_complexity}, + {"row cache penalty", DUMP_FP_FMT, row_cache_penalty}, + {"OPB B/W (single instance)", DUMP_FP_FMT, opb_bw}, + + {"INTERMEDIATE DDR B/W", "", DUMP_HEADER_MAGIC}, + {"VSP read", DUMP_FP_FMT, ddr.vsp_read}, + {"VSP write", DUMP_FP_FMT, ddr.vsp_write}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, + {"collocated write", DUMP_FP_FMT, ddr.collocated_write}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, + {"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write}, + {"recon read", DUMP_FP_FMT, ddr.recon_read}, + {"recon write", DUMP_FP_FMT, ddr.recon_write}, + {"OPB read", DUMP_FP_FMT, ddr.opb_read}, + {"OPB write", DUMP_FP_FMT, ddr.opb_write}, + {"DPB read", DUMP_FP_FMT, ddr.dpb_read}, + {"DPB write", DUMP_FP_FMT, ddr.dpb_write}, + + {"INTERMEDIATE VMEM B/W", "", DUMP_HEADER_MAGIC}, + {"VSP read", "%d", vmem.vsp_read}, + {"VSP write", DUMP_FP_FMT, vmem.vsp_write}, + {"collocated read", DUMP_FP_FMT, vmem.collocated_read}, + {"collocated write", DUMP_FP_FMT, vmem.collocated_write}, + {"line buffer read", DUMP_FP_FMT, vmem.line_buffer_read}, + {"line buffer write", DUMP_FP_FMT, vmem.line_buffer_write}, + {"recon read", DUMP_FP_FMT, vmem.recon_read}, + {"recon write", DUMP_FP_FMT, vmem.recon_write}, + {"OPB read", DUMP_FP_FMT, vmem.opb_read}, + {"OPB write", DUMP_FP_FMT, vmem.opb_write}, + {"DPB read", DUMP_FP_FMT, vmem.dpb_read}, + {"DPB write", DUMP_FP_FMT, vmem.dpb_write}, + }; + __dump(dump, ARRAY_SIZE(dump)); + } + + switch (type) { + case DDR: + ret = kbps(fp_round(ddr.total)); + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + } + + return ret; +} + + +static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + /* + * XXX: Don't fool around with any of the hardcoded numbers unless you + * know /exactly/ what you're doing. Many of these numbers are + * measured heuristics and hardcoded numbers taken from the firmware. + */ + /* Encoder Parameters */ + enum scenario scenario, bitrate_scenario; + enum hal_video_codec standard; + int width, height, fps, vmem_size; + enum hal_uncompressed_format dpb_color_format; + enum hal_uncompressed_format original_color_format; + bool dpb_compression_enabled, original_compression_enabled, + two_stage_encoding, low_power, rotation, cropping_or_scaling; + fp_t dpb_compression_factor, original_compression_factor, + qsmmu_bw_overhead_factor; + bool b_frames_enabled; + + /* Derived Parameters */ + int lcu_size; + enum gop { + GOP_IBBP, + GOP_IPPP, + } gop; + unsigned long bitrate; + fp_t bins_to_bit_factor, chroma_luma_factor_dpb, one_frame_bw_dpb, + chroma_luma_factor_original, one_frame_bw_original, + line_buffer_size_per_lcu, line_buffer_size, line_buffer_bw, + original_vmem_requirement, bw_increase_p, bw_increase_b; + int collocated_mv_per_lcu, max_transaction_size, + search_window_size_vertical_p, search_window_factor_p, + search_window_factor_bw_p, vmem_size_p, available_vmem_p, + search_window_size_vertical_b, search_window_factor_b, + search_window_factor_bw_b, vmem_size_b, available_vmem_b; + + /* Output parameters */ + struct { + fp_t vsp_read, vsp_write, collocated_read, collocated_write, + line_buffer_read, line_buffer_write, original_read, + original_write, dpb_read, dpb_write, total; + } ddr, vmem; + + unsigned long ret = 0; + + /* Encoder Parameters setup */ + scenario = SCENARIO_WORST; + + standard = d->codec; + width = max(d->width, BASELINE_DIMENSIONS.width); + height = max(d->height, BASELINE_DIMENSIONS.height); + + dpb_color_format = HAL_COLOR_FORMAT_NV12_UBWC; + original_color_format = d->num_formats >= 1 ? + d->color_formats[0] : HAL_UNUSED_COLOR; + + fps = d->fps; + bitrate_scenario = SCENARIO_WORST; + + dpb_compression_enabled = __ubwc(dpb_color_format); + original_compression_enabled = __ubwc(original_color_format); + + two_stage_encoding = false; + low_power = d->power_mode == VIDC_POWER_LOW; + b_frames_enabled = false; + + dpb_compression_factor = !dpb_compression_enabled ? FP_ONE : + __compression_ratio(__lut(width, height), + __bpp(dpb_color_format), scenario); + original_compression_factor = !original_compression_enabled ? FP_ONE : + __compression_ratio(__lut(width, height), + __bpp(original_color_format), scenario); + + rotation = false; + cropping_or_scaling = false; + vmem_size = 512; /* in kB */ + + /* Derived Parameters */ + lcu_size = 16; + gop = b_frames_enabled ? GOP_IBBP : GOP_IPPP; + bitrate = __lut(width, height)->bitrate[bitrate_scenario]; + bins_to_bit_factor = FP(1, 6, 10); + + /* + * FIXME: Minor color format related hack: a lot of the derived params + * depend on the YUV bitdepth as a variable. However, we don't have + * appropriate enums defined yet (hence no support). As a result omit + * a lot of the checks (which should look like the snippet below) in + * favour of hardcoding. + * dpb_color_format == YUV420 ? 0.5 : + * dpb_color_format == YUV422 ? 1.0 : 2.0 + * Similar hacks are annotated inline in code with the string "CF hack" + * for documentation purposes. + */ + chroma_luma_factor_dpb = FP(0, 1, 2); + one_frame_bw_dpb = fp_mult(FP_ONE + chroma_luma_factor_dpb, + fp_div(FP_INT(width * height * fps), + FP_INT(1000 * 1000))); + + chroma_luma_factor_original = FP(0, 1, 2); /* XXX: CF hack */ + one_frame_bw_original = fp_mult(FP_ONE + chroma_luma_factor_original, + fp_div(FP_INT(width * height * fps), + FP_INT(1000 * 1000))); + + line_buffer_size_per_lcu = FP_ZERO; + if (lcu_size == 16) + line_buffer_size_per_lcu = FP_INT(128) + fp_mult(FP_INT(256), + FP_ONE /*XXX: CF hack */); + else + line_buffer_size_per_lcu = FP_INT(192) + fp_mult(FP_INT(512), + FP_ONE /*XXX: CF hack */); + + line_buffer_size = fp_div( + fp_mult(FP_INT(width / lcu_size), + line_buffer_size_per_lcu), + FP_INT(1024)); + line_buffer_bw = fp_mult(line_buffer_size, + fp_div(FP_INT((height / lcu_size / + (two_stage_encoding ? 2 : 1) - 1) * fps), + FP_INT(1000))); + + collocated_mv_per_lcu = lcu_size == 16 ? 16 : 64; + max_transaction_size = 256; + + original_vmem_requirement = FP_INT(3 * + (two_stage_encoding ? 2 : 1) * lcu_size); + original_vmem_requirement = fp_mult(original_vmem_requirement, + (FP_ONE + chroma_luma_factor_original)); + original_vmem_requirement += FP_INT((cropping_or_scaling ? 3 : 0) * 2); + original_vmem_requirement = fp_mult(original_vmem_requirement, + FP_INT(max_transaction_size)); + original_vmem_requirement = fp_div(original_vmem_requirement, + FP_INT(1024)); + + search_window_size_vertical_p = low_power ? 32 : + b_frames_enabled ? 80 : + width > 2048 ? 64 : 48; + search_window_factor_p = search_window_size_vertical_p * 2 / lcu_size; + search_window_factor_bw_p = !two_stage_encoding ? + search_window_size_vertical_p * 2 / lcu_size + 1 : + (search_window_size_vertical_p * 2 / lcu_size + 2) / 2; + vmem_size_p = (search_window_factor_p * width + 128 * 2) * + lcu_size / 2 / 1024; /* XXX: CF hack */ + bw_increase_p = fp_mult(one_frame_bw_dpb, + FP_INT(search_window_factor_bw_p - 1) / 3); + available_vmem_p = min_t(int, 3, (vmem_size - fp_int(line_buffer_size) - + fp_int(original_vmem_requirement)) / vmem_size_p); + + search_window_size_vertical_b = 48; + search_window_factor_b = search_window_size_vertical_b * 2 / lcu_size; + search_window_factor_bw_b = !two_stage_encoding ? + search_window_size_vertical_b * 2 / lcu_size + 1 : + (search_window_size_vertical_b * 2 / lcu_size + 2) / 2; + vmem_size_b = (search_window_factor_b * width + 128 * 2) * lcu_size / + 2 / 1024; + bw_increase_b = fp_mult(one_frame_bw_dpb, + FP_INT((search_window_factor_bw_b - 1) / 3)); + available_vmem_b = min_t(int, 6, (vmem_size - fp_int(line_buffer_size) - + fp_int(original_vmem_requirement)) / vmem_size_b); + + /* Output parameters for DDR */ + ddr.vsp_read = fp_mult(fp_div(FP_INT(bitrate), FP_INT(8)), + bins_to_bit_factor); + ddr.vsp_write = ddr.vsp_read + fp_div(FP_INT(bitrate), FP_INT(8)); + + ddr.collocated_read = fp_div(FP_INT(DIV_ROUND_UP(width, lcu_size) * + DIV_ROUND_UP(height, lcu_size) * + collocated_mv_per_lcu * fps), FP_INT(1000 * 1000)); + ddr.collocated_write = ddr.collocated_read; + + ddr.line_buffer_read = (FP_INT(vmem_size) >= line_buffer_size + + original_vmem_requirement) ? FP_ZERO : line_buffer_bw; + ddr.line_buffer_write = ddr.line_buffer_read; + + ddr.original_read = fp_div(one_frame_bw_original, + original_compression_factor); + ddr.original_write = FP_ZERO; + + ddr.dpb_read = FP_ZERO; + if (gop == GOP_IPPP) { + ddr.dpb_read = one_frame_bw_dpb + fp_mult(bw_increase_p, + FP_INT(3 - available_vmem_p)); + } else if (scenario == SCENARIO_WORST || + scenario == SCENARIO_SUSTAINED_WORST) { + ddr.dpb_read = fp_mult(one_frame_bw_dpb, FP_INT(2)); + ddr.dpb_read += fp_mult(FP_INT(6 - available_vmem_b), + bw_increase_b); + } else { + fp_t part_p, part_b; + + part_p = one_frame_bw_dpb + fp_mult(bw_increase_p, + FP_INT(3 - available_vmem_p)); + part_p = fp_div(part_p, FP_INT(3)); + + part_b = fp_mult(one_frame_bw_dpb, 2) + + fp_mult(FP_INT(6 - available_vmem_b), bw_increase_b); + part_b = fp_mult(part_b, FP(0, 2, 3)); + + ddr.dpb_read = part_p + part_b; + } + + ddr.dpb_read = fp_div(ddr.dpb_read, dpb_compression_factor); + ddr.dpb_write = fp_div(one_frame_bw_dpb, dpb_compression_factor); + + ddr.total = ddr.vsp_read + ddr.vsp_write + + ddr.collocated_read + ddr.collocated_write + + ddr.line_buffer_read + ddr.line_buffer_write + + ddr.original_read + ddr.original_write + + ddr.dpb_read + ddr.dpb_write; + + qsmmu_bw_overhead_factor = FP(1, 3, 100); + ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor); + + /* ................. for VMEM */ + vmem.vsp_read = FP_ZERO; + vmem.vsp_write = FP_ZERO; + + vmem.collocated_read = FP_ZERO; + vmem.collocated_write = FP_ZERO; + + vmem.line_buffer_read = line_buffer_bw - ddr.line_buffer_read; + vmem.line_buffer_write = vmem.line_buffer_read; + + vmem.original_read = FP_INT(vmem_size) >= original_vmem_requirement ? + ddr.original_read : FP_ZERO; + vmem.original_write = vmem.original_read; + + vmem.dpb_read = FP_ZERO; + if (gop == GOP_IPPP) { + fp_t temp = fp_mult(one_frame_bw_dpb, + FP_INT(search_window_factor_bw_p * available_vmem_p)); + temp = fp_div(temp, FP_INT(3)); + + vmem.dpb_read = temp; + } else if (scenario != SCENARIO_AVERAGE) { + fp_t temp = fp_mult(one_frame_bw_dpb, FP_INT(2)); + + temp = fp_mult(temp, FP_INT(search_window_factor_bw_b * + available_vmem_b)); + temp = fp_div(temp, FP_INT(6)); + + vmem.dpb_read = temp; + } else { + fp_t part_p, part_b; + + part_p = fp_mult(one_frame_bw_dpb, FP_INT( + search_window_factor_bw_p * + available_vmem_p)); + part_p = fp_div(part_p, FP_INT(3 * 3)); + + part_b = fp_mult(one_frame_bw_dpb, FP_INT(2 * + search_window_factor_bw_b * + available_vmem_b)); + part_b = fp_div(part_b, FP_INT(6)); + part_b = fp_mult(part_b, FP(0, 2, 3)); + + vmem.dpb_read = part_p + part_b; + } + + vmem.dpb_write = FP_ZERO; + if (gop == GOP_IPPP) { + fp_t temp = fp_mult(one_frame_bw_dpb, + FP_INT(available_vmem_p)); + temp = fp_div(temp, FP_INT(3)); + + vmem.dpb_write = temp; + } else if (scenario != SCENARIO_AVERAGE) { + fp_t temp = fp_mult(one_frame_bw_dpb, + FP_INT(2 * available_vmem_b)); + temp = fp_div(temp, FP_INT(6)); + + vmem.dpb_write = temp; + } else { + fp_t part_b, part_p; + + part_b = fp_mult(one_frame_bw_dpb, FP_INT(available_vmem_p)); + part_b = fp_div(part_b, FP_INT(9)); + + part_p = fp_mult(one_frame_bw_dpb, FP_INT( + 2 * available_vmem_b)); + part_p = fp_div(part_p, FP_INT(6)); + part_b = fp_mult(part_b, FP(0, 2, 3)); + + vmem.dpb_write = part_p + part_b; + } + + vmem.total = vmem.vsp_read + vmem.vsp_write + + vmem.collocated_read + vmem.collocated_write + + vmem.line_buffer_read + vmem.line_buffer_write + + vmem.original_read + vmem.original_write + + vmem.dpb_read + vmem.dpb_write; + + /* + * When in low power mode, attempt to force the VMEM clocks a certain + * frequency that DCVS would prefer + */ + if (width * height >= 3840 * 2160 && low_power) + vmem.total = FP_INT(NOMINAL_BW_MBPS); + + if (msm_vidc_debug & VIDC_DBG) { + struct dump dump[] = { + {"ENCODER PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"scenario", "%d", scenario}, + {"standard", "%#x", standard}, + {"width", "%d", width}, + {"height", "%d", height}, + {"DPB format", "%#x", dpb_color_format}, + {"original frame format", "%#x", original_color_format}, + {"fps", "%d", fps}, + {"target bitrate", "%d", bitrate_scenario}, + {"DPB compression enable", "%d", dpb_compression_enabled}, + {"original compression enable", "%d", + original_compression_enabled}, + {"two stage encoding", "%d", two_stage_encoding}, + {"low power mode", "%d", low_power}, + {"DPB compression factor", DUMP_FP_FMT, + dpb_compression_factor}, + {"original compression factor", DUMP_FP_FMT, + original_compression_factor}, + {"rotation", "%d", rotation}, + {"cropping or scaling", "%d", cropping_or_scaling}, + {"VMEM size (KB)", "%d", vmem_size}, + + {"DERIVED PARAMETERS", "", DUMP_HEADER_MAGIC}, + {"LCU size", "%d", lcu_size}, + {"GOB pattern", "%d", gop}, + {"bitrate (Mbit/sec)", "%lu", bitrate}, + {"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor}, + {"B-frames enabled", "%d", b_frames_enabled}, + {"search window size vertical (B)", "%d", + search_window_size_vertical_b}, + {"search window factor (B)", "%d", search_window_factor_b}, + {"search window factor BW (B)", "%d", + search_window_factor_bw_b}, + {"VMEM size (B)", "%d", vmem_size_b}, + {"bw increase (MB/s) (B)", DUMP_FP_FMT, bw_increase_b}, + {"available VMEM (B)", "%d", available_vmem_b}, + {"search window size vertical (P)", "%d", + search_window_size_vertical_p}, + {"search window factor (P)", "%d", search_window_factor_p}, + {"search window factor BW (P)", "%d", + search_window_factor_bw_p}, + {"VMEM size (P)", "%d", vmem_size_p}, + {"bw increase (MB/s) (P)", DUMP_FP_FMT, bw_increase_p}, + {"available VMEM (P)", "%d", available_vmem_p}, + {"chroma/luma factor DPB", DUMP_FP_FMT, + chroma_luma_factor_dpb}, + {"one frame BW DPB (MB/s)", DUMP_FP_FMT, one_frame_bw_dpb}, + {"chroma/Luma factor original", DUMP_FP_FMT, + chroma_luma_factor_original}, + {"one frame BW original (MB/s)", DUMP_FP_FMT, + one_frame_bw_original}, + {"line buffer size per LCU", DUMP_FP_FMT, + line_buffer_size_per_lcu}, + {"line buffer size (KB)", DUMP_FP_FMT, line_buffer_size}, + {"line buffer BW (MB/s)", DUMP_FP_FMT, line_buffer_bw}, + {"collocated MVs per LCU", "%d", collocated_mv_per_lcu}, + {"original VMEM requirement (KB)", DUMP_FP_FMT, + original_vmem_requirement}, + + {"INTERMEDIATE B/W DDR", "", DUMP_HEADER_MAGIC}, + {"VSP read", DUMP_FP_FMT, ddr.vsp_read}, + {"VSP read", DUMP_FP_FMT, ddr.vsp_write}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_read}, + {"collocated read", DUMP_FP_FMT, ddr.collocated_write}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read}, + {"line buffer read", DUMP_FP_FMT, ddr.line_buffer_write}, + {"original read", DUMP_FP_FMT, ddr.original_read}, + {"original read", DUMP_FP_FMT, ddr.original_write}, + {"DPB read", DUMP_FP_FMT, ddr.dpb_read}, + {"DPB write", DUMP_FP_FMT, ddr.dpb_write}, + + {"INTERMEDIATE B/W VMEM", "", DUMP_HEADER_MAGIC}, + {"VSP read", DUMP_FP_FMT, vmem.vsp_read}, + {"VSP read", DUMP_FP_FMT, vmem.vsp_write}, + {"collocated read", DUMP_FP_FMT, vmem.collocated_read}, + {"collocated read", DUMP_FP_FMT, vmem.collocated_write}, + {"line buffer read", DUMP_FP_FMT, vmem.line_buffer_read}, + {"line buffer read", DUMP_FP_FMT, vmem.line_buffer_write}, + {"original read", DUMP_FP_FMT, vmem.original_read}, + {"original read", DUMP_FP_FMT, vmem.original_write}, + {"DPB read", DUMP_FP_FMT, vmem.dpb_read}, + {"DPB write", DUMP_FP_FMT, vmem.dpb_write}, + }; + __dump(dump, ARRAY_SIZE(dump)); + } + + switch (type) { + case DDR: + ret = kbps(fp_round(ddr.total)); + break; + default: + dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__); + } + + return ret; +} + +static unsigned long __calculate(struct vidc_bus_vote_data *d, + enum vidc_bus_type type) +{ + unsigned long value = 0; + + switch (d->domain) { + case HAL_VIDEO_DOMAIN_VPE: + value = __calculate_vpe(d, type); + break; + case HAL_VIDEO_DOMAIN_ENCODER: + value = __calculate_encoder(d, type); + break; + case HAL_VIDEO_DOMAIN_DECODER: + value = __calculate_decoder(d, type); + break; + default: + dprintk(VIDC_ERR, "Unknown Domain"); + } + + return value; +} + +unsigned long __calc_bw(struct bus_info *bus, + struct msm_vidc_gov_data *vidc_data) +{ + unsigned long ab_kbps = 0, c = 0; + enum vidc_bus_type type; + + if (!vidc_data || !vidc_data->data_count || !vidc_data->data) + goto exit; + + for (c = 0; c < vidc_data->data_count; ++c) { + if (vidc_data->data->power_mode == VIDC_POWER_TURBO) { + ab_kbps = INT_MAX; + goto exit; + } + } + + type = get_type_frm_name(bus->name); + + for (c = 0; c < vidc_data->data_count; ++c) + ab_kbps += __calculate(&vidc_data->data[c], type); + +exit: + trace_msm_vidc_perf_bus_vote(bus->name, ab_kbps); + return ab_kbps; +} diff --git a/drivers/media/platform/msm/vidc_3x/governors/msm_vidc_table_gov.c b/drivers/media/platform/msm/vidc_3x/governors/msm_vidc_table_gov.c new file mode 100644 index 000000000000..38816ff2208c --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/governors/msm_vidc_table_gov.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2016, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "governor.h" +#include "../msm_vidc_debug.h" +#include "../msm_vidc_res_parse.h" +#include "../msm_vidc_internal.h" +#include "../venus_hfi.h" +#include "../vidc_hfi_api.h" + + +enum bus_profile { + VIDC_BUS_PROFILE_NORMAL = BIT(0), + VIDC_BUS_PROFILE_LOW = BIT(1), + VIDC_BUS_PROFILE_UBWC = BIT(2), +}; + +struct bus_profile_entry { + struct { + u32 load, freq; + } *bus_table; + u32 bus_table_size; + u32 codec_mask; + enum bus_profile profile; +}; + +struct msm_vidc_bus_table_gov { + struct bus_profile_entry *bus_prof_entries; + u32 count; + struct devfreq_governor devfreq_gov; +}; + +static int __get_bus_freq(struct msm_vidc_bus_table_gov *gov, + struct vidc_bus_vote_data *data, + enum bus_profile profile) +{ + int i = 0, load = 0, freq = 0; + enum vidc_vote_data_session sess_type = 0; + struct bus_profile_entry *entry = NULL; + bool found = false; + + load = NUM_MBS_PER_SEC(data->width, data->height, data->fps); + sess_type = VIDC_VOTE_DATA_SESSION_VAL(data->codec, data->domain); + + /* check if ubwc bus profile is present */ + for (i = 0; i < gov->count; i++) { + entry = &gov->bus_prof_entries[i]; + if (!entry->bus_table || !entry->bus_table_size) + continue; + if (!venus_hfi_is_session_supported( + entry->codec_mask, sess_type)) + continue; + if (entry->profile == profile) { + found = true; + break; + } + } + + if (found) { + /* loop over bus table and select frequency */ + for (i = entry->bus_table_size - 1; i >= 0; --i) { + /*load is arranged in descending order */ + freq = entry->bus_table[i].freq; + if (load <= entry->bus_table[i].load) + break; + } + } + + return freq; +} + + +static int msm_vidc_table_get_target_freq(struct devfreq *dev, + unsigned long *frequency) +{ + struct devfreq_dev_status status = {0}; + struct msm_vidc_gov_data *vidc_data = NULL; + struct msm_vidc_bus_table_gov *gov = NULL; + enum bus_profile profile = 0; + int i = 0; + + if (!dev || !frequency) { + dprintk(VIDC_ERR, "%s: Invalid params %pK, %pK\n", + __func__, dev, frequency); + return -EINVAL; + } + + gov = container_of(dev->governor, + struct msm_vidc_bus_table_gov, devfreq_gov); + if (!gov) { + dprintk(VIDC_ERR, "%s: governor not found\n", __func__); + return -EINVAL; + } + + dev->profile->get_dev_status(dev->dev.parent, &status); + vidc_data = (struct msm_vidc_gov_data *)status.private_data; + + *frequency = 0; + for (i = 0; i < vidc_data->data_count; i++) { + struct vidc_bus_vote_data *data = &vidc_data->data[i]; + int freq = 0; + + if (data->power_mode == VIDC_POWER_TURBO) { + dprintk(VIDC_DBG, "bus: found turbo session[%d] %#x\n", + i, VIDC_VOTE_DATA_SESSION_VAL(data->codec, + data->domain)); + *frequency = INT_MAX; + goto exit; + } + + profile = VIDC_BUS_PROFILE_NORMAL; + if (data->color_formats[0] == HAL_COLOR_FORMAT_NV12_TP10_UBWC || + data->color_formats[0] == HAL_COLOR_FORMAT_NV12_UBWC) + profile = VIDC_BUS_PROFILE_UBWC; + + freq = __get_bus_freq(gov, data, profile); + + /* chose frequency from normal profile + * if specific profile frequency was not found. + */ + if (!freq) + freq = __get_bus_freq(gov, data, + VIDC_BUS_PROFILE_NORMAL); + + *frequency += (unsigned long)freq; + + dprintk(VIDC_DBG, + "session[%d] %#x: wxh %dx%d, fps %d, bus_profile %#x, freq %d, total_freq %ld KBps\n", + i, VIDC_VOTE_DATA_SESSION_VAL( + data->codec, data->domain), data->width, + data->height, data->fps, profile, + freq, *frequency); + } +exit: + return 0; +} + +int msm_vidc_table_event_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + int rc = 0; + + if (!devfreq) { + dprintk(VIDC_ERR, "%s: NULL devfreq\n", __func__); + return -EINVAL; + } + + switch (event) { + case DEVFREQ_GOV_START: + case DEVFREQ_GOV_RESUME: + mutex_lock(&devfreq->lock); + rc = update_devfreq(devfreq); + mutex_unlock(&devfreq->lock); + break; + } + + return rc; +} + +static int msm_vidc_free_bus_table(struct platform_device *pdev, + struct msm_vidc_bus_table_gov *data) +{ + int rc = 0, i = 0; + + if (!pdev || !data) { + dprintk(VIDC_ERR, "%s: invalid args %pK %pK\n", + __func__, pdev, data); + return -EINVAL; + } + + for (i = 0; i < data->count; i++) + data->bus_prof_entries[i].bus_table = NULL; + + data->bus_prof_entries = NULL; + data->count = 0; + + return rc; +} + +static int msm_vidc_load_bus_table(struct platform_device *pdev, + struct msm_vidc_bus_table_gov *data) +{ + int rc = 0, i = 0, j = 0; + const char *name = NULL; + struct bus_profile_entry *entry = NULL; + struct device_node *parent_node = NULL; + struct device_node *child_node = NULL; + + if (!pdev || !data) { + dprintk(VIDC_ERR, "%s: invalid args %pK %pK\n", + __func__, pdev, data); + return -EINVAL; + } + + of_property_read_string(pdev->dev.of_node, "name", &name); + if (strlen(name) > ARRAY_SIZE(data->devfreq_gov.name) - 1) { + dprintk(VIDC_ERR, + "%s: name is too long, max should be %zu chars\n", + __func__, ARRAY_SIZE(data->devfreq_gov.name) - 1); + return -EINVAL; + } + + strlcpy((char *)data->devfreq_gov.name, name, + ARRAY_SIZE(data->devfreq_gov.name)); + data->devfreq_gov.get_target_freq = msm_vidc_table_get_target_freq; + data->devfreq_gov.event_handler = msm_vidc_table_event_handler; + + parent_node = of_find_node_by_name(pdev->dev.of_node, + "qcom,bus-freq-table"); + if (!parent_node) { + dprintk(VIDC_DBG, "Node qcom,bus-freq-table not found.\n"); + return 0; + } + + data->count = of_get_child_count(parent_node); + if (!data->count) { + dprintk(VIDC_DBG, "No child nodes in qcom,bus-freq-table\n"); + return 0; + } + + data->bus_prof_entries = devm_kzalloc(&pdev->dev, + sizeof(*data->bus_prof_entries) * data->count, + GFP_KERNEL); + if (!data->bus_prof_entries) { + dprintk(VIDC_DBG, "no memory to allocate bus_prof_entries\n"); + return -ENOMEM; + } + + for_each_child_of_node(parent_node, child_node) { + + if (i >= data->count) { + dprintk(VIDC_ERR, + "qcom,bus-freq-table: invalid child node %d, max is %d\n", + i, data->count); + break; + } + entry = &data->bus_prof_entries[i]; + + if (of_find_property(child_node, "qcom,codec-mask", NULL)) { + rc = of_property_read_u32(child_node, + "qcom,codec-mask", &entry->codec_mask); + if (rc) { + dprintk(VIDC_ERR, + "qcom,codec-mask not found\n"); + break; + } + } + + if (of_find_property(child_node, "qcom,low-power-mode", NULL)) + entry->profile = VIDC_BUS_PROFILE_LOW; + else if (of_find_property(child_node, "qcom,ubwc-mode", NULL)) + entry->profile = VIDC_BUS_PROFILE_UBWC; + else + entry->profile = VIDC_BUS_PROFILE_NORMAL; + + if (of_find_property(child_node, + "qcom,load-busfreq-tbl", NULL)) { + rc = msm_vidc_load_u32_table(pdev, child_node, + "qcom,load-busfreq-tbl", + sizeof(*entry->bus_table), + (u32 **)&entry->bus_table, + &entry->bus_table_size); + if (rc) { + dprintk(VIDC_ERR, + "qcom,load-busfreq-tbl failed\n"); + break; + } + } else { + entry->bus_table = NULL; + entry->bus_table_size = 0; + } + + dprintk(VIDC_DBG, + "qcom,load-busfreq-tbl: size %d, codec_mask %#x, profile %#x\n", + entry->bus_table_size, entry->codec_mask, + entry->profile); + for (j = 0; j < entry->bus_table_size; j++) + dprintk(VIDC_DBG, " load %8d freq %8d\n", + entry->bus_table[j].load, + entry->bus_table[j].freq); + + i++; + } + + return rc; +} + +static int msm_vidc_bus_table_probe(struct platform_device *pdev) +{ + int rc = 0; + struct msm_vidc_bus_table_gov *gov = NULL; + + dprintk(VIDC_DBG, "%s\n", __func__); + + gov = devm_kzalloc(&pdev->dev, sizeof(*gov), GFP_KERNEL); + if (!gov) { + dprintk(VIDC_ERR, "%s: allocation failed\n", __func__); + return -ENOMEM; + } + + platform_set_drvdata(pdev, gov); + + rc = msm_vidc_load_bus_table(pdev, gov); + if (rc) + return rc; + + rc = devfreq_add_governor(&gov->devfreq_gov); + if (rc) + dprintk(VIDC_ERR, "%s: add governor failed\n", __func__); + + return rc; +} + +static int msm_vidc_bus_table_remove(struct platform_device *pdev) +{ + int rc = 0; + struct msm_vidc_bus_table_gov *gov = NULL; + + dprintk(VIDC_DBG, "%s\n", __func__); + + gov = platform_get_drvdata(pdev); + if (IS_ERR_OR_NULL(gov)) + return PTR_ERR(gov); + + rc = msm_vidc_free_bus_table(pdev, gov); + if (rc) + dprintk(VIDC_WARN, "%s: free bus table failed\n", __func__); + + rc = devfreq_remove_governor(&gov->devfreq_gov); + + return rc; +} + +static const struct of_device_id device_id[] = { + {.compatible = "qcom,msm-vidc,governor,table"}, + {} +}; + +static struct platform_driver msm_vidc_bus_table_driver = { + .probe = msm_vidc_bus_table_probe, + .remove = msm_vidc_bus_table_remove, + .driver = { + .name = "msm_vidc_bus_table_governor", + .of_match_table = device_id, + }, +}; + +static int __init msm_vidc_bus_table_init(void) +{ + + dprintk(VIDC_DBG, "%s\n", __func__); + + return platform_driver_register(&msm_vidc_bus_table_driver); +} + +module_init(msm_vidc_bus_table_init); + +static void __exit msm_vidc_bus_table_exit(void) +{ + dprintk(VIDC_DBG, "%s\n", __func__); + platform_driver_unregister(&msm_vidc_bus_table_driver); +} + +module_exit(msm_vidc_bus_table_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/vidc_3x/hfi_packetization.c b/drivers/media/platform/msm/vidc_3x/hfi_packetization.c new file mode 100644 index 000000000000..872023956953 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/hfi_packetization.c @@ -0,0 +1,2568 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include "hfi_packetization.h" +#include "msm_vidc_debug.h" + +/* Set up look-up tables to convert HAL_* to HFI_*. + * + * The tables below mostly take advantage of the fact that most + * HAL_* types are defined bitwise. So if we index them normally + * when declaring the tables, we end up with huge arrays with wasted + * space. So before indexing them, we apply log2 to use a more + * sensible index. + */ +static int profile_table[] = { + [ilog2(HAL_H264_PROFILE_BASELINE)] = HFI_H264_PROFILE_BASELINE, + [ilog2(HAL_H264_PROFILE_MAIN)] = HFI_H264_PROFILE_MAIN, + [ilog2(HAL_H264_PROFILE_HIGH)] = HFI_H264_PROFILE_HIGH, + [ilog2(HAL_H264_PROFILE_CONSTRAINED_BASE)] = + HFI_H264_PROFILE_CONSTRAINED_BASE, + [ilog2(HAL_H264_PROFILE_CONSTRAINED_HIGH)] = + HFI_H264_PROFILE_CONSTRAINED_HIGH, + [ilog2(HAL_VPX_PROFILE_VERSION_1)] = HFI_VPX_PROFILE_VERSION_1, + [ilog2(HAL_MVC_PROFILE_STEREO_HIGH)] = HFI_H264_PROFILE_STEREO_HIGH, +}; + +static int entropy_mode[] = { + [ilog2(HAL_H264_ENTROPY_CAVLC)] = HFI_H264_ENTROPY_CAVLC, + [ilog2(HAL_H264_ENTROPY_CABAC)] = HFI_H264_ENTROPY_CABAC, +}; + +static int cabac_model[] = { + [ilog2(HAL_H264_CABAC_MODEL_0)] = HFI_H264_CABAC_MODEL_0, + [ilog2(HAL_H264_CABAC_MODEL_1)] = HFI_H264_CABAC_MODEL_1, + [ilog2(HAL_H264_CABAC_MODEL_2)] = HFI_H264_CABAC_MODEL_2, +}; + +static int statistics_mode[] = { + [ilog2(HAL_STATISTICS_MODE_DEFAULT)] = HFI_STATISTICS_MODE_DEFAULT, + [ilog2(HAL_STATISTICS_MODE_1)] = HFI_STATISTICS_MODE_1, + [ilog2(HAL_STATISTICS_MODE_2)] = HFI_STATISTICS_MODE_2, + [ilog2(HAL_STATISTICS_MODE_3)] = HFI_STATISTICS_MODE_3, +}; + +static int color_format[] = { + [ilog2(HAL_COLOR_FORMAT_MONOCHROME)] = HFI_COLOR_FORMAT_MONOCHROME, + [ilog2(HAL_COLOR_FORMAT_NV12)] = HFI_COLOR_FORMAT_NV12, + [ilog2(HAL_COLOR_FORMAT_NV21)] = HFI_COLOR_FORMAT_NV21, + [ilog2(HAL_COLOR_FORMAT_NV12_4x4TILE)] = HFI_COLOR_FORMAT_NV12_4x4TILE, + [ilog2(HAL_COLOR_FORMAT_NV21_4x4TILE)] = HFI_COLOR_FORMAT_NV21_4x4TILE, + [ilog2(HAL_COLOR_FORMAT_YUYV)] = HFI_COLOR_FORMAT_YUYV, + [ilog2(HAL_COLOR_FORMAT_YVYU)] = HFI_COLOR_FORMAT_YVYU, + [ilog2(HAL_COLOR_FORMAT_UYVY)] = HFI_COLOR_FORMAT_UYVY, + [ilog2(HAL_COLOR_FORMAT_VYUY)] = HFI_COLOR_FORMAT_VYUY, + [ilog2(HAL_COLOR_FORMAT_RGB565)] = HFI_COLOR_FORMAT_RGB565, + [ilog2(HAL_COLOR_FORMAT_BGR565)] = HFI_COLOR_FORMAT_BGR565, + [ilog2(HAL_COLOR_FORMAT_RGB888)] = HFI_COLOR_FORMAT_RGB888, + [ilog2(HAL_COLOR_FORMAT_BGR888)] = HFI_COLOR_FORMAT_BGR888, + [ilog2(HAL_COLOR_FORMAT_RGBA8888)] = HFI_COLOR_FORMAT_RGBA8888, + /* UBWC Color formats*/ + [ilog2(HAL_COLOR_FORMAT_NV12_UBWC)] = HFI_COLOR_FORMAT_NV12_UBWC, + [ilog2(HAL_COLOR_FORMAT_NV12_TP10_UBWC)] = + HFI_COLOR_FORMAT_YUV420_TP10_UBWC, + [ilog2(HAL_COLOR_FORMAT_RGBA8888_UBWC)] = + HFI_COLOR_FORMAT_RGBA8888_UBWC, +}; + +static int nal_type[] = { + [ilog2(HAL_NAL_FORMAT_STARTCODES)] = HFI_NAL_FORMAT_STARTCODES, + [ilog2(HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER)] = + HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER, + [ilog2(HAL_NAL_FORMAT_ONE_BYTE_LENGTH)] = + HFI_NAL_FORMAT_ONE_BYTE_LENGTH, + [ilog2(HAL_NAL_FORMAT_TWO_BYTE_LENGTH)] = + HFI_NAL_FORMAT_TWO_BYTE_LENGTH, + [ilog2(HAL_NAL_FORMAT_FOUR_BYTE_LENGTH)] = + HFI_NAL_FORMAT_FOUR_BYTE_LENGTH, +}; + +static inline int hal_to_hfi_type(int property, int hal_type) +{ + if (hal_type <= 0 || roundup_pow_of_two(hal_type) != hal_type) { + /* Not a power of 2, it's not going + * to be in any of the tables anyway + */ + return -EINVAL; + } + + if (hal_type) + hal_type = ilog2(hal_type); + + switch (property) { + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + return (hal_type >= ARRAY_SIZE(profile_table)) ? + -ENOTSUPP : profile_table[hal_type]; + case HAL_PARAM_VENC_H264_ENTROPY_CONTROL: + return (hal_type >= ARRAY_SIZE(entropy_mode)) ? + -ENOTSUPP : entropy_mode[hal_type]; + case HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL: + return (hal_type >= ARRAY_SIZE(cabac_model)) ? + -ENOTSUPP : cabac_model[hal_type]; + case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT: + return (hal_type >= ARRAY_SIZE(color_format)) ? + -ENOTSUPP : color_format[hal_type]; + case HAL_PARAM_NAL_STREAM_FORMAT_SELECT: + return (hal_type >= ARRAY_SIZE(nal_type)) ? + -ENOTSUPP : nal_type[hal_type]; + case HAL_PARAM_VENC_MBI_STATISTICS_MODE: + return (hal_type >= ARRAY_SIZE(statistics_mode)) ? + -ENOTSUPP : statistics_mode[hal_type]; + default: + return -ENOTSUPP; + } +} + +u32 get_hfi_layout(enum hal_buffer_layout_type hal_buf_layout) +{ + u32 hfi_layout; + + switch (hal_buf_layout) { + case HAL_BUFFER_LAYOUT_TOP_BOTTOM: + hfi_layout = HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM; + break; + case HAL_BUFFER_LAYOUT_SEQ: + hfi_layout = HFI_MVC_BUFFER_LAYOUT_SEQ; + break; + default: + dprintk(VIDC_ERR, "Invalid buffer layout: %#x\n", + hal_buf_layout); + hfi_layout = HFI_MVC_BUFFER_LAYOUT_SEQ; + break; + } + return hfi_layout; +} + +enum hal_domain vidc_get_hal_domain(u32 hfi_domain) +{ + enum hal_domain hal_domain = 0; + + switch (hfi_domain) { + case HFI_VIDEO_DOMAIN_VPE: + hal_domain = HAL_VIDEO_DOMAIN_VPE; + break; + case HFI_VIDEO_DOMAIN_ENCODER: + hal_domain = HAL_VIDEO_DOMAIN_ENCODER; + break; + case HFI_VIDEO_DOMAIN_DECODER: + hal_domain = HAL_VIDEO_DOMAIN_DECODER; + break; + default: + dprintk(VIDC_ERR, "%s: invalid domain %x\n", + __func__, hfi_domain); + hal_domain = 0; + break; + } + return hal_domain; +} + +enum hal_video_codec vidc_get_hal_codec(u32 hfi_codec) +{ + enum hal_video_codec hal_codec = 0; + + switch (hfi_codec) { + case HFI_VIDEO_CODEC_H264: + hal_codec = HAL_VIDEO_CODEC_H264; + break; + case HFI_VIDEO_CODEC_H263: + hal_codec = HAL_VIDEO_CODEC_H263; + break; + case HFI_VIDEO_CODEC_MPEG1: + hal_codec = HAL_VIDEO_CODEC_MPEG1; + break; + case HFI_VIDEO_CODEC_MPEG2: + hal_codec = HAL_VIDEO_CODEC_MPEG2; + break; + case HFI_VIDEO_CODEC_MPEG4: + hal_codec = HAL_VIDEO_CODEC_MPEG4; + break; + case HFI_VIDEO_CODEC_DIVX_311: + hal_codec = HAL_VIDEO_CODEC_DIVX_311; + break; + case HFI_VIDEO_CODEC_DIVX: + hal_codec = HAL_VIDEO_CODEC_DIVX; + break; + case HFI_VIDEO_CODEC_VC1: + hal_codec = HAL_VIDEO_CODEC_VC1; + break; + case HFI_VIDEO_CODEC_SPARK: + hal_codec = HAL_VIDEO_CODEC_SPARK; + break; + case HFI_VIDEO_CODEC_VP8: + hal_codec = HAL_VIDEO_CODEC_VP8; + break; + case HFI_VIDEO_CODEC_HEVC: + hal_codec = HAL_VIDEO_CODEC_HEVC; + break; + case HFI_VIDEO_CODEC_VP9: + hal_codec = HAL_VIDEO_CODEC_VP9; + break; + case HFI_VIDEO_CODEC_HEVC_HYBRID: + hal_codec = HAL_VIDEO_CODEC_HEVC_HYBRID; + break; + default: + dprintk(VIDC_INFO, "%s: invalid codec 0x%x\n", + __func__, hfi_codec); + hal_codec = 0; + break; + } + return hal_codec; +} + + +u32 vidc_get_hfi_domain(enum hal_domain hal_domain) +{ + u32 hfi_domain; + + switch (hal_domain) { + case HAL_VIDEO_DOMAIN_VPE: + hfi_domain = HFI_VIDEO_DOMAIN_VPE; + break; + case HAL_VIDEO_DOMAIN_ENCODER: + hfi_domain = HFI_VIDEO_DOMAIN_ENCODER; + break; + case HAL_VIDEO_DOMAIN_DECODER: + hfi_domain = HFI_VIDEO_DOMAIN_DECODER; + break; + default: + dprintk(VIDC_ERR, "%s: invalid domain 0x%x\n", + __func__, hal_domain); + hfi_domain = 0; + break; + } + return hfi_domain; +} + +u32 vidc_get_hfi_codec(enum hal_video_codec hal_codec) +{ + u32 hfi_codec = 0; + + switch (hal_codec) { + case HAL_VIDEO_CODEC_MVC: + case HAL_VIDEO_CODEC_H264: + hfi_codec = HFI_VIDEO_CODEC_H264; + break; + case HAL_VIDEO_CODEC_H263: + hfi_codec = HFI_VIDEO_CODEC_H263; + break; + case HAL_VIDEO_CODEC_MPEG1: + hfi_codec = HFI_VIDEO_CODEC_MPEG1; + break; + case HAL_VIDEO_CODEC_MPEG2: + hfi_codec = HFI_VIDEO_CODEC_MPEG2; + break; + case HAL_VIDEO_CODEC_MPEG4: + hfi_codec = HFI_VIDEO_CODEC_MPEG4; + break; + case HAL_VIDEO_CODEC_DIVX_311: + hfi_codec = HFI_VIDEO_CODEC_DIVX_311; + break; + case HAL_VIDEO_CODEC_DIVX: + hfi_codec = HFI_VIDEO_CODEC_DIVX; + break; + case HAL_VIDEO_CODEC_VC1: + hfi_codec = HFI_VIDEO_CODEC_VC1; + break; + case HAL_VIDEO_CODEC_SPARK: + hfi_codec = HFI_VIDEO_CODEC_SPARK; + break; + case HAL_VIDEO_CODEC_VP8: + hfi_codec = HFI_VIDEO_CODEC_VP8; + break; + case HAL_VIDEO_CODEC_HEVC: + hfi_codec = HFI_VIDEO_CODEC_HEVC; + break; + case HAL_VIDEO_CODEC_VP9: + hfi_codec = HFI_VIDEO_CODEC_VP9; + break; + case HAL_VIDEO_CODEC_HEVC_HYBRID: + hfi_codec = HFI_VIDEO_CODEC_HEVC_HYBRID; + break; + default: + dprintk(VIDC_INFO, "%s: invalid codec 0x%x\n", + __func__, hal_codec); + hfi_codec = 0; + break; + } + return hfi_codec; +} + +static void create_pkt_enable(void *pkt, u32 type, bool enable) +{ + u32 *pkt_header = pkt; + u32 *pkt_type = &pkt_header[0]; + struct hfi_enable *hfi_enable = (struct hfi_enable *)&pkt_header[1]; + + *pkt_type = type; + hfi_enable->enable = enable; +} + +int create_pkt_cmd_sys_init(struct hfi_cmd_sys_init_packet *pkt, + u32 arch_type) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SYS_INIT; + pkt->size = sizeof(struct hfi_cmd_sys_init_packet); + pkt->arch_type = arch_type; + return rc; +} + +int create_pkt_cmd_sys_pc_prep(struct hfi_cmd_sys_pc_prep_packet *pkt) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SYS_PC_PREP; + pkt->size = sizeof(struct hfi_cmd_sys_pc_prep_packet); + return rc; +} + +int create_pkt_cmd_sys_idle_indicator( + struct hfi_cmd_sys_set_property_packet *pkt, + u32 enable) +{ + struct hfi_enable *hfi; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(struct hfi_enable) + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IDLE_INDICATOR; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = enable; + return 0; +} + +int create_pkt_cmd_sys_debug_config( + struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode) +{ + struct hfi_debug_config *hfi; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(struct hfi_debug_config) + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG; + hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1]; + hfi->debug_config = mode; + hfi->debug_mode = HFI_DEBUG_MODE_QUEUE; + if (msm_vidc_fw_debug_mode + <= (HFI_DEBUG_MODE_QUEUE | HFI_DEBUG_MODE_QDSS)) + hfi->debug_mode = msm_vidc_fw_debug_mode; + return 0; +} + +int create_pkt_cmd_sys_coverage_config( + struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode) +{ + if (!pkt) { + dprintk(VIDC_ERR, "In %s(), No input packet\n", __func__); + return -EINVAL; + } + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CONFIG_COVERAGE; + pkt->rg_property_data[1] = mode; + dprintk(VIDC_DBG, "Firmware coverage mode %d\n", + pkt->rg_property_data[1]); + return 0; +} + +int create_pkt_cmd_sys_set_resource( + struct hfi_cmd_sys_set_resource_packet *pkt, + struct vidc_resource_hdr *resource_hdr, + void *resource_value) +{ + int rc = 0; + + if (!pkt || !resource_hdr || !resource_value) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SYS_SET_RESOURCE; + pkt->size = sizeof(struct hfi_cmd_sys_set_resource_packet); + pkt->resource_handle = hash32_ptr(resource_hdr->resource_handle); + + switch (resource_hdr->resource_id) { + case VIDC_RESOURCE_OCMEM: + case VIDC_RESOURCE_VMEM: + { + struct hfi_resource_ocmem *hfioc_mem = + (struct hfi_resource_ocmem *) + &pkt->rg_resource_data[0]; + + phys_addr_t imem_addr = (phys_addr_t)resource_value; + + pkt->resource_type = HFI_RESOURCE_OCMEM; + pkt->size += sizeof(struct hfi_resource_ocmem) - sizeof(u32); + hfioc_mem->size = (u32)resource_hdr->size; + hfioc_mem->mem = imem_addr; + break; + } + default: + dprintk(VIDC_ERR, "Invalid resource_id %d\n", + resource_hdr->resource_id); + rc = -ENOTSUPP; + } + + return rc; +} + +int create_pkt_cmd_sys_release_resource( + struct hfi_cmd_sys_release_resource_packet *pkt, + struct vidc_resource_hdr *resource_hdr) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_release_resource_packet); + pkt->packet_type = HFI_CMD_SYS_RELEASE_RESOURCE; + pkt->resource_handle = hash32_ptr(resource_hdr->resource_handle); + + switch (resource_hdr->resource_id) { + case VIDC_RESOURCE_OCMEM: + case VIDC_RESOURCE_VMEM: + pkt->resource_type = HFI_RESOURCE_OCMEM; + break; + default: + dprintk(VIDC_ERR, "Invalid resource_id %d\n", + resource_hdr->resource_id); + rc = -ENOTSUPP; + } + + return rc; +} + +int create_pkt_cmd_sys_ping(struct hfi_cmd_sys_ping_packet *pkt) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_ping_packet); + pkt->packet_type = HFI_CMD_SYS_PING; + + return rc; +} + +inline int create_pkt_cmd_sys_session_init( + struct hfi_cmd_sys_session_init_packet *pkt, + struct hal_session *session, + u32 session_domain, u32 session_codec) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_sys_session_init_packet); + pkt->packet_type = HFI_CMD_SYS_SESSION_INIT; + pkt->session_id = hash32_ptr(session); + pkt->session_domain = vidc_get_hfi_domain(session_domain); + pkt->session_codec = vidc_get_hfi_codec(session_codec); + if (!pkt->session_codec) + return -EINVAL; + + return rc; +} + +int create_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt, + int pkt_type, struct hal_session *session) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + /* + * Legacy packetization should skip sending any 3xx specific session + * cmds. Add 3xx specific packetization to the switch case below. + */ + switch (pkt_type) { + case HFI_CMD_SESSION_CONTINUE: + dprintk(VIDC_INFO, + "%s - skip sending %x for legacy hfi\n", + __func__, pkt_type); + return -EPERM; + default: + break; + } + + pkt->size = sizeof(struct vidc_hal_session_cmd_pkt); + pkt->packet_type = pkt_type; + pkt->session_id = hash32_ptr(session); + + return rc; +} + +int create_3x_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt, + int pkt_type, struct hal_session *session) +{ + int rc = 0; + + if (!pkt) + return -EINVAL; + + pkt->size = sizeof(struct vidc_hal_session_cmd_pkt); + pkt->packet_type = pkt_type; + pkt->session_id = hash32_ptr(session); + + return rc; +} + +int create_pkt_cmd_sys_power_control( + struct hfi_cmd_sys_set_property_packet *pkt, u32 enable) +{ + struct hfi_enable *hfi; + + if (!pkt) { + dprintk(VIDC_ERR, "No input packet\n"); + return -EINVAL; + } + + pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) + + sizeof(struct hfi_enable) + sizeof(u32); + pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = enable; + return 0; +} + +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_INTERNAL_SCRATCH; + break; + case HAL_BUFFER_INTERNAL_SCRATCH_1: + buffer = HFI_BUFFER_INTERNAL_SCRATCH_1; + break; + case HAL_BUFFER_INTERNAL_SCRATCH_2: + buffer = HFI_BUFFER_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 get_hfi_extradata_index(enum hal_extradata_id index) +{ + int ret = 0; + + switch (index) { + case HAL_EXTRADATA_MB_QUANTIZATION: + ret = HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION; + break; + case HAL_EXTRADATA_INTERLACE_VIDEO: + ret = HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA; + break; + case HAL_EXTRADATA_VC1_FRAMEDISP: + ret = HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA; + break; + case HAL_EXTRADATA_VC1_SEQDISP: + ret = HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA; + break; + case HAL_EXTRADATA_TIMESTAMP: + ret = HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA; + break; + case HAL_EXTRADATA_S3D_FRAME_PACKING: + ret = HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA; + break; + case HAL_EXTRADATA_FRAME_RATE: + ret = HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA; + break; + case HAL_EXTRADATA_PANSCAN_WINDOW: + ret = HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA; + break; + case HAL_EXTRADATA_RECOVERY_POINT_SEI: + ret = HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_MULTISLICE_INFO: + ret = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO; + break; + case HAL_EXTRADATA_NUM_CONCEALED_MB: + ret = HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB; + break; + case HAL_EXTRADATA_ASPECT_RATIO: + case HAL_EXTRADATA_INPUT_CROP: + case HAL_EXTRADATA_DIGITAL_ZOOM: + case HAL_EXTRADATA_OUTPUT_CROP: + ret = HFI_PROPERTY_PARAM_INDEX_EXTRADATA; + break; + case HAL_EXTRADATA_MPEG2_SEQDISP: + ret = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA; + break; + case HAL_EXTRADATA_STREAM_USERDATA: + ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA; + break; + case HAL_EXTRADATA_DEC_FRAME_QP: + ret = HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA; + break; + case HAL_EXTRADATA_ENC_FRAME_QP: + ret = HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA; + break; + case HAL_EXTRADATA_FRAME_BITS_INFO: + ret = HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA; + break; + case HAL_EXTRADATA_LTR_INFO: + ret = HFI_PROPERTY_PARAM_VENC_LTR_INFO; + break; + case HAL_EXTRADATA_METADATA_MBI: + ret = HFI_PROPERTY_PARAM_VENC_MBI_DUMPING; + break; + case HAL_EXTRADATA_VQZIP_SEI: + ret = HFI_PROPERTY_PARAM_VDEC_VQZIP_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_YUV_STATS: + ret = HFI_PROPERTY_PARAM_VENC_YUVSTAT_INFO_EXTRADATA; + break; + case HAL_EXTRADATA_ROI_QP: + ret = HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA; + break; + case HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI: + ret = + HFI_PROPERTY_PARAM_VDEC_MASTERING_DISPLAY_COLOUR_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI: + ret = HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA; + break; + case HAL_EXTRADATA_VUI_DISPLAY_INFO: + ret = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA; + break; + case HAL_EXTRADATA_VPX_COLORSPACE: + ret = HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA; + break; + case HAL_EXTRADATA_PQ_INFO: + ret = HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA; + break; + default: + dprintk(VIDC_WARN, "Extradata index not found: %d\n", index); + break; + } + return ret; +} + +static int get_hfi_extradata_id(enum hal_extradata_id index) +{ + int ret = 0; + + switch (index) { + case HAL_EXTRADATA_ASPECT_RATIO: + ret = MSM_VIDC_EXTRADATA_ASPECT_RATIO; + break; + case HAL_EXTRADATA_INPUT_CROP: + ret = MSM_VIDC_EXTRADATA_INPUT_CROP; + break; + case HAL_EXTRADATA_DIGITAL_ZOOM: + ret = MSM_VIDC_EXTRADATA_DIGITAL_ZOOM; + break; + case HAL_EXTRADATA_OUTPUT_CROP: + ret = MSM_VIDC_EXTRADATA_OUTPUT_CROP; + break; + default: + ret = get_hfi_extradata_index(index); + break; + } + return ret; +} + +static u32 get_hfi_buf_mode(enum buffer_mode_type hal_buf_mode) +{ + u32 buf_mode; + + switch (hal_buf_mode) { + case HAL_BUFFER_MODE_STATIC: + buf_mode = HFI_BUFFER_MODE_STATIC; + break; + case HAL_BUFFER_MODE_RING: + buf_mode = HFI_BUFFER_MODE_RING; + break; + case HAL_BUFFER_MODE_DYNAMIC: + buf_mode = HFI_BUFFER_MODE_DYNAMIC; + break; + default: + dprintk(VIDC_ERR, "Invalid buffer mode: %#x\n", + hal_buf_mode); + buf_mode = 0; + break; + } + return buf_mode; +} + +static u32 get_hfi_ltr_mode(enum ltr_mode ltr_mode_type) +{ + u32 ltrmode; + + switch (ltr_mode_type) { + case HAL_LTR_MODE_DISABLE: + ltrmode = HFI_LTR_MODE_DISABLE; + break; + case HAL_LTR_MODE_MANUAL: + ltrmode = HFI_LTR_MODE_MANUAL; + break; + case HAL_LTR_MODE_PERIODIC: + ltrmode = HFI_LTR_MODE_PERIODIC; + break; + default: + dprintk(VIDC_ERR, "Invalid ltr mode: %#x\n", + ltr_mode_type); + ltrmode = HFI_LTR_MODE_DISABLE; + break; + } + return ltrmode; +} + +int create_pkt_cmd_session_set_buffers( + struct hfi_cmd_session_set_buffers_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info) +{ + int rc = 0; + int i = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SESSION_SET_BUFFERS; + pkt->session_id = hash32_ptr(session); + pkt->buffer_size = buffer_info->buffer_size; + pkt->min_buffer_size = buffer_info->buffer_size; + pkt->num_buffers = buffer_info->num_buffers; + + if (buffer_info->buffer_type == HAL_BUFFER_OUTPUT || + buffer_info->buffer_type == HAL_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + pkt->extra_data_size = buffer_info->extradata_size; + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) - + sizeof(u32) + (buffer_info->num_buffers * + sizeof(struct hfi_buffer_info)); + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + for (i = 0; i < pkt->num_buffers; i++) { + buff->buffer_addr = + (u32)buffer_info->align_device_addr; + buff->extra_data_addr = + (u32)buffer_info->extradata_addr; + } + } else { + pkt->extra_data_size = 0; + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) + + ((buffer_info->num_buffers - 1) * sizeof(u32)); + for (i = 0; i < pkt->num_buffers; i++) { + pkt->rg_buffer_info[i] = + (u32)buffer_info->align_device_addr; + } + } + + pkt->buffer_type = get_hfi_buffer(buffer_info->buffer_type); + if (!pkt->buffer_type) + return -EINVAL; + + return rc; +} + +int create_pkt_cmd_session_release_buffers( + struct hfi_cmd_session_release_buffer_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info) +{ + int rc = 0; + int i = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->packet_type = HFI_CMD_SESSION_RELEASE_BUFFERS; + pkt->session_id = hash32_ptr(session); + pkt->buffer_size = buffer_info->buffer_size; + pkt->num_buffers = buffer_info->num_buffers; + + if (buffer_info->buffer_type == HAL_BUFFER_OUTPUT || + buffer_info->buffer_type == HAL_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + for (i = 0; i < pkt->num_buffers; i++) { + buff->buffer_addr = + (u32)buffer_info->align_device_addr; + buff->extra_data_addr = + (u32)buffer_info->extradata_addr; + } + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) - + sizeof(u32) + (buffer_info->num_buffers * + sizeof(struct hfi_buffer_info)); + } else { + for (i = 0; i < pkt->num_buffers; i++) { + pkt->rg_buffer_info[i] = + (u32)buffer_info->align_device_addr; + } + pkt->extra_data_size = 0; + pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) + + ((buffer_info->num_buffers - 1) * sizeof(u32)); + } + pkt->response_req = buffer_info->response_required; + pkt->buffer_type = get_hfi_buffer(buffer_info->buffer_type); + if (!pkt->buffer_type) + return -EINVAL; + return rc; +} + +int create_pkt_cmd_session_etb_decoder( + struct hfi_cmd_session_empty_buffer_compressed_packet *pkt, + struct hal_session *session, struct vidc_frame_data *input_frame) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = + sizeof(struct hfi_cmd_session_empty_buffer_compressed_packet); + pkt->packet_type = HFI_CMD_SESSION_EMPTY_BUFFER; + pkt->session_id = hash32_ptr(session); + pkt->time_stamp_hi = upper_32_bits(input_frame->timestamp); + pkt->time_stamp_lo = lower_32_bits(input_frame->timestamp); + pkt->flags = input_frame->flags; + pkt->mark_target = input_frame->mark_target; + pkt->mark_data = input_frame->mark_data; + pkt->offset = input_frame->offset; + pkt->alloc_len = input_frame->alloc_len; + pkt->filled_len = input_frame->filled_len; + pkt->input_tag = input_frame->clnt_data; + pkt->packet_buffer = (u32)input_frame->device_addr; + + trace_msm_v4l2_vidc_buffer_event_start("ETB", + input_frame->device_addr, input_frame->timestamp, + input_frame->alloc_len, input_frame->filled_len, + input_frame->offset); + + if (!pkt->packet_buffer) + rc = -EINVAL; + return rc; +} + +int create_pkt_cmd_session_etb_encoder( + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet *pkt, + struct hal_session *session, struct vidc_frame_data *input_frame) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct + hfi_cmd_session_empty_buffer_uncompressed_plane0_packet); + pkt->packet_type = HFI_CMD_SESSION_EMPTY_BUFFER; + pkt->session_id = hash32_ptr(session); + pkt->view_id = 0; + pkt->time_stamp_hi = upper_32_bits(input_frame->timestamp); + pkt->time_stamp_lo = lower_32_bits(input_frame->timestamp); + pkt->flags = input_frame->flags; + pkt->mark_target = input_frame->mark_target; + pkt->mark_data = input_frame->mark_data; + pkt->offset = input_frame->offset; + pkt->alloc_len = input_frame->alloc_len; + pkt->filled_len = input_frame->filled_len; + pkt->input_tag = input_frame->clnt_data; + pkt->packet_buffer = (u32)input_frame->device_addr; + pkt->extra_data_buffer = (u32)input_frame->extradata_addr; + + trace_msm_v4l2_vidc_buffer_event_start("ETB", + input_frame->device_addr, input_frame->timestamp, + input_frame->alloc_len, input_frame->filled_len, + input_frame->offset); + + if (!pkt->packet_buffer) + rc = -EINVAL; + return rc; +} + +int create_pkt_cmd_session_ftb(struct hfi_cmd_session_fill_buffer_packet *pkt, + struct hal_session *session, + struct vidc_frame_data *output_frame) +{ + int rc = 0; + + if (!pkt || !session || !output_frame) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_fill_buffer_packet); + pkt->packet_type = HFI_CMD_SESSION_FILL_BUFFER; + pkt->session_id = hash32_ptr(session); + + if (output_frame->buffer_type == HAL_BUFFER_OUTPUT) + pkt->stream_id = 0; + else if (output_frame->buffer_type == HAL_BUFFER_OUTPUT2) + pkt->stream_id = 1; + + if (!output_frame->device_addr) + return -EINVAL; + + pkt->packet_buffer = (u32)output_frame->device_addr; + pkt->extra_data_buffer = (u32)output_frame->extradata_addr; + pkt->alloc_len = output_frame->alloc_len; + pkt->filled_len = output_frame->filled_len; + pkt->offset = output_frame->offset; + pkt->rgData[0] = output_frame->extradata_size; + + trace_msm_v4l2_vidc_buffer_event_start("FTB", + output_frame->device_addr, output_frame->timestamp, + output_frame->alloc_len, output_frame->filled_len, + output_frame->offset); + dprintk(VIDC_DBG, "### Q OUTPUT BUFFER ###: %d, %d, %d\n", + pkt->alloc_len, pkt->filled_len, pkt->offset); + + return rc; +} + +int create_pkt_cmd_session_parse_seq_header( + struct hfi_cmd_session_parse_sequence_header_packet *pkt, + struct hal_session *session, struct vidc_seq_hdr *seq_hdr) +{ + int rc = 0; + + if (!pkt || !session || !seq_hdr) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_parse_sequence_header_packet); + pkt->packet_type = HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER; + pkt->session_id = hash32_ptr(session); + pkt->header_len = seq_hdr->seq_hdr_len; + if (!seq_hdr->seq_hdr) + return -EINVAL; + pkt->packet_buffer = (u32)seq_hdr->seq_hdr; + return rc; +} + +int create_pkt_cmd_session_get_seq_hdr( + struct hfi_cmd_session_get_sequence_header_packet *pkt, + struct hal_session *session, struct vidc_seq_hdr *seq_hdr) +{ + int rc = 0; + + if (!pkt || !session || !seq_hdr) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_get_sequence_header_packet); + pkt->packet_type = HFI_CMD_SESSION_GET_SEQUENCE_HEADER; + pkt->session_id = hash32_ptr(session); + pkt->buffer_len = seq_hdr->seq_hdr_len; + if (!seq_hdr->seq_hdr) + return -EINVAL; + pkt->packet_buffer = (u32)seq_hdr->seq_hdr; + return rc; +} + +int create_pkt_cmd_session_get_buf_req( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_get_property_packet); + pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS; + + return rc; +} + +int create_pkt_cmd_session_flush(struct hfi_cmd_session_flush_packet *pkt, + struct hal_session *session, enum hal_flush flush_mode) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_flush_packet); + pkt->packet_type = HFI_CMD_SESSION_FLUSH; + pkt->session_id = hash32_ptr(session); + switch (flush_mode) { + case HAL_FLUSH_INPUT: + pkt->flush_type = HFI_FLUSH_INPUT; + break; + case HAL_FLUSH_OUTPUT: + pkt->flush_type = HFI_FLUSH_OUTPUT; + break; + case HAL_FLUSH_ALL: + pkt->flush_type = HFI_FLUSH_ALL; + break; + default: + dprintk(VIDC_ERR, "Invalid flush mode: %#x\n", flush_mode); + return -EINVAL; + } + return rc; +} + +int create_pkt_cmd_session_get_property( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session, enum hal_property ptype) +{ + int rc = 0; + + if (!pkt || !session) { + dprintk(VIDC_ERR, "%s Invalid parameters\n", __func__); + return -EINVAL; + } + pkt->size = sizeof(struct hfi_cmd_session_get_property_packet); + pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + switch (ptype) { + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + break; + default: + dprintk(VIDC_ERR, "%s cmd:%#x not supported\n", __func__, + ptype); + rc = -EINVAL; + break; + } + return rc; +} + +int create_3x_pkt_cmd_session_get_property( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session, enum hal_property ptype) +{ + int rc = 0; + + if (!pkt || !session) { + dprintk(VIDC_ERR, "%s Invalid parameters\n", __func__); + return -EINVAL; + } + pkt->size = sizeof(struct hfi_cmd_session_get_property_packet); + pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + switch (ptype) { + case HAL_CONFIG_VDEC_ENTROPY: + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY; + break; + default: + rc = create_pkt_cmd_session_get_property(pkt, + session, ptype); + } + return rc; +} + +int create_pkt_cmd_session_set_property( + struct hfi_cmd_session_set_property_packet *pkt, + struct hal_session *session, + enum hal_property ptype, void *pdata) +{ + int rc = 0; + + if (!pkt || !session) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_set_property_packet); + pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + + switch (ptype) { + case HAL_CONFIG_FRAME_RATE: + { + u32 buffer_type; + struct hfi_frame_rate *hfi; + struct hal_frame_rate *prop = (struct hal_frame_rate *) pdata; + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_FRAME_RATE; + hfi = (struct hfi_frame_rate *) &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->frame_rate = prop->frame_rate; + pkt->size += sizeof(u32) + sizeof(struct hfi_frame_rate); + break; + } + case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT: + { + u32 buffer_type; + struct hfi_uncompressed_format_select *hfi; + struct hal_uncompressed_format_select *prop = + (struct hal_uncompressed_format_select *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; + + hfi = (struct hfi_uncompressed_format_select *) + &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + hfi->format = hal_to_hfi_type( + HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, + prop->format); + pkt->size += sizeof(u32) + + sizeof(struct hfi_uncompressed_format_select); + break; + } + case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: + break; + case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: + break; + case HAL_PARAM_EXTRA_DATA_HEADER_CONFIG: + break; + case HAL_PARAM_FRAME_SIZE: + { + struct hfi_frame_size *hfi; + struct hal_frame_size *prop = (struct hal_frame_size *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_FRAME_SIZE; + hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->height = prop->height; + hfi->width = prop->width; + pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size); + break; + } + case HAL_CONFIG_REALTIME: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_REALTIME, + (((struct hal_enable *) pdata)->enable)); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_BUFFER_COUNT_ACTUAL: + { + struct hfi_buffer_count_actual *hfi; + struct hal_buffer_count_actual *prop = + (struct hal_buffer_count_actual *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; + hfi = (struct hfi_buffer_count_actual *) + &pkt->rg_property_data[1]; + hfi->buffer_count_actual = prop->buffer_count_actual; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + pkt->size += sizeof(u32) + sizeof(struct + hfi_buffer_count_actual); + + break; + } + case HAL_PARAM_NAL_STREAM_FORMAT_SELECT: + { + struct hfi_nal_stream_format_select *hfi; + struct hal_nal_stream_format_select *prop = + (struct hal_nal_stream_format_select *)pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT; + hfi = (struct hfi_nal_stream_format_select *) + &pkt->rg_property_data[1]; + dprintk(VIDC_DBG, "data is :%d\n", + prop->nal_stream_format_select); + hfi->nal_stream_format_select = hal_to_hfi_type( + HAL_PARAM_NAL_STREAM_FORMAT_SELECT, + prop->nal_stream_format_select); + pkt->size += sizeof(u32) + + sizeof(struct hfi_nal_stream_format_select); + break; + } + case HAL_PARAM_VDEC_OUTPUT_ORDER: + { + int *data = (int *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER; + switch (*data) { + case HAL_OUTPUT_ORDER_DECODE: + pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DECODE; + break; + case HAL_OUTPUT_ORDER_DISPLAY: + pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DISPLAY; + break; + default: + dprintk(VIDC_ERR, "invalid output order: %#x\n", + *data); + break; + } + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE: + { + struct hfi_enable_picture *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE; + hfi = (struct hfi_enable_picture *) &pkt->rg_property_data[1]; + hfi->picture_type = + ((struct hfi_enable_picture *)pdata)->picture_type; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_MULTI_STREAM: + { + struct hfi_multi_stream *hfi; + struct hal_multi_stream *prop = + (struct hal_multi_stream *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + hfi = (struct hfi_multi_stream *) &pkt->rg_property_data[1]; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + hfi->enable = prop->enable; + hfi->width = prop->width; + hfi->height = prop->height; + pkt->size += sizeof(u32) + sizeof(struct hfi_multi_stream); + break; + } + case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT: + { + struct hfi_display_picture_buffer_count *hfi; + struct hal_display_picture_buffer_count *prop = + (struct hal_display_picture_buffer_count *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT; + hfi = (struct hfi_display_picture_buffer_count *) + &pkt->rg_property_data[1]; + hfi->count = prop->count; + hfi->enable = prop->enable; + pkt->size += sizeof(u32) + + sizeof(struct hfi_display_picture_buffer_count); + break; + } + case HAL_PARAM_DIVX_FORMAT: + { + int *data = pdata; + + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_DIVX_FORMAT; + switch (*data) { + case HAL_DIVX_FORMAT_4: + pkt->rg_property_data[1] = HFI_DIVX_FORMAT_4; + break; + case HAL_DIVX_FORMAT_5: + pkt->rg_property_data[1] = HFI_DIVX_FORMAT_5; + break; + case HAL_DIVX_FORMAT_6: + pkt->rg_property_data[1] = HFI_DIVX_FORMAT_6; + break; + default: + dprintk(VIDC_ERR, "Invalid divx format: %#x\n", *data); + break; + } + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_SYNC_FRAME_DECODE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VENC_REQUEST_IFRAME: + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME; + pkt->size += sizeof(u32); + break; + case HAL_PARAM_VENC_MPEG4_SHORT_HEADER: + break; + case HAL_PARAM_VENC_MPEG4_AC_PREDICTION: + break; + case HAL_CONFIG_VENC_TARGET_BITRATE: + { + struct hfi_bitrate *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE; + hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1]; + hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate; + hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id; + pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate); + break; + } + case HAL_CONFIG_VENC_MAX_BITRATE: + { + struct hfi_bitrate *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE; + hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1]; + hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate; + hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id; + + pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate); + break; + } + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + { + struct hfi_profile_level *hfi; + struct hal_profile_level *prop = + (struct hal_profile_level *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + hfi = (struct hfi_profile_level *) + &pkt->rg_property_data[1]; + hfi->level = prop->level; + hfi->profile = hal_to_hfi_type(HAL_PARAM_PROFILE_LEVEL_CURRENT, + prop->profile); + if (hfi->profile == 0) { + hfi->profile = HFI_H264_PROFILE_HIGH; + dprintk(VIDC_WARN, + "Profile %d not supported, falling back to high\n", + prop->profile); + } + + if (!hfi->level) { + hfi->level = 1; + dprintk(VIDC_WARN, + "Level %d not supported, falling back to high\n", + prop->level); + } + + pkt->size += sizeof(u32) + sizeof(struct hfi_profile_level); + break; + } + case HAL_PARAM_VENC_H264_ENTROPY_CONTROL: + { + struct hfi_h264_entropy_control *hfi; + struct hal_h264_entropy_control *prop = + (struct hal_h264_entropy_control *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL; + hfi = (struct hfi_h264_entropy_control *) + &pkt->rg_property_data[1]; + hfi->entropy_mode = hal_to_hfi_type( + HAL_PARAM_VENC_H264_ENTROPY_CONTROL, + prop->entropy_mode); + if (hfi->entropy_mode == HAL_H264_ENTROPY_CABAC) + hfi->cabac_model = hal_to_hfi_type( + HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL, + prop->cabac_model); + pkt->size += sizeof(u32) + sizeof( + struct hfi_h264_entropy_control); + break; + } + case HAL_PARAM_VENC_RATE_CONTROL: + { + u32 *rc; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_RATE_CONTROL; + rc = (u32 *)pdata; + switch ((enum hal_rate_control) *rc) { + case HAL_RATE_CONTROL_OFF: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_OFF; + break; + case HAL_RATE_CONTROL_CBR_CFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_CBR_CFR; + break; + case HAL_RATE_CONTROL_CBR_VFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_CBR_VFR; + break; + case HAL_RATE_CONTROL_VBR_CFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_CFR; + break; + case HAL_RATE_CONTROL_VBR_VFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_VFR; + break; + case HAL_RATE_CONTROL_MBR_CFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_CFR; + break; + case HAL_RATE_CONTROL_MBR_VFR: + pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_VFR; + break; + default: + dprintk(VIDC_ERR, + "Invalid Rate control setting: %pK\n", + pdata); + break; + } + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION: + { + struct hfi_mpeg4_time_resolution *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION; + hfi = (struct hfi_mpeg4_time_resolution *) + &pkt->rg_property_data[1]; + hfi->time_increment_resolution = + ((struct hal_mpeg4_time_resolution *)pdata)-> + time_increment_resolution; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION: + { + struct hfi_mpeg4_header_extension *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION; + hfi = (struct hfi_mpeg4_header_extension *) + &pkt->rg_property_data[1]; + hfi->header_extension = (u32)(unsigned long) pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL: + { + struct hfi_h264_db_control *hfi; + struct hal_h264_db_control *prop = + (struct hal_h264_db_control *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL; + hfi = (struct hfi_h264_db_control *) &pkt->rg_property_data[1]; + switch (prop->mode) { + case HAL_H264_DB_MODE_DISABLE: + hfi->mode = HFI_H264_DB_MODE_DISABLE; + break; + case HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY: + hfi->mode = HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY; + break; + case HAL_H264_DB_MODE_ALL_BOUNDARY: + hfi->mode = HFI_H264_DB_MODE_ALL_BOUNDARY; + break; + default: + dprintk(VIDC_ERR, "Invalid deblocking mode: %#x\n", + prop->mode); + break; + } + hfi->slice_alpha_offset = prop->slice_alpha_offset; + hfi->slice_beta_offset = prop->slice_beta_offset; + pkt->size += sizeof(u32) + + sizeof(struct hfi_h264_db_control); + break; + } + case HAL_PARAM_VENC_SESSION_QP: + { + struct hfi_quantization *hfi; + struct hal_quantization *hal_quant = + (struct hal_quantization *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_SESSION_QP; + hfi = (struct hfi_quantization *) &pkt->rg_property_data[1]; + hfi->qp_i = hal_quant->qpi; + hfi->qp_p = hal_quant->qpp; + hfi->qp_b = hal_quant->qpb; + hfi->layer_id = hal_quant->layer_id; + pkt->size += sizeof(u32) + sizeof(struct hfi_quantization); + break; + } + case HAL_PARAM_VENC_SESSION_QP_RANGE: + { + struct hfi_quantization_range *hfi; + struct hfi_quantization_range *hal_range = + (struct hfi_quantization_range *) pdata; + u32 min_qp, max_qp; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; + hfi = (struct hfi_quantization_range *) + &pkt->rg_property_data[1]; + + min_qp = hal_range->min_qp; + max_qp = hal_range->max_qp; + + /* We'll be packing in the qp, so make sure we + * won't be losing data when masking + */ + if (min_qp > 0xff || max_qp > 0xff) { + dprintk(VIDC_ERR, "qp value out of range\n"); + rc = -ERANGE; + break; + } + + /* When creating the packet, pack the qp value as + * 0xiippbb, where ii = qp range for I-frames, + * pp = qp range for P-frames, etc. + */ + hfi->min_qp = min_qp | min_qp << 8 | min_qp << 16; + hfi->max_qp = max_qp | max_qp << 8 | max_qp << 16; + hfi->layer_id = hal_range->layer_id; + + pkt->size += sizeof(u32) + + sizeof(struct hfi_quantization_range); + break; + } + case HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED: + { + struct hfi_quantization_range *hfi; + struct hfi_quantization_range *hal_range = + (struct hfi_quantization_range *) pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE; + hfi = (struct hfi_quantization_range *) + &pkt->rg_property_data[1]; + + hfi->min_qp = hal_range->min_qp; + hfi->max_qp = hal_range->max_qp; + hfi->layer_id = hal_range->layer_id; + + pkt->size += sizeof(u32) + + sizeof(struct hfi_quantization_range); + break; + } + case HAL_PARAM_VENC_SEARCH_RANGE: + { + struct hfi_vc1e_perf_cfg_type *hfi; + struct hal_vc1e_perf_cfg_type *hal_mv_searchrange = + (struct hal_vc1e_perf_cfg_type *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG; + hfi = (struct hfi_vc1e_perf_cfg_type *) + &pkt->rg_property_data[1]; + hfi->search_range_x_subsampled[0] = + hal_mv_searchrange->i_frame.x_subsampled; + hfi->search_range_x_subsampled[1] = + hal_mv_searchrange->p_frame.x_subsampled; + hfi->search_range_x_subsampled[2] = + hal_mv_searchrange->b_frame.x_subsampled; + hfi->search_range_y_subsampled[0] = + hal_mv_searchrange->i_frame.y_subsampled; + hfi->search_range_y_subsampled[1] = + hal_mv_searchrange->p_frame.y_subsampled; + hfi->search_range_y_subsampled[2] = + hal_mv_searchrange->b_frame.y_subsampled; + pkt->size += sizeof(u32) + + sizeof(struct hfi_vc1e_perf_cfg_type); + break; + } + case HAL_PARAM_VENC_MAX_NUM_B_FRAMES: + { + struct hfi_max_num_b_frames *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES; + hfi = (struct hfi_max_num_b_frames *) &pkt->rg_property_data[1]; + memcpy(hfi, (struct hfi_max_num_b_frames *) pdata, + sizeof(struct hfi_max_num_b_frames)); + pkt->size += sizeof(u32) + sizeof(struct hfi_max_num_b_frames); + break; + } + case HAL_CONFIG_VENC_INTRA_PERIOD: + { + struct hfi_intra_period *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD; + hfi = (struct hfi_intra_period *) &pkt->rg_property_data[1]; + memcpy(hfi, (struct hfi_intra_period *) pdata, + sizeof(struct hfi_intra_period)); + pkt->size += sizeof(u32) + sizeof(struct hfi_intra_period); + break; + } + case HAL_CONFIG_VENC_IDR_PERIOD: + { + struct hfi_idr_period *hfi; + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD; + hfi = (struct hfi_idr_period *) &pkt->rg_property_data[1]; + hfi->idr_period = ((struct hfi_idr_period *) pdata)->idr_period; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_CONCEAL_COLOR: + { + struct hfi_conceal_color *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR; + hfi = (struct hfi_conceal_color *) &pkt->rg_property_data[1]; + if (hfi) + hfi->conceal_color = + ((struct hfi_conceal_color *) pdata)-> + conceal_color; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VPE_OPERATIONS: + { + struct hfi_operations_type *hfi; + + struct hal_operations *prop = + (struct hal_operations *) pdata; + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VPE_OPERATIONS; + hfi = (struct hfi_operations_type *) &pkt->rg_property_data[1]; + switch (prop->rotate) { + case HAL_ROTATE_NONE: + hfi->rotation = HFI_ROTATE_NONE; + break; + case HAL_ROTATE_90: + hfi->rotation = HFI_ROTATE_90; + break; + case HAL_ROTATE_180: + hfi->rotation = HFI_ROTATE_180; + break; + case HAL_ROTATE_270: + hfi->rotation = HFI_ROTATE_270; + break; + default: + dprintk(VIDC_ERR, "Invalid rotation setting: %#x\n", + prop->rotate); + rc = -EINVAL; + break; + } + switch (prop->flip) { + case HAL_FLIP_NONE: + hfi->flip = HFI_FLIP_NONE; + break; + case HAL_FLIP_HORIZONTAL: + hfi->flip = HFI_FLIP_HORIZONTAL; + break; + case HAL_FLIP_VERTICAL: + hfi->flip = HFI_FLIP_VERTICAL; + break; + default: + dprintk(VIDC_ERR, "Invalid flip setting: %#x\n", + prop->flip); + rc = -EINVAL; + break; + } + pkt->size += sizeof(u32) + sizeof(struct hfi_operations_type); + break; + } + case HAL_PARAM_VENC_INTRA_REFRESH: + { + struct hfi_intra_refresh *hfi; + struct hal_intra_refresh *prop = + (struct hal_intra_refresh *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH; + hfi = (struct hfi_intra_refresh *) &pkt->rg_property_data[1]; + switch (prop->mode) { + case HAL_INTRA_REFRESH_NONE: + hfi->mode = HFI_INTRA_REFRESH_NONE; + break; + case HAL_INTRA_REFRESH_ADAPTIVE: + hfi->mode = HFI_INTRA_REFRESH_ADAPTIVE; + break; + case HAL_INTRA_REFRESH_CYCLIC: + hfi->mode = HFI_INTRA_REFRESH_CYCLIC; + break; + case HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE: + hfi->mode = HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE; + break; + case HAL_INTRA_REFRESH_RANDOM: + hfi->mode = HFI_INTRA_REFRESH_RANDOM; + break; + default: + dprintk(VIDC_ERR, + "Invalid intra refresh setting: %#x\n", + prop->mode); + break; + } + hfi->air_mbs = prop->air_mbs; + hfi->air_ref = prop->air_ref; + hfi->cir_mbs = prop->cir_mbs; + pkt->size += sizeof(u32) + sizeof(struct hfi_intra_refresh); + break; + } + case HAL_PARAM_VENC_MULTI_SLICE_CONTROL: + { + struct hfi_multi_slice_control *hfi; + struct hal_multi_slice_control *prop = + (struct hal_multi_slice_control *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL; + hfi = (struct hfi_multi_slice_control *) + &pkt->rg_property_data[1]; + switch (prop->multi_slice) { + case HAL_MULTI_SLICE_OFF: + hfi->multi_slice = HFI_MULTI_SLICE_OFF; + break; + case HAL_MULTI_SLICE_GOB: + hfi->multi_slice = HFI_MULTI_SLICE_GOB; + break; + case HAL_MULTI_SLICE_BY_MB_COUNT: + hfi->multi_slice = HFI_MULTI_SLICE_BY_MB_COUNT; + break; + case HAL_MULTI_SLICE_BY_BYTE_COUNT: + hfi->multi_slice = HFI_MULTI_SLICE_BY_BYTE_COUNT; + break; + default: + dprintk(VIDC_ERR, "Invalid slice settings: %#x\n", + prop->multi_slice); + break; + } + hfi->slice_size = prop->slice_size; + pkt->size += sizeof(u32) + sizeof(struct + hfi_multi_slice_control); + break; + } + case HAL_PARAM_INDEX_EXTRADATA: + { + struct hfi_index_extradata_config *hfi; + struct hal_extradata_enable *extra = pdata; + int id = 0; + + pkt->rg_property_data[0] = + get_hfi_extradata_index(extra->index); + hfi = (struct hfi_index_extradata_config *) + &pkt->rg_property_data[1]; + hfi->enable = extra->enable; + id = get_hfi_extradata_id(extra->index); + if (id) + hfi->index_extra_data_id = id; + else { + dprintk(VIDC_WARN, + "Failed to find extradata id: %d\n", + id); + rc = -EINVAL; + } + pkt->size += sizeof(u32) + + sizeof(struct hfi_index_extradata_config); + break; + } + case HAL_PARAM_VENC_SLICE_DELIVERY_MODE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_VUI_TIMING_INFO: + { + struct hfi_h264_vui_timing_info *hfi; + struct hal_h264_vui_timing_info *timing_info = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO; + + hfi = (struct hfi_h264_vui_timing_info *) + &pkt->rg_property_data[1]; + hfi->enable = timing_info->enable; + hfi->fixed_frame_rate = timing_info->fixed_frame_rate; + hfi->time_scale = timing_info->time_scale; + + pkt->size += sizeof(u32) + + sizeof(struct hfi_h264_vui_timing_info); + break; + } + case HAL_CONFIG_VPE_DEINTERLACE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_CONFIG_VPE_DEINTERLACE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_GENERATE_AUDNAL: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_BUFFER_ALLOC_MODE: + { + u32 buffer_type; + u32 buffer_mode; + struct hfi_buffer_alloc_mode *hfi; + struct hal_buffer_alloc_mode *alloc_info = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; + hfi = (struct hfi_buffer_alloc_mode *) + &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(alloc_info->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + buffer_mode = get_hfi_buf_mode(alloc_info->buffer_mode); + if (buffer_mode) + hfi->buffer_mode = buffer_mode; + else + return -EINVAL; + pkt->size += sizeof(u32) + sizeof(struct hfi_buffer_alloc_mode); + break; + } + case HAL_PARAM_VDEC_FRAME_ASSEMBLY: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VDEC_SCS_THRESHOLD: + { + struct hfi_scs_threshold *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD; + hfi = (struct hfi_scs_threshold *) &pkt->rg_property_data[1]; + hfi->threshold_value = + ((struct hal_scs_threshold *) pdata)->threshold_value; + pkt->size += sizeof(u32) + sizeof(struct hfi_scs_threshold); + break; + } + case HAL_PARAM_MVC_BUFFER_LAYOUT: + { + struct hfi_mvc_buffer_layout_descp_type *hfi; + struct hal_mvc_buffer_layout *layout_info = pdata; + + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT; + hfi = (struct hfi_mvc_buffer_layout_descp_type *) + &pkt->rg_property_data[1]; + hfi->layout_type = get_hfi_layout(layout_info->layout_type); + hfi->bright_view_first = layout_info->bright_view_first; + hfi->ngap = layout_info->ngap; + pkt->size += sizeof(u32) + + sizeof(struct hfi_mvc_buffer_layout_descp_type); + break; + } + case HAL_PARAM_VENC_LTRMODE: + { + struct hfi_ltr_mode *hfi; + struct hal_ltr_mode *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_LTRMODE; + hfi = (struct hfi_ltr_mode *) &pkt->rg_property_data[1]; + hfi->ltr_mode = get_hfi_ltr_mode(hal->mode); + hfi->ltr_count = hal->count; + hfi->trust_mode = hal->trust_mode; + pkt->size += sizeof(u32) + sizeof(struct hfi_ltr_mode); + break; + } + case HAL_CONFIG_VENC_USELTRFRAME: + { + struct hfi_ltr_use *hfi; + struct hal_ltr_use *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_USELTRFRAME; + hfi = (struct hfi_ltr_use *) &pkt->rg_property_data[1]; + hfi->frames = hal->frames; + hfi->ref_ltr = hal->ref_ltr; + hfi->use_constrnt = hal->use_constraint; + pkt->size += sizeof(u32) + sizeof(struct hfi_ltr_use); + break; + } + case HAL_CONFIG_VENC_MARKLTRFRAME: + { + struct hfi_ltr_mark *hfi; + struct hal_ltr_mark *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME; + hfi = (struct hfi_ltr_mark *) &pkt->rg_property_data[1]; + hfi->mark_frame = hal->mark_frame; + pkt->size += sizeof(u32) + sizeof(struct hfi_ltr_mark); + break; + } + case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VENC_HIER_P_NUM_FRAMES: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_ENABLE_INITIAL_QP: + { + struct hfi_initial_quantization *hfi; + struct hal_initial_quantization *quant = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_INITIAL_QP; + hfi = (struct hfi_initial_quantization *) + &pkt->rg_property_data[1]; + hfi->init_qp_enable = quant->init_qp_enable; + hfi->qp_i = quant->qpi; + hfi->qp_p = quant->qpp; + hfi->qp_b = quant->qpb; + pkt->size += sizeof(u32) + + sizeof(struct hfi_initial_quantization); + break; + } + case HAL_PARAM_VPE_COLOR_SPACE_CONVERSION: + { + struct hfi_vpe_color_space_conversion *hfi = NULL; + struct hal_vpe_color_space_conversion *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION; + hfi = (struct hfi_vpe_color_space_conversion *) + &pkt->rg_property_data[1]; + memcpy(hfi->csc_matrix, hal->csc_matrix, + sizeof(hfi->csc_matrix)); + memcpy(hfi->csc_bias, hal->csc_bias, sizeof(hfi->csc_bias)); + memcpy(hfi->csc_limit, hal->csc_limit, sizeof(hfi->csc_limit)); + pkt->size += sizeof(u32) + + sizeof(struct hfi_vpe_color_space_conversion); + break; + } + case HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_NAL_SVC_EXT: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_CONFIG_VENC_PERF_MODE: + { + u32 hfi_perf_mode = 0; + enum hal_perf_mode hal_perf_mode = *(enum hal_perf_mode *)pdata; + + switch (hal_perf_mode) { + case HAL_PERF_MODE_POWER_SAVE: + hfi_perf_mode = HFI_VENC_PERFMODE_POWER_SAVE; + break; + case HAL_PERF_MODE_POWER_MAX_QUALITY: + hfi_perf_mode = HFI_VENC_PERFMODE_MAX_QUALITY; + break; + default: + return -ENOTSUPP; + } + + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_PERF_MODE; + pkt->rg_property_data[1] = hfi_perf_mode; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_HIER_B_MAX_ENH_LAYERS: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_NON_SECURE_OUTPUT2: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_HIER_P_HYBRID_MODE: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE; + pkt->rg_property_data[1] = + ((struct hfi_hybrid_hierp *)pdata)->layers; + pkt->size += sizeof(u32) + + sizeof(struct hfi_hybrid_hierp); + break; + } + case HAL_PARAM_VENC_MBI_STATISTICS_MODE: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_MBI_DUMPING; + pkt->rg_property_data[1] = hal_to_hfi_type( + HAL_PARAM_VENC_MBI_STATISTICS_MODE, + *(u32 *)pdata); + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VENC_FRAME_QP: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_FRAME_QP; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VENC_BASELAYER_PRIORITYID: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO: + { + struct hfi_aspect_ratio *hfi = NULL; + struct hal_aspect_ratio *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO; + hfi = (struct hfi_aspect_ratio *) + &pkt->rg_property_data[1]; + memcpy(hfi, hal, + sizeof(struct hfi_aspect_ratio)); + pkt->size += sizeof(u32) + + sizeof(struct hfi_aspect_ratio); + break; + } + case HAL_PARAM_VENC_BITRATE_TYPE: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_BITRATE_TYPE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_CONSTRAINED_INTRA_PRED, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_VIDEO_SIGNAL_INFO: + { + struct hal_video_signal_info *hal = pdata; + struct hfi_video_signal_metadata *signal_info = + (struct hfi_video_signal_metadata *) + &pkt->rg_property_data[1]; + + signal_info->enable = true; + signal_info->video_format = MSM_VIDC_NTSC; + signal_info->video_full_range = hal->full_range; + signal_info->color_description = MSM_VIDC_COLOR_DESC_PRESENT; + signal_info->color_primaries = hal->color_space; + signal_info->transfer_characteristics = hal->transfer_chars; + signal_info->matrix_coeffs = hal->matrix_coeffs; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO; + pkt->size += sizeof(u32) + sizeof(*signal_info); + break; + } + case HAL_PARAM_VENC_H264_TRANSFORM_8x8: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_IFRAMESIZE_TYPE: + { + enum hal_iframesize_type hal = + *(enum hal_iframesize_type *)pdata; + struct hfi_iframe_size *hfi = (struct hfi_iframe_size *) + &pkt->rg_property_data[1]; + + switch (hal) { + case HAL_IFRAMESIZE_TYPE_DEFAULT: + hfi->type = HFI_IFRAME_SIZE_DEFAULT; + break; + case HAL_IFRAMESIZE_TYPE_MEDIUM: + hfi->type = HFI_IFRAME_SIZE_MEDIUM; + break; + case HAL_IFRAMESIZE_TYPE_HUGE: + hfi->type = HFI_IFRAME_SIZE_HIGH; + break; + case HAL_IFRAMESIZE_TYPE_UNLIMITED: + hfi->type = HFI_IFRAME_SIZE_UNLIMITED; + break; + default: + return -ENOTSUPP; + } + pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_VENC_IFRAMESIZE; + pkt->size += sizeof(u32) + sizeof(struct hfi_iframe_size); + break; + } + /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ + case HAL_CONFIG_BUFFER_REQUIREMENTS: + case HAL_CONFIG_PRIORITY: + case HAL_CONFIG_BATCH_INFO: + case HAL_PARAM_METADATA_PASS_THROUGH: + case HAL_SYS_IDLE_INDICATOR: + case HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + case HAL_PARAM_INTERLACE_FORMAT_SUPPORTED: + case HAL_PARAM_CHROMA_SITE: + case HAL_PARAM_PROPERTIES_SUPPORTED: + case HAL_PARAM_PROFILE_LEVEL_SUPPORTED: + case HAL_PARAM_CAPABILITY_SUPPORTED: + case HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED: + case HAL_PARAM_MULTI_VIEW_FORMAT: + case HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE: + case HAL_PARAM_CODEC_SUPPORTED: + case HAL_PARAM_VDEC_MULTI_VIEW_SELECT: + case HAL_PARAM_VDEC_MB_QUANTIZATION: + case HAL_PARAM_VDEC_NUM_CONCEALED_MB: + case HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING: + case HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING: + case HAL_CONFIG_BUFFER_COUNT_ACTUAL: + case HAL_CONFIG_VDEC_MULTI_STREAM: + case HAL_PARAM_VENC_MULTI_SLICE_INFO: + case HAL_CONFIG_VENC_TIMESTAMP_SCALE: + case HAL_PARAM_BUFFER_SIZE_MINIMUM: + default: + dprintk(VIDC_ERR, "DEFAULT: Calling %#x\n", ptype); + rc = -ENOTSUPP; + break; + } + return rc; +} + +static int get_hfi_ssr_type(enum hal_ssr_trigger_type type) +{ + int rc = HFI_TEST_SSR_HW_WDOG_IRQ; + + switch (type) { + case SSR_ERR_FATAL: + rc = HFI_TEST_SSR_SW_ERR_FATAL; + break; + case SSR_SW_DIV_BY_ZERO: + rc = HFI_TEST_SSR_SW_DIV_BY_ZERO; + break; + case SSR_HW_WDOG_IRQ: + rc = HFI_TEST_SSR_HW_WDOG_IRQ; + break; + default: + dprintk(VIDC_WARN, + "SSR trigger type not recognized, using WDOG.\n"); + } + return rc; +} + +int create_pkt_ssr_cmd(enum hal_ssr_trigger_type type, + struct hfi_cmd_sys_test_ssr_packet *pkt) +{ + if (!pkt) { + dprintk(VIDC_ERR, "Invalid params, device: %pK\n", pkt); + return -EINVAL; + } + pkt->size = sizeof(struct hfi_cmd_sys_test_ssr_packet); + pkt->packet_type = HFI_CMD_SYS_TEST_SSR; + pkt->trigger_type = get_hfi_ssr_type(type); + return 0; +} + +int create_pkt_cmd_sys_image_version( + struct hfi_cmd_sys_get_property_packet *pkt) +{ + if (!pkt) { + dprintk(VIDC_ERR, "%s invalid param :%pK\n", __func__, pkt); + return -EINVAL; + } + pkt->size = sizeof(struct hfi_cmd_sys_get_property_packet); + pkt->packet_type = HFI_CMD_SYS_GET_PROPERTY; + pkt->num_properties = 1; + pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION; + return 0; +} + +static int create_3x_pkt_cmd_session_set_property( + struct hfi_cmd_session_set_property_packet *pkt, + struct hal_session *session, + enum hal_property ptype, void *pdata) +{ + int rc = 0; + + if (!pkt || !session || !pdata) + return -EINVAL; + + pkt->size = sizeof(struct hfi_cmd_session_set_property_packet); + pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->session_id = hash32_ptr(session); + pkt->num_properties = 1; + + /* + * Any session set property which is different in 3XX packetization + * should be added as a new case below. All unchanged session set + * properties will be handled in the default case. + */ + switch (ptype) { + case HAL_PARAM_VDEC_MULTI_STREAM: + { + u32 buffer_type; + struct hfi_3x_multi_stream *hfi; + struct hal_multi_stream *prop = + (struct hal_multi_stream *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + hfi = (struct hfi_3x_multi_stream *) &pkt->rg_property_data[1]; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + hfi->enable = prop->enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_3x_multi_stream); + break; + } + case HAL_PARAM_VENC_INTRA_REFRESH: + { + struct hfi_3x_intra_refresh *hfi; + struct hal_intra_refresh *prop = + (struct hal_intra_refresh *) pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH; + hfi = (struct hfi_3x_intra_refresh *) &pkt->rg_property_data[1]; + hfi->mbs = 0; + switch (prop->mode) { + case HAL_INTRA_REFRESH_NONE: + hfi->mode = HFI_INTRA_REFRESH_NONE; + break; + case HAL_INTRA_REFRESH_ADAPTIVE: + hfi->mode = HFI_INTRA_REFRESH_ADAPTIVE; + hfi->mbs = prop->air_mbs; + break; + case HAL_INTRA_REFRESH_CYCLIC: + hfi->mode = HFI_INTRA_REFRESH_CYCLIC; + hfi->mbs = prop->cir_mbs; + break; + case HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE: + hfi->mode = HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE; + hfi->mbs = prop->air_mbs; + break; + case HAL_INTRA_REFRESH_RANDOM: + hfi->mode = HFI_INTRA_REFRESH_RANDOM; + hfi->mbs = prop->air_mbs; + break; + default: + dprintk(VIDC_ERR, + "Invalid intra refresh setting: %d\n", + prop->mode); + break; + } + pkt->size += sizeof(u32) + sizeof(struct hfi_3x_intra_refresh); + break; + } + case HAL_PARAM_SYNC_BASED_INTERRUPT: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_VQZIP_SEI: + { + create_pkt_enable(pkt->rg_property_data, + HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE, + ((struct hal_enable *)pdata)->enable); + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + /* Deprecated param on Venus 3xx */ + case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER: + { + rc = -ENOTSUPP; + break; + } + case HAL_PARAM_BUFFER_SIZE_MINIMUM: + { + struct hfi_buffer_size_minimum *hfi; + struct hal_buffer_size_minimum *prop = + (struct hal_buffer_size_minimum *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM; + + hfi = (struct hfi_buffer_size_minimum *) + &pkt->rg_property_data[1]; + hfi->buffer_size = prop->buffer_size; + + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + pkt->size += sizeof(u32) + sizeof(struct + hfi_buffer_count_actual); + break; + } + case HAL_PARAM_VENC_H264_PIC_ORDER_CNT: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_LOW_LATENCY: + { + struct hfi_enable *hfi; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hal_enable *) pdata)->enable; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VENC_BLUR_RESOLUTION: + { + struct hfi_frame_size *hfi; + struct hal_frame_size *prop = (struct hal_frame_size *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE; + hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1]; + buffer_type = get_hfi_buffer(prop->buffer_type); + if (buffer_type) + hfi->buffer_type = buffer_type; + else + return -EINVAL; + + hfi->height = prop->height; + hfi->width = prop->width; + pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size); + break; + } + default: + rc = create_pkt_cmd_session_set_property(pkt, + session, ptype, pdata); + } + return rc; +} + +int create_pkt_cmd_session_sync_process( + struct hfi_cmd_session_sync_process_packet *pkt, + struct hal_session *session) +{ + if (!pkt || !session) + return -EINVAL; + + *pkt = (struct hfi_cmd_session_sync_process_packet) {0}; + pkt->size = sizeof(*pkt); + pkt->packet_type = HFI_CMD_SESSION_SYNC; + pkt->session_id = hash32_ptr(session); + pkt->sync_id = 0; + + return 0; +} + +static struct hfi_packetization_ops hfi_default = { + .sys_init = create_pkt_cmd_sys_init, + .sys_pc_prep = create_pkt_cmd_sys_pc_prep, + .sys_idle_indicator = create_pkt_cmd_sys_idle_indicator, + .sys_power_control = create_pkt_cmd_sys_power_control, + .sys_set_resource = create_pkt_cmd_sys_set_resource, + .sys_debug_config = create_pkt_cmd_sys_debug_config, + .sys_coverage_config = create_pkt_cmd_sys_coverage_config, + .sys_release_resource = create_pkt_cmd_sys_release_resource, + .sys_ping = create_pkt_cmd_sys_ping, + .sys_image_version = create_pkt_cmd_sys_image_version, + .ssr_cmd = create_pkt_ssr_cmd, + .session_init = create_pkt_cmd_sys_session_init, + .session_cmd = create_pkt_cmd_session_cmd, + .session_set_buffers = create_pkt_cmd_session_set_buffers, + .session_release_buffers = create_pkt_cmd_session_release_buffers, + .session_etb_decoder = create_pkt_cmd_session_etb_decoder, + .session_etb_encoder = create_pkt_cmd_session_etb_encoder, + .session_ftb = create_pkt_cmd_session_ftb, + .session_parse_seq_header = create_pkt_cmd_session_parse_seq_header, + .session_get_seq_hdr = create_pkt_cmd_session_get_seq_hdr, + .session_get_buf_req = create_pkt_cmd_session_get_buf_req, + .session_flush = create_pkt_cmd_session_flush, + .session_get_property = create_pkt_cmd_session_get_property, + .session_set_property = create_pkt_cmd_session_set_property, +}; + +struct hfi_packetization_ops *get_venus_3x_ops(void) +{ + static struct hfi_packetization_ops hfi_venus_3x; + + hfi_venus_3x = hfi_default; + + /* Override new HFI functions for HFI_PACKETIZATION_3XX here. */ + hfi_venus_3x.session_set_property = + create_3x_pkt_cmd_session_set_property; + hfi_venus_3x.session_get_property = + create_3x_pkt_cmd_session_get_property; + hfi_venus_3x.session_cmd = create_3x_pkt_cmd_session_cmd; + hfi_venus_3x.session_sync_process = create_pkt_cmd_session_sync_process; + + return &hfi_venus_3x; +} + +struct hfi_packetization_ops *hfi_get_pkt_ops_handle( + enum hfi_packetization_type type) +{ + dprintk(VIDC_DBG, "%s selected\n", + type == HFI_PACKETIZATION_LEGACY ? "legacy packetization" : + type == HFI_PACKETIZATION_3XX ? "3xx packetization" : + "Unknown hfi"); + + switch (type) { + case HFI_PACKETIZATION_LEGACY: + return &hfi_default; + case HFI_PACKETIZATION_3XX: + return get_venus_3x_ops(); + } + + return NULL; +} diff --git a/drivers/media/platform/msm/vidc_3x/hfi_packetization.h b/drivers/media/platform/msm/vidc_3x/hfi_packetization.h new file mode 100644 index 000000000000..d7a827a46c90 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/hfi_packetization.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2015, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __HFI_PACKETIZATION__ +#define __HFI_PACKETIZATION__ + +#include +#include "vidc_hfi_helper.h" +#include "vidc_hfi.h" +#include "vidc_hfi_api.h" + +#define call_hfi_pkt_op(q, op, args...) \ + (((q) && (q)->pkt_ops && (q)->pkt_ops->op) ? \ + ((q)->pkt_ops->op(args)) : 0) + +enum hfi_packetization_type { + HFI_PACKETIZATION_LEGACY, + HFI_PACKETIZATION_3XX, +}; + +struct hfi_packetization_ops { + int (*sys_init)(struct hfi_cmd_sys_init_packet *pkt, u32 arch_type); + int (*sys_pc_prep)(struct hfi_cmd_sys_pc_prep_packet *pkt); + int (*sys_idle_indicator)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 enable); + int (*sys_power_control)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 enable); + int (*sys_set_resource)( + struct hfi_cmd_sys_set_resource_packet *pkt, + struct vidc_resource_hdr *resource_hdr, + void *resource_value); + int (*sys_debug_config)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode); + int (*sys_coverage_config)(struct hfi_cmd_sys_set_property_packet *pkt, + u32 mode); + int (*sys_release_resource)( + struct hfi_cmd_sys_release_resource_packet *pkt, + struct vidc_resource_hdr *resource_hdr); + int (*sys_ping)(struct hfi_cmd_sys_ping_packet *pkt); + int (*sys_image_version)(struct hfi_cmd_sys_get_property_packet *pkt); + int (*ssr_cmd)(enum hal_ssr_trigger_type type, + struct hfi_cmd_sys_test_ssr_packet *pkt); + int (*session_init)( + struct hfi_cmd_sys_session_init_packet *pkt, + struct hal_session *session, + u32 session_domain, u32 session_codec); + int (*session_cmd)(struct vidc_hal_session_cmd_pkt *pkt, + int pkt_type, struct hal_session *session); + int (*session_set_buffers)( + struct hfi_cmd_session_set_buffers_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info); + int (*session_release_buffers)( + struct hfi_cmd_session_release_buffer_packet *pkt, + struct hal_session *session, + struct vidc_buffer_addr_info *buffer_info); + int (*session_etb_decoder)( + struct hfi_cmd_session_empty_buffer_compressed_packet *pkt, + struct hal_session *session, + struct vidc_frame_data *input_frame); + int (*session_etb_encoder)( + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + *pkt, struct hal_session *session, + struct vidc_frame_data *input_frame); + int (*session_ftb)(struct hfi_cmd_session_fill_buffer_packet *pkt, + struct hal_session *session, + struct vidc_frame_data *output_frame); + int (*session_parse_seq_header)( + struct hfi_cmd_session_parse_sequence_header_packet *pkt, + struct hal_session *session, struct vidc_seq_hdr *seq_hdr); + int (*session_get_seq_hdr)( + struct hfi_cmd_session_get_sequence_header_packet *pkt, + struct hal_session *session, struct vidc_seq_hdr *seq_hdr); + int (*session_get_buf_req)( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session); + int (*session_flush)(struct hfi_cmd_session_flush_packet *pkt, + struct hal_session *session, enum hal_flush flush_mode); + int (*session_get_property)( + struct hfi_cmd_session_get_property_packet *pkt, + struct hal_session *session, enum hal_property ptype); + int (*session_set_property)( + struct hfi_cmd_session_set_property_packet *pkt, + struct hal_session *session, + enum hal_property ptype, void *pdata); + int (*session_sync_process)( + struct hfi_cmd_session_sync_process_packet *pkt, + struct hal_session *session); +}; + +struct hfi_packetization_ops *hfi_get_pkt_ops_handle( + enum hfi_packetization_type); +#endif diff --git a/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c b/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c new file mode 100644 index 000000000000..d0fc335ebca8 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/hfi_response_handler.c @@ -0,0 +1,2106 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "vidc_hfi_helper.h" +#include "vidc_hfi_io.h" +#include "msm_vidc_debug.h" +#include "vidc_hfi.h" + +static enum vidc_status hfi_parse_init_done_properties( + struct msm_vidc_capability *capability, + u32 num_sessions, u8 *data_ptr, u32 num_properties, + u32 rem_bytes, u32 codec, u32 domain); + +static enum vidc_status hfi_map_err_status(u32 hfi_err) +{ + enum vidc_status vidc_err; + + switch (hfi_err) { + case HFI_ERR_NONE: + case HFI_ERR_SESSION_SAME_STATE_OPERATION: + vidc_err = VIDC_ERR_NONE; + break; + case HFI_ERR_SYS_FATAL: + vidc_err = VIDC_ERR_HW_FATAL; + break; + case HFI_ERR_SYS_VERSION_MISMATCH: + case HFI_ERR_SYS_INVALID_PARAMETER: + case HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE: + case HFI_ERR_SESSION_INVALID_PARAMETER: + case HFI_ERR_SESSION_INVALID_SESSION_ID: + case HFI_ERR_SESSION_INVALID_STREAM_ID: + vidc_err = VIDC_ERR_BAD_PARAM; + break; + case HFI_ERR_SYS_INSUFFICIENT_RESOURCES: + case HFI_ERR_SYS_UNSUPPORTED_DOMAIN: + case HFI_ERR_SYS_UNSUPPORTED_CODEC: + case HFI_ERR_SESSION_UNSUPPORTED_PROPERTY: + case HFI_ERR_SESSION_UNSUPPORTED_SETTING: + case HFI_ERR_SESSION_INSUFFICIENT_RESOURCES: + case HFI_ERR_SESSION_UNSUPPORTED_STREAM: + vidc_err = VIDC_ERR_NOT_SUPPORTED; + break; + case HFI_ERR_SYS_MAX_SESSIONS_REACHED: + vidc_err = VIDC_ERR_MAX_CLIENTS; + break; + case HFI_ERR_SYS_SESSION_IN_USE: + vidc_err = VIDC_ERR_CLIENT_PRESENT; + break; + case HFI_ERR_SESSION_FATAL: + vidc_err = VIDC_ERR_CLIENT_FATAL; + break; + case HFI_ERR_SESSION_BAD_POINTER: + vidc_err = VIDC_ERR_BAD_PARAM; + break; + case HFI_ERR_SESSION_INCORRECT_STATE_OPERATION: + vidc_err = VIDC_ERR_BAD_STATE; + break; + case HFI_ERR_SESSION_STREAM_CORRUPT: + case HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED: + vidc_err = VIDC_ERR_BITSTREAM_ERR; + break; + case HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED: + vidc_err = VIDC_ERR_IFRAME_EXPECTED; + break; + case HFI_ERR_SESSION_START_CODE_NOT_FOUND: + vidc_err = VIDC_ERR_START_CODE_NOT_FOUND; + break; + case HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING: + default: + vidc_err = VIDC_ERR_FAIL; + break; + } + return vidc_err; +} + +static enum msm_vidc_pixel_depth get_hal_pixel_depth(u32 hfi_bit_depth) +{ + switch (hfi_bit_depth) { + case HFI_BITDEPTH_8: return MSM_VIDC_BIT_DEPTH_8; + case HFI_BITDEPTH_9: + case HFI_BITDEPTH_10: return MSM_VIDC_BIT_DEPTH_10; + } + dprintk(VIDC_ERR, "Unsupported bit depth: %d\n", hfi_bit_depth); + return MSM_VIDC_BIT_DEPTH_UNSUPPORTED; +} + +static inline int validate_pkt_size(u32 rem_size, u32 msg_size) +{ + if (rem_size < msg_size) { + dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", + __func__, rem_size); + return false; + } + return true; +} + +static int hfi_process_sess_evt_seq_changed(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_event event_notify = {0}; + u32 num_properties_changed, rem_size; + struct hfi_frame_size *frame_sz; + struct hfi_profile_level *profile_level; + struct hfi_bit_depth *pixel_depth; + struct hfi_pic_struct *pic_struct; + u8 *data_ptr; + int prop_id; + enum msm_vidc_pixel_depth luma_bit_depth, chroma_bit_depth; + struct hfi_colour_space *colour_info; + /* Initialize pic_struct to unknown as default */ + event_notify.pic_struct = MSM_VIDC_PIC_STRUCT_UNKNOWN; + if (!validate_pkt_size(pkt->size, + sizeof(struct hfi_msg_event_notify_packet))) + return -E2BIG; + + event_notify.device_id = device_id; + event_notify.session_id = (void *)(uintptr_t)pkt->session_id; + event_notify.status = VIDC_ERR_NONE; + num_properties_changed = pkt->event_data2; + switch (pkt->event_data1) { + case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES: + event_notify.hal_event_type = + HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES; + break; + case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES: + event_notify.hal_event_type = + HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES; + break; + default: + break; + } + + if (num_properties_changed) { + data_ptr = (u8 *) &pkt->rg_ext_event_data[0]; + rem_size = pkt->size - sizeof(struct + hfi_msg_event_notify_packet) + sizeof(u32); + do { + if (!validate_pkt_size(rem_size, sizeof(u32))) + return -E2BIG; + prop_id = (int) *((u32 *)data_ptr); + rem_size -= sizeof(u32); + switch (prop_id) { + case HFI_PROPERTY_PARAM_FRAME_SIZE: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_frame_size))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + frame_sz = + (struct hfi_frame_size *) data_ptr; + event_notify.width = frame_sz->width; + event_notify.height = frame_sz->height; + dprintk(VIDC_DBG, "height: %d width: %d\n", + frame_sz->height, frame_sz->width); + data_ptr += + sizeof(struct hfi_frame_size); + rem_size -= sizeof(struct hfi_frame_size); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_profile_level))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + profile_level = + (struct hfi_profile_level *) data_ptr; + dprintk(VIDC_DBG, "profile: %d level: %d\n", + profile_level->profile, + profile_level->level); + data_ptr += + sizeof(struct hfi_profile_level); + rem_size -= sizeof(struct hfi_profile_level); + break; + case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_bit_depth))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + pixel_depth = (struct hfi_bit_depth *) data_ptr; + /* + * Luma and chroma can have different bitdepths. + * Driver should rely on luma and chroma + * bitdepth for determining output bitdepth + * type. + * + * pixel_depth->bitdepth will include luma + * bitdepth info in bits 0..15 and chroma + * bitdept in bits 16..31. + */ + luma_bit_depth = get_hal_pixel_depth( + pixel_depth->bit_depth & + GENMASK(15, 0)); + chroma_bit_depth = get_hal_pixel_depth( + (pixel_depth->bit_depth & + GENMASK(31, 16)) >> 16); + if (luma_bit_depth == MSM_VIDC_BIT_DEPTH_10 || + chroma_bit_depth == + MSM_VIDC_BIT_DEPTH_10) + event_notify.bit_depth = + MSM_VIDC_BIT_DEPTH_10; + else + event_notify.bit_depth = luma_bit_depth; + dprintk(VIDC_DBG, + "bitdepth(%d), luma_bit_depth(%d), chroma_bit_depth(%d)\n", + event_notify.bit_depth, luma_bit_depth, + chroma_bit_depth); + data_ptr += sizeof(struct hfi_bit_depth); + rem_size -= sizeof(struct hfi_bit_depth); + break; + case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_pic_struct))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + pic_struct = (struct hfi_pic_struct *) data_ptr; + event_notify.pic_struct = + pic_struct->progressive_only; + dprintk(VIDC_DBG, + "Progressive only flag: %d\n", + pic_struct->progressive_only); + data_ptr += + sizeof(struct hfi_pic_struct); + rem_size -= sizeof(struct hfi_pic_struct); + break; + case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: + if (!validate_pkt_size(rem_size, sizeof(struct + hfi_colour_space))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + colour_info = + (struct hfi_colour_space *) data_ptr; + event_notify.colour_space = + colour_info->colour_space; + dprintk(VIDC_DBG, + "Colour space value is: %d\n", + colour_info->colour_space); + data_ptr += + sizeof(struct hfi_colour_space); + rem_size -= sizeof(struct hfi_colour_space); + break; + break; + default: + dprintk(VIDC_ERR, + "%s cmd: %#x not supported\n", + __func__, prop_id); + break; + } + num_properties_changed--; + } while (num_properties_changed > 0); + } + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_EVENT_CHANGE, + .response.event = event_notify, + }; + + return 0; +} + +static int hfi_process_evt_release_buffer_ref(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_event event_notify = {0}; + struct hfi_msg_release_buffer_ref_event_packet *data; + + dprintk(VIDC_DBG, + "RECEIVED: EVENT_NOTIFY - release_buffer_reference\n"); + if (sizeof(struct hfi_msg_event_notify_packet) + > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); + return -E2BIG; + } + if (pkt->size < sizeof(struct hfi_msg_event_notify_packet) - sizeof(u32) + + sizeof(struct hfi_msg_release_buffer_ref_event_packet)) { + dprintk(VIDC_ERR, + "hfi_msg_release_buffer_ref_event: bad_pkt_size\n"); + return -E2BIG; + } + + data = (struct hfi_msg_release_buffer_ref_event_packet *) + pkt->rg_ext_event_data; + + event_notify.device_id = device_id; + event_notify.session_id = (void *)(uintptr_t)pkt->session_id; + event_notify.status = VIDC_ERR_NONE; + event_notify.hal_event_type = HAL_EVENT_RELEASE_BUFFER_REFERENCE; + event_notify.packet_buffer = data->packet_buffer; + event_notify.extra_data_buffer = data->extra_data_buffer; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_EVENT_CHANGE, + .response.event = event_notify, + }; + + return 0; +} + +static int hfi_process_sys_error(u32 device_id, struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + cmd_done.device_id = device_id; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SYS_ERROR, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_error(u32 device_id, + struct hfi_msg_event_notify_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->event_data1); + dprintk(VIDC_INFO, "Received: SESSION_ERROR with event id : %d\n", + pkt->event_data1); + switch (pkt->event_data1) { + case HFI_ERR_SESSION_INVALID_SCALE_FACTOR: + case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE: + case HFI_ERR_SESSION_UNSUPPORTED_SETTING: + case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED: + cmd_done.status = VIDC_ERR_NONE; + dprintk(VIDC_INFO, "Non Fatal: HFI_EVENT_SESSION_ERROR\n"); + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + .response.cmd = cmd_done, + }; + return 0; + default: + dprintk(VIDC_ERR, "HFI_EVENT_SESSION_ERROR\n"); + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_ERROR, + .response.cmd = cmd_done, + }; + return 0; + } +} + +static int hfi_process_event_notify(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_event_notify_packet *pkt = _pkt; + + dprintk(VIDC_DBG, "Received: EVENT_NOTIFY\n"); + if (pkt->size < sizeof(struct hfi_msg_event_notify_packet)) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -E2BIG; + } + + switch (pkt->event_id) { + case HFI_EVENT_SYS_ERROR: + dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d, %#x\n", + pkt->event_data1, pkt->event_data2); + return hfi_process_sys_error(device_id, info); + case HFI_EVENT_SESSION_ERROR: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR[%#x]\n", + pkt->session_id); + return hfi_process_session_error(device_id, pkt, info); + + case HFI_EVENT_SESSION_SEQUENCE_CHANGED: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED[%#x]\n", + pkt->session_id); + return hfi_process_sess_evt_seq_changed(device_id, pkt, info); + + case HFI_EVENT_RELEASE_BUFFER_REFERENCE: + dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE[%#x]\n", + pkt->session_id); + return hfi_process_evt_release_buffer_ref(device_id, pkt, info); + + case HFI_EVENT_SESSION_PROPERTY_CHANGED: + default: + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + }; + + return 0; + } +} + +static int hfi_process_sys_init_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_init_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + enum vidc_status status = VIDC_ERR_NONE; + + dprintk(VIDC_DBG, "RECEIVED: SYS_INIT_DONE\n"); + if (sizeof(struct hfi_msg_sys_init_done_packet) > pkt->size) { + dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", __func__, + pkt->size); + return -E2BIG; + } + if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hal_process_sys_init_done: no_properties\n"); + status = VIDC_ERR_FAIL; + goto err_no_prop; + } + + status = hfi_map_err_status(pkt->error_type); + if (status) { + dprintk(VIDC_ERR, "%s: status %#x\n", + __func__, status); + goto err_no_prop; + } + +err_no_prop: + cmd_done.device_id = device_id; + cmd_done.session_id = NULL; + cmd_done.status = (u32)status; + cmd_done.size = sizeof(struct vidc_hal_sys_init_done); + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SYS_INIT_DONE, + .response.cmd = cmd_done, + }; + return 0; +} + +static int hfi_process_sys_rel_resource_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_release_resource_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + enum vidc_status status = VIDC_ERR_NONE; + u32 pkt_size; + + dprintk(VIDC_DBG, "RECEIVED: SYS_RELEASE_RESOURCE_DONE\n"); + pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet); + if (pkt_size > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_sys_rel_resource_done: bad size: %d\n", + pkt->size); + return -E2BIG; + } + + status = hfi_map_err_status(pkt->error_type); + cmd_done.device_id = device_id; + cmd_done.session_id = NULL; + cmd_done.status = (u32) status; + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SYS_RELEASE_RESOURCE_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +enum hal_capability get_hal_cap_type(u32 capability_type) +{ + enum hal_capability hal_cap = 0; + + switch (capability_type) { + case HFI_CAPABILITY_FRAME_WIDTH: + hal_cap = HAL_CAPABILITY_FRAME_WIDTH; + break; + case HFI_CAPABILITY_FRAME_HEIGHT: + hal_cap = HAL_CAPABILITY_FRAME_HEIGHT; + break; + case HFI_CAPABILITY_MBS_PER_FRAME: + hal_cap = HAL_CAPABILITY_MBS_PER_FRAME; + break; + case HFI_CAPABILITY_MBS_PER_SECOND: + hal_cap = HAL_CAPABILITY_MBS_PER_SECOND; + break; + case HFI_CAPABILITY_FRAMERATE: + hal_cap = HAL_CAPABILITY_FRAMERATE; + break; + case HFI_CAPABILITY_SCALE_X: + hal_cap = HAL_CAPABILITY_SCALE_X; + break; + case HFI_CAPABILITY_SCALE_Y: + hal_cap = HAL_CAPABILITY_SCALE_Y; + break; + case HFI_CAPABILITY_BITRATE: + hal_cap = HAL_CAPABILITY_BITRATE; + break; + case HFI_CAPABILITY_BFRAME: + hal_cap = HAL_CAPABILITY_BFRAME; + break; + case HFI_CAPABILITY_PEAKBITRATE: + hal_cap = HAL_CAPABILITY_PEAKBITRATE; + break; + case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: + hal_cap = HAL_CAPABILITY_HIER_P_NUM_ENH_LAYERS; + break; + case HFI_CAPABILITY_ENC_LTR_COUNT: + hal_cap = HAL_CAPABILITY_ENC_LTR_COUNT; + break; + case HFI_CAPABILITY_CP_OUTPUT2_THRESH: + hal_cap = HAL_CAPABILITY_SECURE_OUTPUT2_THRESHOLD; + break; + case HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS: + hal_cap = HAL_CAPABILITY_HIER_B_NUM_ENH_LAYERS; + break; + case HFI_CAPABILITY_LCU_SIZE: + hal_cap = HAL_CAPABILITY_LCU_SIZE; + break; + case HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS: + hal_cap = HAL_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS; + break; + case HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE: + hal_cap = HAL_CAPABILITY_MBS_PER_SECOND_POWER_SAVE; + break; + default: + dprintk(VIDC_DBG, "%s: unknown capablity %#x\n", + __func__, capability_type); + break; + } + + return hal_cap; +} + +static inline void copy_cap_prop( + struct hfi_capability_supported *in, + struct msm_vidc_capability *capability) +{ + struct hal_capability_supported *out = NULL; + + if (!in || !capability) { + dprintk(VIDC_ERR, "%s Invalid input parameters\n", + __func__); + return; + } + + switch (in->capability_type) { + case HFI_CAPABILITY_FRAME_WIDTH: + out = &capability->width; + break; + case HFI_CAPABILITY_FRAME_HEIGHT: + out = &capability->height; + break; + case HFI_CAPABILITY_MBS_PER_FRAME: + out = &capability->mbs_per_frame; + break; + case HFI_CAPABILITY_MBS_PER_SECOND: + out = &capability->mbs_per_sec; + break; + case HFI_CAPABILITY_FRAMERATE: + out = &capability->frame_rate; + break; + case HFI_CAPABILITY_SCALE_X: + out = &capability->scale_x; + break; + case HFI_CAPABILITY_SCALE_Y: + out = &capability->scale_y; + break; + case HFI_CAPABILITY_BITRATE: + out = &capability->bitrate; + break; + case HFI_CAPABILITY_BFRAME: + out = &capability->bframe; + break; + case HFI_CAPABILITY_PEAKBITRATE: + out = &capability->peakbitrate; + break; + case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: + out = &capability->hier_p; + break; + case HFI_CAPABILITY_ENC_LTR_COUNT: + out = &capability->ltr_count; + break; + case HFI_CAPABILITY_CP_OUTPUT2_THRESH: + out = &capability->secure_output2_threshold; + break; + case HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS: + out = &capability->hier_b; + break; + case HFI_CAPABILITY_LCU_SIZE: + out = &capability->lcu_size; + break; + case HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS: + out = &capability->hier_p_hybrid; + break; + case HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE: + out = &capability->mbs_per_sec_power_save; + break; + default: + dprintk(VIDC_DBG, "%s: unknown capablity %#x\n", + __func__, in->capability_type); + break; + } + + if (out) { + out->capability_type = get_hal_cap_type(in->capability_type); + out->min = in->min; + out->max = in->max; + out->step_size = in->step_size; + } +} + +static int hfi_fill_codec_info(u8 *data_ptr, + struct vidc_hal_sys_init_done *sys_init_done, u32 rem_size) +{ + u32 i; + u32 codecs = 0, codec_count = 0, size = 0; + struct msm_vidc_capability *capability; + u32 prop_id = *((u32 *)data_ptr); + u8 *orig_data_ptr = data_ptr; + + if (prop_id == HFI_PROPERTY_PARAM_CODEC_SUPPORTED) { + struct hfi_codec_supported *prop; + + if (!validate_pkt_size(rem_size - sizeof(u32), + sizeof(struct hfi_codec_supported))) + return -E2BIG; + data_ptr = data_ptr + sizeof(u32); + prop = (struct hfi_codec_supported *) data_ptr; + sys_init_done->dec_codec_supported = + prop->decoder_codec_supported; + sys_init_done->enc_codec_supported = + prop->encoder_codec_supported; + size = sizeof(struct hfi_codec_supported) + sizeof(u32); + rem_size -= + sizeof(struct hfi_codec_supported) + sizeof(u32); + } else { + dprintk(VIDC_WARN, + "%s: prop_id %#x, expected codec_supported property\n", + __func__, prop_id); + } + + codecs = sys_init_done->dec_codec_supported; + for (i = 0; i < 8 * sizeof(codecs); i++) { + if ((1 << i) & codecs) { + capability = + &sys_init_done->capabilities[codec_count++]; + capability->codec = + vidc_get_hal_codec((1 << i) & codecs); + capability->domain = + vidc_get_hal_domain(HFI_VIDEO_DOMAIN_DECODER); + if (codec_count == VIDC_MAX_DECODE_SESSIONS) { + dprintk(VIDC_ERR, + "Max supported decoder sessions reached\n"); + break; + } + } + } + codecs = sys_init_done->enc_codec_supported; + for (i = 0; i < 8 * sizeof(codecs); i++) { + if ((1 << i) & codecs) { + capability = + &sys_init_done->capabilities[codec_count++]; + capability->codec = + vidc_get_hal_codec((1 << i) & codecs); + capability->domain = + vidc_get_hal_domain(HFI_VIDEO_DOMAIN_ENCODER); + if (codec_count == VIDC_MAX_SESSIONS) { + dprintk(VIDC_ERR, + "Max supported sessions reached\n"); + break; + } + } + } + sys_init_done->codec_count = codec_count; + + if (!validate_pkt_size(rem_size, sizeof(u32))) + return -E2BIG; + prop_id = *((u32 *)(orig_data_ptr + size)); + if (prop_id == HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED) { + struct hfi_max_sessions_supported *prop; + + if (!validate_pkt_size(rem_size - sizeof(u32), sizeof(struct + hfi_max_sessions_supported))) + return -E2BIG; + prop = (struct hfi_max_sessions_supported *) + (orig_data_ptr + size + sizeof(u32)); + + sys_init_done->max_sessions_supported = prop->max_sessions; + size += sizeof(struct hfi_max_sessions_supported) + sizeof(u32); + rem_size -= + sizeof(struct hfi_max_sessions_supported) + sizeof(u32); + dprintk(VIDC_DBG, "max_sessions_supported %d\n", + prop->max_sessions); + } + return size; +} + +enum vidc_status hfi_process_session_init_done_prop_read( + struct hfi_msg_sys_session_init_done_packet *pkt, + struct vidc_hal_session_init_done *session_init_done) +{ + enum vidc_status status = VIDC_ERR_NONE; + struct msm_vidc_capability *capability = NULL; + u32 rem_bytes, num_properties; + u8 *data_ptr; + + rem_bytes = pkt->size - sizeof(struct + hfi_msg_sys_session_init_done_packet) + sizeof(u32); + if (!rem_bytes) { + dprintk(VIDC_ERR, "%s: invalid property info\n", __func__); + return VIDC_ERR_FAIL; + } + + status = hfi_map_err_status(pkt->error_type); + if (status) { + dprintk(VIDC_ERR, "%s: error status 0x%x\n", __func__, status); + return status; + } + + data_ptr = (u8 *)&pkt->rg_property_data[0]; + num_properties = pkt->num_properties; + + capability = &session_init_done->capability; + status = hfi_parse_init_done_properties( + capability, 1, data_ptr, num_properties, rem_bytes, + vidc_get_hfi_codec(capability->codec), + vidc_get_hfi_domain(capability->domain)); + if (status) { + dprintk(VIDC_ERR, "%s: parse status 0x%x\n", __func__, status); + return status; + } + + return status; +} + +static int copy_caps_to_sessions(struct hfi_capability_supported *cap, + u32 num_caps, struct msm_vidc_capability *capabilities, + u32 num_sessions, u32 codecs, u32 domain) +{ + u32 i = 0, j = 0; + struct msm_vidc_capability *capability; + u32 sess_codec; + u32 sess_domain; + + /* + * iterate over num_sessions and copy all the capabilities + * to matching sessions. + */ + for (i = 0; i < num_sessions; i++) { + sess_codec = 0; + sess_domain = 0; + capability = &capabilities[i]; + + if (capability->codec) + sess_codec = + vidc_get_hfi_codec(capability->codec); + if (capability->domain) + sess_domain = + vidc_get_hfi_domain(capability->domain); + + if (!(sess_codec & codecs && sess_domain & domain)) + continue; + + for (j = 0; j < num_caps; j++) + copy_cap_prop(&cap[j], capability); + } + + return 0; +} + +static int copy_alloc_mode_to_sessions( + struct hfi_buffer_alloc_mode_supported *prop, + struct msm_vidc_capability *capabilities, + u32 num_sessions, u32 codecs, u32 domain) +{ + u32 i = 0, j = 0; + struct msm_vidc_capability *capability; + u32 sess_codec; + u32 sess_domain; + + /* + * iterate over num_sessions and copy all the entries + * to matching sessions. + */ + for (i = 0; i < num_sessions; i++) { + sess_codec = 0; + sess_domain = 0; + capability = &capabilities[i]; + + if (capability->codec) + sess_codec = + vidc_get_hfi_codec(capability->codec); + if (capability->domain) + sess_domain = + vidc_get_hfi_domain(capability->domain); + + if (!(sess_codec & codecs && sess_domain & domain)) + continue; + + for (j = 0; j < prop->num_entries; j++) { + if (prop->buffer_type == HFI_BUFFER_OUTPUT || + prop->buffer_type == HFI_BUFFER_OUTPUT2) { + switch (prop->rg_data[j]) { + case HFI_BUFFER_MODE_STATIC: + capability->alloc_mode_out |= + HAL_BUFFER_MODE_STATIC; + break; + case HFI_BUFFER_MODE_RING: + capability->alloc_mode_out |= + HAL_BUFFER_MODE_RING; + break; + case HFI_BUFFER_MODE_DYNAMIC: + capability->alloc_mode_out |= + HAL_BUFFER_MODE_DYNAMIC; + break; + } + } else if (prop->buffer_type == HFI_BUFFER_INPUT) { + switch (prop->rg_data[j]) { + case HFI_BUFFER_MODE_STATIC: + capability->alloc_mode_in |= + HAL_BUFFER_MODE_STATIC; + break; + case HFI_BUFFER_MODE_RING: + capability->alloc_mode_in |= + HAL_BUFFER_MODE_RING; + break; + case HFI_BUFFER_MODE_DYNAMIC: + capability->alloc_mode_in |= + HAL_BUFFER_MODE_DYNAMIC; + break; + } + } + } + } + + return 0; +} + +static enum vidc_status hfi_parse_init_done_properties( + struct msm_vidc_capability *capabilities, + u32 num_sessions, u8 *data_ptr, u32 num_properties, + u32 rem_bytes, u32 codecs, u32 domain) +{ + enum vidc_status status = VIDC_ERR_NONE; + u32 prop_id, next_offset; +#define VALIDATE_PROPERTY_STRUCTURE_SIZE(pkt_size, property_size) ({\ + if (pkt_size < property_size) { \ + status = VIDC_ERR_BAD_PARAM; \ + break; \ + } \ +}) + +#define VALIDATE_PROPERTY_PAYLOAD_SIZE(pkt_size, payload_size, \ + property_count) ({\ + if (pkt_size/payload_size < property_count) { \ + status = VIDC_ERR_BAD_PARAM; \ + break; \ + } \ +}) + + while (status == VIDC_ERR_NONE && num_properties && + rem_bytes >= sizeof(u32)) { + + prop_id = *((u32 *)data_ptr); + next_offset = sizeof(u32); + + switch (prop_id) { + case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: + { + struct hfi_codec_mask_supported *prop = + (struct hfi_codec_mask_supported *) + (data_ptr + next_offset); + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + + codecs = prop->codecs; + domain = prop->video_domains; + next_offset += sizeof(struct hfi_codec_mask_supported); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: + { + struct hfi_capability_supported_info *prop = + (struct hfi_capability_supported_info *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + VALIDATE_PROPERTY_PAYLOAD_SIZE(rem_bytes - + next_offset - sizeof(u32), + sizeof(struct hfi_capability_supported), + prop->num_capabilities); + + next_offset += sizeof(u32) + + prop->num_capabilities * + sizeof(struct hfi_capability_supported); + + copy_caps_to_sessions(&prop->rg_data[0], + prop->num_capabilities, + capabilities, num_sessions, + codecs, domain); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + { + struct hfi_uncompressed_format_supported *prop = + (struct hfi_uncompressed_format_supported *) + (data_ptr + next_offset); + u32 num_format_entries; + char *fmt_ptr; + struct hfi_uncompressed_plane_info *plane_info; + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + + num_format_entries = prop->format_entries; + next_offset = sizeof(*prop); + fmt_ptr = (char *)&prop->rg_format_info[0]; + + while (num_format_entries) { + u32 bytes_to_skip; + + plane_info = + (struct hfi_uncompressed_plane_info *) fmt_ptr; + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*plane_info)); + + bytes_to_skip = sizeof(*plane_info) - + sizeof(struct + hfi_uncompressed_plane_constraints) + + plane_info->num_planes * + sizeof(struct + hfi_uncompressed_plane_constraints); + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + bytes_to_skip); + + fmt_ptr += bytes_to_skip; + next_offset += bytes_to_skip; + num_format_entries--; + } + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: + { + struct hfi_properties_supported *prop = + (struct hfi_properties_supported *) + (data_ptr + next_offset); + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + VALIDATE_PROPERTY_PAYLOAD_SIZE(rem_bytes - + next_offset - sizeof(*prop) + + sizeof(u32), sizeof(u32), + prop->num_properties); + next_offset += sizeof(*prop) - sizeof(u32) + + prop->num_properties * sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + { + struct msm_vidc_capability capability; + char *ptr = NULL; + u32 count = 0; + u32 prof_count = 0; + struct hfi_profile_level *prof_level; + struct hfi_profile_level_supported *prop = + (struct hfi_profile_level_supported *) + (data_ptr + next_offset); + + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + ptr = (char *) &prop->rg_profile_level[0]; + prof_count = prop->profile_count; + next_offset += sizeof(u32); + + if (prof_count > MAX_PROFILE_COUNT) { + prof_count = MAX_PROFILE_COUNT; + dprintk(VIDC_WARN, + "prop count exceeds max profile count\n"); + break; + } + while (prof_count) { + struct hal_profile_level_supported *caps_pl; + + caps_pl = &capability.profile_level; + prof_level = (struct hfi_profile_level *)ptr; + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prof_level)); + caps_pl->profile_level[count].profile = + prof_level->profile; + caps_pl->profile_level[count].level = + prof_level->level; + prof_count--; + count++; + ptr += sizeof(struct hfi_profile_level); + next_offset += sizeof(struct hfi_profile_level); + } + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(struct hfi_interlace_format_supported)); + next_offset += + sizeof(struct hfi_interlace_format_supported); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(struct hfi_nal_stream_format_supported)); + next_offset += + sizeof(struct hfi_nal_stream_format_supported); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(u32)); + next_offset += sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(u32)); + next_offset += sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: + { + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(struct hfi_intra_refresh)); + next_offset += + sizeof(struct hfi_intra_refresh); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: + { + struct hfi_buffer_alloc_mode_supported *prop = + (struct hfi_buffer_alloc_mode_supported *) + (data_ptr + next_offset); + VALIDATE_PROPERTY_STRUCTURE_SIZE(rem_bytes - + next_offset, + sizeof(*prop)); + if (prop->num_entries >= 32) { + dprintk(VIDC_ERR, + "%s - num_entries: %d from f/w seems suspect\n", + __func__, prop->num_entries); + break; + } + VALIDATE_PROPERTY_PAYLOAD_SIZE(rem_bytes - + next_offset - + sizeof(struct hfi_buffer_alloc_mode_supported) + + sizeof(u32), + sizeof(u32), + prop->num_entries); + next_offset += + sizeof(struct hfi_buffer_alloc_mode_supported) - + sizeof(u32) + prop->num_entries * sizeof(u32); + + copy_alloc_mode_to_sessions(prop, + capabilities, num_sessions, + codecs, domain); + + num_properties--; + break; + } + default: + dprintk(VIDC_DBG, + "%s: default case - data_ptr %pK, prop_id 0x%x\n", + __func__, data_ptr, prop_id); + break; + } + if (rem_bytes > next_offset) { + rem_bytes -= next_offset; + data_ptr += next_offset; + } else { + rem_bytes = 0; + } + } + + return status; +} + +enum vidc_status hfi_process_sys_init_done_prop_read( + struct hfi_msg_sys_init_done_packet *pkt, + struct vidc_hal_sys_init_done *sys_init_done) +{ + enum vidc_status status = VIDC_ERR_NONE; + int bytes_read; + u32 rem_bytes, num_properties; + u8 *data_ptr; + u32 codecs = 0, domain = 0; + + if (!pkt || !sys_init_done) { + dprintk(VIDC_ERR, + "hfi_msg_sys_init_done: Invalid input\n"); + return VIDC_ERR_FAIL; + } + if (pkt->size < sizeof(struct hfi_msg_sys_init_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet size: %d\n", + __func__, pkt->size); + return VIDC_ERR_FAIL; + } + + rem_bytes = pkt->size - sizeof(struct + hfi_msg_sys_init_done_packet) + sizeof(u32); + + if (!rem_bytes) { + dprintk(VIDC_ERR, + "hfi_msg_sys_init_done: missing_prop_info\n"); + return VIDC_ERR_FAIL; + } + + status = hfi_map_err_status(pkt->error_type); + if (status) { + dprintk(VIDC_ERR, "%s: status %#x\n", __func__, status); + return status; + } + + data_ptr = (u8 *) &pkt->rg_property_data[0]; + num_properties = pkt->num_properties; + dprintk(VIDC_DBG, + "%s: data_start %pK, num_properties %#x\n", + __func__, data_ptr, num_properties); + if (!num_properties) { + sys_init_done->capabilities = NULL; + dprintk(VIDC_DBG, + "Venus didn't set any properties in SYS_INIT_DONE"); + return status; + } + bytes_read = hfi_fill_codec_info(data_ptr, sys_init_done, rem_bytes); + if (bytes_read < 0) + return VIDC_ERR_FAIL; + data_ptr += bytes_read; + rem_bytes -= bytes_read; + num_properties--; + + status = hfi_parse_init_done_properties( + sys_init_done->capabilities, + VIDC_MAX_SESSIONS, data_ptr, num_properties, + rem_bytes, codecs, domain); + if (status) { + dprintk(VIDC_ERR, "%s: parse status %#x\n", + __func__, status); + return status; + } + + return status; +} + +static void hfi_process_sess_get_prop_dec_entropy( + struct hfi_msg_session_property_info_packet *prop, + enum hal_h264_entropy *entropy) +{ + u32 req_bytes, hfi_entropy; + + req_bytes = prop->size - sizeof( + struct hfi_msg_session_property_info_packet); + + if (!req_bytes || req_bytes % sizeof(hfi_entropy)) { + dprintk(VIDC_ERR, "%s: bad packet: %d\n", __func__, req_bytes); + return; + } + + hfi_entropy = prop->rg_property_data[1]; + *entropy = + hfi_entropy == HFI_H264_ENTROPY_CAVLC ? HAL_H264_ENTROPY_CAVLC : + hfi_entropy == HFI_H264_ENTROPY_CABAC ? HAL_H264_ENTROPY_CABAC : + HAL_UNUSED_ENTROPY; +} + +static void hfi_process_sess_get_prop_profile_level( + struct hfi_msg_session_property_info_packet *prop, + struct hfi_profile_level *profile_level) +{ + struct hfi_profile_level *hfi_profile_level; + u32 req_bytes; + + dprintk(VIDC_DBG, "Entered %s\n", __func__); + if (!prop) { + dprintk(VIDC_ERR, + "hal_process_sess_get_profile_level: bad_prop: %pK\n", + prop); + return; + } + req_bytes = prop->size - sizeof( + struct hfi_msg_session_property_info_packet); + + if (!req_bytes || req_bytes % sizeof(struct hfi_profile_level)) { + dprintk(VIDC_ERR, + "hal_process_sess_get_profile_level: bad_pkt: %d\n", + req_bytes); + return; + } + hfi_profile_level = (struct hfi_profile_level *) + &prop->rg_property_data[1]; + profile_level->profile = hfi_profile_level->profile; + profile_level->level = hfi_profile_level->level; + dprintk(VIDC_DBG, "%s profile: %d level: %d\n", + __func__, profile_level->profile, + profile_level->level); +} + +static void hfi_process_sess_get_prop_buf_req( + struct hfi_msg_session_property_info_packet *prop, + struct buffer_requirements *buffreq) +{ + struct hfi_buffer_requirements *hfi_buf_req; + u32 req_bytes; + u32 count_min = 0, count_actual = 0; + + if (!prop) { + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_prop: %pK\n", + prop); + return; + } + + req_bytes = prop->size - sizeof( + struct hfi_msg_session_property_info_packet); + if (!req_bytes || req_bytes % sizeof(struct hfi_buffer_requirements) || + !prop->rg_property_data[1]) { + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_pkt: %d\n", + req_bytes); + return; + } + + hfi_buf_req = (struct hfi_buffer_requirements *) + &prop->rg_property_data[1]; + + if (!hfi_buf_req) { + dprintk(VIDC_ERR, "%s - invalid buffer req pointer\n", + __func__); + return; + } + + while (req_bytes) { + count_min = hfi_buf_req->buffer_count_min; + count_actual = hfi_buf_req->buffer_count_actual; + if (hfi_buf_req->buffer_size && count_min > count_actual) + dprintk(VIDC_WARN, + "Bad buff req for %#x: min %d, actual %d\n", + hfi_buf_req->buffer_type, + hfi_buf_req->buffer_count_min, + hfi_buf_req->buffer_count_actual); + + dprintk(VIDC_DBG, "got buffer requirements for: %d\n", + hfi_buf_req->buffer_type); + switch (hfi_buf_req->buffer_type) { + case HFI_BUFFER_INPUT: + memcpy(&buffreq->buffer[0], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[0].buffer_type = HAL_BUFFER_INPUT; + break; + case HFI_BUFFER_OUTPUT: + memcpy(&buffreq->buffer[1], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[1].buffer_type = HAL_BUFFER_OUTPUT; + break; + case HFI_BUFFER_OUTPUT2: + memcpy(&buffreq->buffer[2], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[2].buffer_type = HAL_BUFFER_OUTPUT2; + break; + case HFI_BUFFER_EXTRADATA_INPUT: + memcpy(&buffreq->buffer[3], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[3].buffer_type = + HAL_BUFFER_EXTRADATA_INPUT; + break; + case HFI_BUFFER_EXTRADATA_OUTPUT: + memcpy(&buffreq->buffer[4], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[4].buffer_type = + HAL_BUFFER_EXTRADATA_OUTPUT; + break; + case HFI_BUFFER_EXTRADATA_OUTPUT2: + memcpy(&buffreq->buffer[5], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[5].buffer_type = + HAL_BUFFER_EXTRADATA_OUTPUT2; + break; + case HFI_BUFFER_INTERNAL_SCRATCH: + memcpy(&buffreq->buffer[6], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[6].buffer_type = + HAL_BUFFER_INTERNAL_SCRATCH; + break; + case HFI_BUFFER_INTERNAL_SCRATCH_1: + memcpy(&buffreq->buffer[7], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[7].buffer_type = + HAL_BUFFER_INTERNAL_SCRATCH_1; + break; + case HFI_BUFFER_INTERNAL_SCRATCH_2: + memcpy(&buffreq->buffer[8], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[8].buffer_type = + HAL_BUFFER_INTERNAL_SCRATCH_2; + break; + case HFI_BUFFER_INTERNAL_PERSIST: + memcpy(&buffreq->buffer[9], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[9].buffer_type = + HAL_BUFFER_INTERNAL_PERSIST; + break; + case HFI_BUFFER_INTERNAL_PERSIST_1: + memcpy(&buffreq->buffer[10], hfi_buf_req, + sizeof(struct hfi_buffer_requirements)); + buffreq->buffer[10].buffer_type = + HAL_BUFFER_INTERNAL_PERSIST_1; + break; + default: + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_buffer_type: %d\n", + hfi_buf_req->buffer_type); + break; + } + req_bytes -= sizeof(struct hfi_buffer_requirements); + hfi_buf_req++; + } +} + +static int hfi_process_session_prop_info(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_property_info_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct hfi_profile_level profile_level = {0}; + enum hal_h264_entropy entropy = HAL_UNUSED_ENTROPY; + struct buffer_requirements buff_req = { { {0} } }; + + dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%#x]\n", + pkt->session_id); + + if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) { + dprintk(VIDC_ERR, + "hal_process_session_prop_info: bad_pkt_size\n"); + return -E2BIG; + } else if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hal_process_session_prop_info: no_properties\n"); + return -EINVAL; + } + + switch (pkt->rg_property_data[0]) { + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + hfi_process_sess_get_prop_buf_req(pkt, &buff_req); + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.data.property.buf_req = buff_req; + cmd_done.size = sizeof(buff_req); + break; + + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + hfi_process_sess_get_prop_profile_level(pkt, &profile_level); + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.data.property.profile_level = + (struct hal_profile_level) { + .profile = profile_level.profile, + .level = profile_level.level, + }; + cmd_done.size = sizeof(struct hal_profile_level); + break; + + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + hfi_process_sess_get_prop_dec_entropy(pkt, &entropy); + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.data.property.h264_entropy = entropy; + cmd_done.size = sizeof(enum hal_h264_entropy); + break; + + default: + dprintk(VIDC_DBG, + "hal_process_session_prop_info: unknown_prop_id: %x\n", + pkt->rg_property_data[0]); + return -ENOTSUPP; + } + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_PROPERTY_INFO, + .response.cmd = cmd_done, + }; + return 0; +} + +static int hfi_process_session_init_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_session_init_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct vidc_hal_session_init_done session_init_done = { {0} }; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE[%x]\n", pkt->session_id); + + if (sizeof(struct hfi_msg_sys_session_init_done_packet) > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + if (!cmd_done.status) { + cmd_done.status = hfi_process_session_init_done_prop_read( + pkt, &session_init_done); + } + + cmd_done.data.session_init_done = session_init_done; + cmd_done.size = sizeof(struct vidc_hal_session_init_done); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_INIT_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_load_res_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_load_resources_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE[%#x]\n", + pkt->session_id); + + if (sizeof(struct hfi_msg_session_load_resources_done_packet) != + pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_load_res_done: bad packet size: %d\n", + pkt->size); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_LOAD_RESOURCE_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_flush_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_flush_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE[%#x]\n", + pkt->session_id); + + if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_flush_done: bad packet size: %d\n", + pkt->size); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = sizeof(u32); + + switch (pkt->flush_type) { + case HFI_FLUSH_OUTPUT: + cmd_done.data.flush_type = HAL_FLUSH_OUTPUT; + break; + case HFI_FLUSH_INPUT: + cmd_done.data.flush_type = HAL_FLUSH_INPUT; + break; + case HFI_FLUSH_ALL: + cmd_done.data.flush_type = HAL_FLUSH_ALL; + break; + default: + dprintk(VIDC_ERR, + "%s: invalid flush type!", __func__); + return -EINVAL; + } + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_FLUSH_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_etb_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_empty_buffer_done_packet *pkt = _pkt; + struct msm_vidc_cb_data_done data_done = {0}; + struct hfi_picture_type *hfi_picture_type = NULL; + u32 is_sync_frame; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%#x]\n", pkt->session_id); + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_empty_buffer_done_packet)) + goto bad_packet_size; + + data_done.device_id = device_id; + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.clnt_data = pkt->input_tag; + data_done.input_done.offset = pkt->offset; + data_done.input_done.filled_len = pkt->filled_len; + data_done.input_done.packet_buffer = + (phys_addr_t)pkt->packet_buffer; + data_done.input_done.extra_data_buffer = + (phys_addr_t)pkt->extra_data_buffer; + data_done.input_done.status = + hfi_map_err_status(pkt->error_type); + is_sync_frame = pkt->rgData[0]; + if (is_sync_frame == 1) { + if (pkt->size < + sizeof(struct hfi_msg_session_empty_buffer_done_packet) + + sizeof(struct hfi_picture_type)) + goto bad_packet_size; + hfi_picture_type = (struct hfi_picture_type *)&pkt->rgData[1]; + if (hfi_picture_type->picture_type) + data_done.input_done.flags = + hfi_picture_type->picture_type; + else + dprintk(VIDC_DBG, + "Non-Sync frame sent for H264/HEVC\n"); + } + + trace_msm_v4l2_vidc_buffer_event_end("ETB", + (u32)pkt->packet_buffer, -1, -1, + pkt->filled_len, pkt->offset); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_ETB_DONE, + .response.data = data_done, + }; + + return 0; +bad_packet_size: + dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", + __func__, pkt ? pkt->size : 0); + return -E2BIG; +} + +static int hfi_process_session_ftb_done( + u32 device_id, void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct vidc_hal_msg_pkt_hdr *msg_hdr = _pkt; + struct msm_vidc_cb_data_done data_done = {0}; + bool is_decoder = false, is_encoder = false; + + if (!msg_hdr) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + is_encoder = msg_hdr->size == sizeof(struct + hfi_msg_session_fill_buffer_done_compressed_packet) + 4; + is_decoder = msg_hdr->size == sizeof(struct + hfi_msg_session_fbd_uncompressed_plane0_packet) + 4; + + if (!(is_encoder ^ is_decoder)) { + dprintk(VIDC_ERR, "Ambiguous packet (%#x) received (size %d)\n", + msg_hdr->packet, msg_hdr->size); + return -EBADHANDLE; + } + + if (is_encoder) { + struct hfi_msg_session_fill_buffer_done_compressed_packet *pkt = + (struct hfi_msg_session_fill_buffer_done_compressed_packet *) + msg_hdr; + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%#x]\n", + pkt->session_id); + if (sizeof(struct + hfi_msg_session_fill_buffer_done_compressed_packet) + > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_ftb_done: bad_pkt_size\n"); + return -E2BIG; + } else if (pkt->error_type != HFI_ERR_NONE) { + dprintk(VIDC_ERR, + "got buffer back with error %x\n", + pkt->error_type); + /* Proceed with the FBD */ + } + + data_done.device_id = device_id; + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.clnt_data = 0; + + data_done.output_done.timestamp_hi = pkt->time_stamp_hi; + data_done.output_done.timestamp_lo = pkt->time_stamp_lo; + data_done.output_done.flags1 = pkt->flags; + data_done.output_done.mark_target = pkt->mark_target; + data_done.output_done.mark_data = pkt->mark_data; + data_done.output_done.stats = pkt->stats; + data_done.output_done.offset1 = pkt->offset; + data_done.output_done.alloc_len1 = pkt->alloc_len; + data_done.output_done.filled_len1 = pkt->filled_len; + data_done.output_done.picture_type = pkt->picture_type; + data_done.output_done.packet_buffer1 = + (phys_addr_t)pkt->packet_buffer; + data_done.output_done.extra_data_buffer = + (phys_addr_t)pkt->extra_data_buffer; + data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT; + } else /* if (is_decoder) */ { + struct hfi_msg_session_fbd_uncompressed_plane0_packet *pkt = + (struct hfi_msg_session_fbd_uncompressed_plane0_packet *) + msg_hdr; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%#x]\n", + pkt->session_id); + if (sizeof( + struct hfi_msg_session_fbd_uncompressed_plane0_packet) > + pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_ftb_done: bad_pkt_size\n"); + return -E2BIG; + } + + data_done.device_id = device_id; + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.clnt_data = 0; + + data_done.output_done.stream_id = pkt->stream_id; + data_done.output_done.view_id = pkt->view_id; + data_done.output_done.timestamp_hi = pkt->time_stamp_hi; + data_done.output_done.timestamp_lo = pkt->time_stamp_lo; + data_done.output_done.flags1 = pkt->flags; + data_done.output_done.mark_target = pkt->mark_target; + data_done.output_done.mark_data = pkt->mark_data; + data_done.output_done.stats = pkt->stats; + data_done.output_done.alloc_len1 = pkt->alloc_len; + data_done.output_done.filled_len1 = pkt->filled_len; + data_done.output_done.offset1 = pkt->offset; + data_done.output_done.frame_width = pkt->frame_width; + data_done.output_done.frame_height = pkt->frame_height; + data_done.output_done.start_x_coord = pkt->start_x_coord; + data_done.output_done.start_y_coord = pkt->start_y_coord; + data_done.output_done.input_tag1 = pkt->input_tag; + data_done.output_done.picture_type = pkt->picture_type; + data_done.output_done.packet_buffer1 = pkt->packet_buffer; + data_done.output_done.extra_data_buffer = + pkt->extra_data_buffer; + + if (!pkt->stream_id) + data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT; + else if (pkt->stream_id == 1) + data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT2; + } + + trace_msm_v4l2_vidc_buffer_event_end("FTB", + (u32)data_done.output_done.packet_buffer1, + (((u64)data_done.output_done.timestamp_hi) << 32) + + ((u64)data_done.output_done.timestamp_lo), + data_done.output_done.alloc_len1, + data_done.output_done.filled_len1, + data_done.output_done.offset1); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_FTB_DONE, + .response.data = data_done, + }; + + return 0; +} + +static int hfi_process_session_start_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_start_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_start_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_START_DONE, + .response.cmd = cmd_done, + }; + return 0; +} + +static int hfi_process_session_stop_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_stop_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_stop_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_STOP_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_rel_res_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_release_resources_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_release_resources_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_RELEASE_RESOURCE_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_rel_buf_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_session_release_buffers_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_release_buffers_done_packet)) { + dprintk(VIDC_ERR, "bad packet/packet size %d\n", + pkt ? pkt->size : 0); + return -E2BIG; + } + dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_BUFFER_DONE[%#x]\n", + pkt->session_id); + + cmd_done.device_id = device_id; + cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done); + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data.buffer_info.buffer_addr = *pkt->rg_buffer_info; + cmd_done.size = sizeof(struct hal_buffer_info); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_RELEASE_BUFFER_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_end_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_session_end_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE[%#x]\n", pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_sys_session_end_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", __func__); + return -E2BIG; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_END_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_abort_done(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_session_abort_done_packet *pkt = _pkt; + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE[%#x]\n", + pkt->session_id); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_sys_session_abort_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size: %d\n", + __func__, pkt ? pkt->size : 0); + return -E2BIG; + } + cmd_done.device_id = device_id; + cmd_done.session_id = (void *)(uintptr_t)pkt->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.size = 0; + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_ABORT_DONE, + .response.cmd = cmd_done, + }; + + return 0; +} + +static int hfi_process_session_get_seq_hdr_done( + u32 device_id, + struct hfi_msg_session_get_sequence_header_done_packet *pkt, + struct msm_vidc_cb_info *info) +{ + struct msm_vidc_cb_data_done data_done = {0}; + + if (!pkt || pkt->size != + sizeof(struct + hfi_msg_session_get_sequence_header_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -E2BIG; + } + + dprintk(VIDC_DBG, "RECEIVED:SESSION_GET_SEQ_HDR_DONE[%#x]\n", + pkt->session_id); + + data_done.device_id = device_id; + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.session_id = (void *)(uintptr_t)pkt->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.output_done.packet_buffer1 = + (phys_addr_t)pkt->sequence_header; + data_done.output_done.filled_len1 = pkt->header_len; + dprintk(VIDC_INFO, "seq_hdr: %#x, Length: %d\n", + pkt->sequence_header, pkt->header_len); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_SESSION_GET_SEQ_HDR_DONE, + .response.data = data_done, + }; + + return 0; +} + +static void hfi_process_sys_get_prop_image_version( + struct hfi_msg_sys_property_info_packet *pkt) +{ + int i = 0; + size_t smem_block_size = 0; + u8 *smem_table_ptr; + char version[256]; + const u32 version_string_size = 128; + const u32 smem_image_index_venus = 14 * 128; + u8 *str_image_version; + int req_bytes; + + req_bytes = pkt->size - sizeof(*pkt); + if (req_bytes < version_string_size || + !pkt->rg_property_data[1] || + pkt->num_properties > 1) { + dprintk(VIDC_ERR, + "%s: bad_pkt: %d\n", __func__, req_bytes); + return; + } + str_image_version = (u8 *)&pkt->rg_property_data[1]; + /* + * The version string returned by firmware includes null + * characters at the start and in between. Replace the null + * characters with space, to print the version info. + */ + for (i = 0; i < version_string_size; i++) { + if (str_image_version[i] != '\0') + version[i] = str_image_version[i]; + else + version[i] = ' '; + } + version[i] = '\0'; + dprintk(VIDC_DBG, "F/W version: %s\n", version); + + smem_table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMAGE_VERSION_TABLE, &smem_block_size); + if ((smem_image_index_venus + version_string_size) <= smem_block_size && + smem_table_ptr) + memcpy(smem_table_ptr + smem_image_index_venus, + str_image_version, version_string_size); +} + +static int hfi_process_sys_property_info(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + struct hfi_msg_sys_property_info_packet *pkt = _pkt; + + if (!pkt) { + dprintk(VIDC_ERR, "%s: invalid param\n", __func__); + return -EINVAL; + } else if (pkt->size < sizeof(*pkt)) { + dprintk(VIDC_ERR, + "%s: bad_pkt_size\n", __func__); + return -E2BIG; + } else if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "%s: no_properties\n", __func__); + return -EINVAL; + } + + switch (pkt->rg_property_data[0]) { + case HFI_PROPERTY_SYS_IMAGE_VERSION: + hfi_process_sys_get_prop_image_version(pkt); + + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + }; + return 0; + default: + dprintk(VIDC_DBG, + "%s: unknown_prop_id: %x\n", __func__, + pkt->rg_property_data[0]); + return -ENOTSUPP; + } + +} + +static int hfi_process_ignore(u32 device_id, + void *_pkt, + struct msm_vidc_cb_info *info) +{ + *info = (struct msm_vidc_cb_info) { + .response_type = HAL_RESPONSE_UNUSED, + }; + + return 0; +} + +int hfi_process_msg_packet(u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr, + struct msm_vidc_cb_info *info) +{ + typedef int (*pkt_func_def)(u32, void *, struct msm_vidc_cb_info *info); + pkt_func_def pkt_func = NULL; + + if (!info || !msg_hdr || msg_hdr->size < VIDC_IFACEQ_MIN_PKT_SIZE) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return -EINVAL; + } + + dprintk(VIDC_DBG, "Parse response %#x\n", msg_hdr->packet); + switch (msg_hdr->packet) { + case HFI_MSG_EVENT_NOTIFY: + pkt_func = (pkt_func_def)hfi_process_event_notify; + break; + case HFI_MSG_SYS_INIT_DONE: + pkt_func = (pkt_func_def)hfi_process_sys_init_done; + break; + case HFI_MSG_SYS_SESSION_INIT_DONE: + pkt_func = (pkt_func_def)hfi_process_session_init_done; + break; + case HFI_MSG_SYS_PROPERTY_INFO: + pkt_func = (pkt_func_def)hfi_process_sys_property_info; + break; + case HFI_MSG_SYS_SESSION_END_DONE: + pkt_func = (pkt_func_def)hfi_process_session_end_done; + break; + case HFI_MSG_SESSION_LOAD_RESOURCES_DONE: + pkt_func = (pkt_func_def)hfi_process_session_load_res_done; + break; + case HFI_MSG_SESSION_START_DONE: + pkt_func = (pkt_func_def)hfi_process_session_start_done; + break; + case HFI_MSG_SESSION_STOP_DONE: + pkt_func = (pkt_func_def)hfi_process_session_stop_done; + break; + case HFI_MSG_SESSION_EMPTY_BUFFER_DONE: + pkt_func = (pkt_func_def)hfi_process_session_etb_done; + break; + case HFI_MSG_SESSION_FILL_BUFFER_DONE: + pkt_func = (pkt_func_def)hfi_process_session_ftb_done; + break; + case HFI_MSG_SESSION_FLUSH_DONE: + pkt_func = (pkt_func_def)hfi_process_session_flush_done; + break; + case HFI_MSG_SESSION_PROPERTY_INFO: + pkt_func = (pkt_func_def)hfi_process_session_prop_info; + break; + case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE: + pkt_func = (pkt_func_def)hfi_process_session_rel_res_done; + break; + case HFI_MSG_SYS_RELEASE_RESOURCE: + pkt_func = (pkt_func_def)hfi_process_sys_rel_resource_done; + break; + case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE: + pkt_func = (pkt_func_def) hfi_process_session_get_seq_hdr_done; + break; + case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE: + pkt_func = (pkt_func_def)hfi_process_session_rel_buf_done; + break; + case HFI_MSG_SYS_SESSION_ABORT_DONE: + pkt_func = (pkt_func_def)hfi_process_session_abort_done; + break; + case HFI_MSG_SESSION_SYNC_DONE: + pkt_func = (pkt_func_def)hfi_process_ignore; + break; + default: + dprintk(VIDC_DBG, "Unable to parse message: %#x\n", + msg_hdr->packet); + break; + } + + return pkt_func ? + pkt_func(device_id, (void *)msg_hdr, info) : -ENOTSUPP; +} diff --git a/drivers/media/platform/msm/vidc_3x/msm_smem.c b/drivers/media/platform/msm/vidc_3x/msm_smem.c new file mode 100644 index 000000000000..7db4efe0413b --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_smem.c @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "media/msm_vidc.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_resources.h" + + +static int msm_dma_get_device_address(struct dma_buf *dbuf, unsigned long align, + dma_addr_t *iova, unsigned long *buffer_size, + unsigned long flags, enum hal_buffer buffer_type, + unsigned long session_type, struct msm_vidc_platform_resources *res, + struct dma_mapping_info *mapping_info) +{ + int rc = 0; + struct dma_buf_attachment *attach; + struct sg_table *table = NULL; + struct context_bank_info *cb = NULL; + + if (!dbuf || !iova || !buffer_size || !mapping_info) { + dprintk(VIDC_ERR, "Invalid params: %pK, %pK, %pK, %pK\n", + dbuf, iova, buffer_size, mapping_info); + return -EINVAL; + } + + if (is_iommu_present(res)) { + cb = msm_smem_get_context_bank( + session_type, (flags & SMEM_SECURE), + res, buffer_type); + if (!cb) { + dprintk(VIDC_ERR, + "%s: Failed to get context bank device\n", + __func__); + rc = -EIO; + goto mem_map_failed; + } + + /* Check if the dmabuf size matches expected size */ + if (dbuf->size < *buffer_size) { + rc = -EINVAL; + dprintk(VIDC_ERR, + "Size mismatch: Dmabuf size: %zu Expected Size: %lu\n", + dbuf->size, *buffer_size); + goto mem_buf_size_mismatch; + } + + /* Prepare a dma buf for dma on the given device */ + attach = dma_buf_attach(dbuf, cb->dev); + if (IS_ERR_OR_NULL(attach)) { + rc = PTR_ERR(attach) ?: -ENOMEM; + dprintk(VIDC_ERR, "Failed to attach dmabuf\n"); + goto mem_buf_attach_failed; + } + + /* + * Get the scatterlist for the given attachment + * Mapping of sg is taken care by map attachment + */ + attach->dma_map_attrs = DMA_ATTR_DELAYED_UNMAP; + /* + * We do not need dma_map function to perform cache operations + * on the whole buffer size and hence pass skip sync flag. + * We do the required cache operations separately for the + * required buffer size + */ + attach->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; + /* + *if (res->sys_cache_present) + * attach->dma_map_attrs |= + * DMA_ATTR_IOMMU_USE_UPSTREAM_HINT; + */ + + table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(table)) { + rc = PTR_ERR(table) ?: -ENOMEM; + dprintk(VIDC_ERR, "Failed to map table\n"); + goto mem_map_table_failed; + } + + /* debug trace's need to be updated later */ + trace_msm_smem_buffer_iommu_op_start("MAP", 0, 0, + align, *iova, *buffer_size); + + if (table->sgl) { + *iova = table->sgl->dma_address; + *buffer_size = table->sgl->dma_length; + } else { + dprintk(VIDC_ERR, "sgl is NULL\n"); + rc = -ENOMEM; + goto mem_map_sg_failed; + } + + mapping_info->dev = cb->dev; + mapping_info->domain = cb->domain; + mapping_info->table = table; + mapping_info->attach = attach; + mapping_info->buf = dbuf; + mapping_info->cb_info = (void *)cb; + + trace_msm_smem_buffer_iommu_op_end("MAP", 0, 0, + align, *iova, *buffer_size); + } else { + dprintk(VIDC_DBG, "iommu not present, use phys mem addr\n"); + } + + return 0; +mem_map_sg_failed: + dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL); +mem_map_table_failed: + dma_buf_detach(dbuf, attach); +mem_buf_size_mismatch: +mem_buf_attach_failed: +mem_map_failed: + return rc; +} + +static int msm_dma_put_device_address(u32 flags, + struct dma_mapping_info *mapping_info, + enum hal_buffer buffer_type) +{ + int rc = 0; + + if (!mapping_info) { + dprintk(VIDC_WARN, "Invalid mapping_info\n"); + return -EINVAL; + } + + if (!mapping_info->dev || !mapping_info->table || + !mapping_info->buf || !mapping_info->attach || + !mapping_info->cb_info) { + dprintk(VIDC_WARN, "Invalid params\n"); + return -EINVAL; + } + + trace_msm_smem_buffer_iommu_op_start("UNMAP", 0, 0, 0, 0, 0); + dma_buf_unmap_attachment(mapping_info->attach, + mapping_info->table, DMA_BIDIRECTIONAL); + dma_buf_detach(mapping_info->buf, mapping_info->attach); + trace_msm_smem_buffer_iommu_op_end("UNMAP", 0, 0, 0, 0, 0); + + mapping_info->dev = NULL; + mapping_info->domain = NULL; + mapping_info->table = NULL; + mapping_info->attach = NULL; + mapping_info->buf = NULL; + mapping_info->cb_info = NULL; + + + return rc; +} + +struct dma_buf *msm_smem_get_dma_buf(int fd) +{ + struct dma_buf *dma_buf; + + dma_buf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dma_buf)) { + dprintk(VIDC_ERR, "Failed to get dma_buf for %d, error %ld\n", + fd, PTR_ERR(dma_buf)); + dma_buf = NULL; + } + + return dma_buf; +} + +void msm_smem_put_dma_buf(void *dma_buf) +{ + if (!dma_buf) { + dprintk(VIDC_ERR, "%s: NULL dma_buf\n", __func__); + return; + } + + dma_buf_put((struct dma_buf *)dma_buf); +} +bool msm_smem_compare_buffers(int fd, void *dma_buf) +{ + unsigned long dma_plane; + + /* + * always compare dma_buf addresses which is guaranteed + * to be same across the processes (duplicate fds). + */ + dma_plane = (unsigned long)msm_smem_get_dma_buf( + fd); + if (!dma_plane) + return false; + msm_smem_put_dma_buf((struct dma_buf *)dma_plane); + + if ((unsigned long)dma_buf == dma_plane) + return true; + + return false; + +} +int msm_smem_map_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem) +{ + int rc = 0; + + dma_addr_t iova = 0; + u32 temp = 0; + unsigned long buffer_size = 0; + unsigned long align = SZ_4K; + struct dma_buf *dbuf; + unsigned long ion_flags = 0; + + if (!inst || !smem) { + dprintk(VIDC_ERR, "%s: Invalid params: %pK %pK\n", + __func__, inst, smem); + rc = -EINVAL; + goto exit; + } + + if (smem->refcount) { + smem->refcount++; + goto exit; + } + + dbuf = msm_smem_get_dma_buf(smem->fd); + if (!dbuf) { + rc = -EINVAL; + goto exit; + } + + smem->dma_buf = dbuf; + + rc = dma_buf_get_flags(dbuf, &ion_flags); + if (rc) { + dprintk(VIDC_ERR, "Failed to get dma buf flags: %d\n", rc); + goto exit; + } + if (ion_flags & ION_FLAG_CACHED) + smem->flags |= SMEM_CACHED; + + if (ion_flags & ION_FLAG_SECURE) + smem->flags |= SMEM_SECURE; + + buffer_size = smem->size; + + rc = msm_dma_get_device_address(dbuf, align, &iova, &buffer_size, + smem->flags, smem->buffer_type, inst->session_type, + &(inst->core->resources), &smem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc); + goto exit; + } + temp = (u32)iova; + if ((dma_addr_t)temp != iova) { + dprintk(VIDC_ERR, "iova(%pa) truncated to %#x", &iova, temp); + rc = -EINVAL; + goto exit; + } + + smem->device_addr = (u32)iova; + + smem->refcount++; +exit: + return rc; +} + +int msm_smem_unmap_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem) +{ + int rc = 0; + + if (!inst || !smem) { + dprintk(VIDC_ERR, "%s: Invalid params: %pK %pK\n", + __func__, inst, smem); + rc = -EINVAL; + goto exit; + } + + if (smem->refcount) { + smem->refcount--; + } else { + dprintk(VIDC_WARN, + "unmap called while refcount is zero already\n"); + return -EINVAL; + } + + if (smem->refcount) + goto exit; + + rc = msm_dma_put_device_address(smem->flags, &smem->mapping_info, + smem->buffer_type); + if (rc) { + dprintk(VIDC_ERR, "Failed to put device address: %d\n", rc); + goto exit; + } + + msm_smem_put_dma_buf(smem->dma_buf); + + smem->device_addr = 0x0; + smem->dma_buf = NULL; + +exit: + return rc; +} +struct msm_smem *msm_smem_user_to_kernel(struct msm_vidc_inst *inst, int fd, + u32 offset, u32 size, + enum hal_buffer buffer_type) +{ + int rc = 0; + struct msm_smem *mem; + + if (fd < 0 || !inst) { + dprintk(VIDC_ERR, "Invalid fd: %d\n", fd); + return NULL; + } + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) { + dprintk(VIDC_ERR, "Failed to allocate shared mem\n"); + return NULL; + } + + mem->fd = fd; + mem->size = size; + mem->buffer_type = buffer_type; + mem->offset = offset; + + //rc = ion_user_to_kernel(clt, fd, offset, mem, buffer_type); + rc = msm_smem_map_dma_buf(inst, mem); + if (rc) { + dprintk(VIDC_ERR, "Failed to allocate shared memory\n"); + kfree(mem); + mem = NULL; + } + return mem; +} +static int get_secure_flag_for_buffer_type( + u32 session_type, enum hal_buffer buffer_type) +{ + switch (buffer_type) { + case HAL_BUFFER_INPUT: + if (session_type == MSM_VIDC_ENCODER) + return ION_FLAG_CP_PIXEL; + else + return ION_FLAG_CP_BITSTREAM; + case HAL_BUFFER_OUTPUT: + case HAL_BUFFER_OUTPUT2: + if (session_type == MSM_VIDC_ENCODER) + return ION_FLAG_CP_BITSTREAM; + else + return ION_FLAG_CP_PIXEL; + case HAL_BUFFER_INTERNAL_SCRATCH: + return ION_FLAG_CP_BITSTREAM; + case HAL_BUFFER_INTERNAL_SCRATCH_1: + return ION_FLAG_CP_NON_PIXEL; + case HAL_BUFFER_INTERNAL_SCRATCH_2: + return ION_FLAG_CP_PIXEL; + case HAL_BUFFER_INTERNAL_PERSIST: + if (session_type == MSM_VIDC_ENCODER) + return ION_FLAG_CP_NON_PIXEL; + else + return ION_FLAG_CP_BITSTREAM; + case HAL_BUFFER_INTERNAL_PERSIST_1: + return ION_FLAG_CP_NON_PIXEL; + default: + WARN(1, "No matching secure flag for buffer type : %x\n", + buffer_type); + return -EINVAL; + } +} + +static int alloc_dma_mem(size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel, + struct msm_vidc_platform_resources *res, u32 session_type, + struct msm_smem *mem) +{ + dma_addr_t iova = 0; + unsigned long buffer_size = 0; + unsigned long heap_mask = 0; + int rc = 0; + int ion_flags = 0; + struct dma_buf *dbuf = NULL; + + if (!res) { + dprintk(VIDC_ERR, "%s: NULL res\n", __func__); + return -EINVAL; + } + + align = ALIGN(align, SZ_4K); + size = ALIGN(size, SZ_4K); + + if (is_iommu_present(res)) { + if (flags & SMEM_ADSP) { + dprintk(VIDC_DBG, "Allocating from ADSP heap\n"); + heap_mask = ION_HEAP(ION_ADSP_HEAP_ID); + } else { + heap_mask = ION_HEAP(ION_SYSTEM_HEAP_ID); + } + } else { + dprintk(VIDC_DBG, + "allocate shared memory from adsp heap size %zx align %d\n", + size, align); + heap_mask = ION_HEAP(ION_ADSP_HEAP_ID); + } + + if (flags & SMEM_CACHED) + ion_flags |= ION_FLAG_CACHED; + + if ((flags & SMEM_SECURE) || + (buffer_type == HAL_BUFFER_INTERNAL_PERSIST && + session_type == MSM_VIDC_ENCODER)) { + int secure_flag = + get_secure_flag_for_buffer_type( + session_type, buffer_type); + if (secure_flag < 0) { + rc = secure_flag; + goto fail_shared_mem_alloc; + } + + ion_flags |= ION_FLAG_SECURE | secure_flag; + heap_mask = ION_HEAP(ION_SECURE_HEAP_ID); + + if (res->slave_side_cp) { + heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID); + size = ALIGN(size, SZ_1M); + align = ALIGN(size, SZ_1M); + } + flags |= SMEM_SECURE; + } + + trace_msm_smem_buffer_dma_op_start("ALLOC", (u32)buffer_type, + heap_mask, size, align, flags, map_kernel); + dbuf = ion_alloc(size, heap_mask, ion_flags); + if (IS_ERR_OR_NULL(dbuf)) { + dprintk(VIDC_ERR, + "Failed to allocate shared memory = %zx, %#x\n", + size, flags); + rc = -ENOMEM; + goto fail_shared_mem_alloc; + } + trace_msm_smem_buffer_dma_op_end("ALLOC", (u32)buffer_type, + heap_mask, size, align, flags, map_kernel); + + mem->flags = flags; + mem->buffer_type = buffer_type; + mem->offset = 0; + mem->size = size; + mem->dma_buf = dbuf; + mem->kvaddr = NULL; + + rc = msm_dma_get_device_address(dbuf, align, &iova, + &buffer_size, flags, buffer_type, + session_type, res, &mem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get device address: %d\n", + rc); + goto fail_device_address; + } + mem->device_addr = (u32)iova; + if ((dma_addr_t)mem->device_addr != iova) { + dprintk(VIDC_ERR, "iova(%pa) truncated to %#x", + &iova, mem->device_addr); + goto fail_device_address; + } + + if (map_kernel) { + dma_buf_begin_cpu_access(dbuf, DMA_BIDIRECTIONAL); + mem->kvaddr = dma_buf_vmap(dbuf); + if (!mem->kvaddr) { + dprintk(VIDC_ERR, + "Failed to map shared mem in kernel\n"); + rc = -EIO; + goto fail_map; + } + } + + dprintk(VIDC_DBG, + "%s: dma_buf = %pK, device_addr = %x, size = %d, kvaddr = %pK, buffer_type = %#x, flags = %#lx\n", + __func__, mem->dma_buf, mem->device_addr, mem->size, + mem->kvaddr, mem->buffer_type, mem->flags); + return rc; + +fail_map: + if (map_kernel) + dma_buf_end_cpu_access(dbuf, DMA_BIDIRECTIONAL); +fail_device_address: + dma_buf_put(dbuf); +fail_shared_mem_alloc: + return rc; +} + +static int free_dma_mem(struct msm_smem *mem) +{ + dprintk(VIDC_DBG, + "%s: dma_buf = %pK, device_addr = %x, size = %d, kvaddr = %pK, buffer_type = %#x\n", + __func__, mem->dma_buf, mem->device_addr, mem->size, + mem->kvaddr, mem->buffer_type); + + if (mem->device_addr) { + msm_dma_put_device_address(mem->flags, + &mem->mapping_info, mem->buffer_type); + mem->device_addr = 0x0; + } + + if (mem->kvaddr) { + dma_buf_vunmap(mem->dma_buf, mem->kvaddr); + mem->kvaddr = NULL; + dma_buf_end_cpu_access(mem->dma_buf, DMA_BIDIRECTIONAL); + } + + if (mem->dma_buf) { + trace_msm_smem_buffer_dma_op_start("FREE", + (u32)mem->buffer_type, -1, mem->size, -1, + mem->flags, -1); + dma_buf_put(mem->dma_buf); + mem->dma_buf = NULL; + trace_msm_smem_buffer_dma_op_end("FREE", (u32)mem->buffer_type, + -1, mem->size, -1, mem->flags, -1); + } + + return 0; +} + +int msm_smem_alloc(size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel, + void *res, u32 session_type, struct msm_smem *smem) +{ + int rc = 0; + + if (!smem || !size) { + dprintk(VIDC_ERR, "%s: NULL smem or %d size\n", + __func__, (u32)size); + return -EINVAL; + } + + rc = alloc_dma_mem(size, align, flags, buffer_type, map_kernel, + (struct msm_vidc_platform_resources *)res, + session_type, smem); + + return rc; +} + +int msm_smem_free(struct msm_smem *smem) +{ + int rc = 0; + + if (!smem) { + dprintk(VIDC_ERR, "NULL smem passed\n"); + return -EINVAL; + } + rc = free_dma_mem(smem); + + return rc; +}; + +int msm_smem_cache_operations(struct dma_buf *dbuf, + enum smem_cache_ops cache_op, unsigned long offset, unsigned long size) +{ + int rc = 0; + unsigned long flags = 0; + + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return -EINVAL; + } + + /* Return if buffer doesn't support caching */ + rc = dma_buf_get_flags(dbuf, &flags); + if (rc) { + dprintk(VIDC_ERR, "%s: dma_buf_get_flags failed, err %d\n", + __func__, rc); + return rc; + } else if (!(flags & ION_FLAG_CACHED)) { + return rc; + } + + switch (cache_op) { + case SMEM_CACHE_CLEAN: + case SMEM_CACHE_CLEAN_INVALIDATE: + rc = dma_buf_begin_cpu_access_partial(dbuf, DMA_TO_DEVICE, + offset, size); + if (rc) + break; + rc = dma_buf_end_cpu_access_partial(dbuf, DMA_TO_DEVICE, + offset, size); + break; + case SMEM_CACHE_INVALIDATE: + rc = dma_buf_begin_cpu_access_partial(dbuf, DMA_TO_DEVICE, + offset, size); + if (rc) + break; + rc = dma_buf_end_cpu_access_partial(dbuf, DMA_FROM_DEVICE, + offset, size); + break; + default: + dprintk(VIDC_ERR, "%s: cache (%d) operation not supported\n", + __func__, cache_op); + rc = -EINVAL; + break; + } + + return rc; +} + +struct context_bank_info *msm_smem_get_context_bank(u32 session_type, + bool is_secure, struct msm_vidc_platform_resources *res, + enum hal_buffer buffer_type) +{ + struct context_bank_info *cb = NULL, *match = NULL; + + /* + * HAL_BUFFER_INPUT is directly mapped to bitstream CB in DT + * as the buffer type structure was initially designed + * just for decoder. For Encoder, input should be mapped to + * yuvpixel CB. Persist is mapped to nonpixel CB. + * So swap the buffer types just in this local scope. + */ + if (is_secure && session_type == MSM_VIDC_ENCODER) { + if (buffer_type == HAL_BUFFER_INPUT) + buffer_type = HAL_BUFFER_OUTPUT; + else if (buffer_type == HAL_BUFFER_OUTPUT) + buffer_type = HAL_BUFFER_INPUT; + else if (buffer_type == HAL_BUFFER_INTERNAL_PERSIST) + buffer_type = HAL_BUFFER_INTERNAL_PERSIST_1; + } + + list_for_each_entry(cb, &res->context_banks, list) { + if (cb->is_secure == is_secure && + cb->buffer_type & buffer_type) { + match = cb; + break; + } + } + if (!match) + dprintk(VIDC_ERR, + "%s: cb not found for buffer_type %x, is_secure %d\n", + __func__, buffer_type, is_secure); + + return match; +} + diff --git a/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c new file mode 100644 index 000000000000..00a35099babd --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_v4l2_vidc.c @@ -0,0 +1,835 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc_common.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_internal.h" +#include "msm_vidc_res_parse.h" +#include "msm_vidc_resources.h" +#include "venus_boot.h" +#include "vidc_hfi_api.h" + +#define BASE_DEVICE_NUMBER 32 + +struct msm_vidc_drv *vidc_driver; + +uint32_t msm_vidc_pwr_collapse_delay = 2000; + +static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh) +{ + return container_of(filp->private_data, + struct msm_vidc_inst, event_handler); +} + +static int msm_v4l2_open(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct msm_video_device *vid_dev = + container_of(vdev, struct msm_video_device, vdev); + struct msm_vidc_core *core = video_drvdata(filp); + struct msm_vidc_inst *vidc_inst; + + trace_msm_v4l2_vidc_open_start("v4l2-vidc open start"); + vidc_inst = msm_vidc_open(core->id, vid_dev->type); + if (!vidc_inst) { + dprintk(VIDC_ERR, + "Failed to create video instance, core: %d, type = %d\n", + core->id, vid_dev->type); + return -ENOMEM; + } + clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags); + filp->private_data = &(vidc_inst->event_handler); + trace_msm_v4l2_vidc_open_end("v4l2-vidc open end"); + return 0; +} + +static int msm_v4l2_close(struct file *filp) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst; + + trace_msm_v4l2_vidc_close_start("v4l2-vidc close start"); + vidc_inst = get_vidc_inst(filp, NULL); + rc = msm_vidc_release_buffers(vidc_inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (rc) + dprintk(VIDC_WARN, + "Failed in %s for release output buffers\n", __func__); + + rc = msm_vidc_close(vidc_inst); + trace_msm_v4l2_vidc_close_end("v4l2-vidc close end"); + return rc; +} + +static int msm_v4l2_querycap(struct file *filp, void *fh, + struct v4l2_capability *cap) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, fh); + + return msm_vidc_querycap((void *)vidc_inst, cap); +} + +int msm_v4l2_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_enum_fmt((void *)vidc_inst, f); +} + +int msm_v4l2_s_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_s_fmt((void *)vidc_inst, f); +} + +int msm_v4l2_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_g_fmt((void *)vidc_inst, f); +} + +int msm_v4l2_s_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_s_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_g_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_g_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_s_ext_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_s_ext_ctrl((void *)vidc_inst, a); +} + +int msm_v4l2_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + int rc = 0; + + if (!b->count) + rc = msm_vidc_release_buffers(vidc_inst, b->type); + if (rc) + dprintk(VIDC_WARN, + "Failed in %s for release output buffers\n", __func__); + return msm_vidc_reqbufs((void *)vidc_inst, b); +} + +int msm_v4l2_prepare_buf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + return msm_vidc_prepare_buf(get_vidc_inst(file, fh), b); +} + +int msm_v4l2_qbuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + return msm_vidc_qbuf(get_vidc_inst(file, fh), b); +} + +int msm_v4l2_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + return msm_vidc_dqbuf(get_vidc_inst(file, fh), b); +} + +int msm_v4l2_streamon(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_streamon((void *)vidc_inst, i); +} + +int msm_v4l2_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_streamoff((void *)vidc_inst, i); +} + +static int msm_v4l2_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct msm_vidc_inst *vidc_inst = container_of(fh, + struct msm_vidc_inst, event_handler); + + return msm_vidc_subscribe_event((void *)vidc_inst, sub); +} + +static int msm_v4l2_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct msm_vidc_inst *vidc_inst = container_of(fh, + struct msm_vidc_inst, event_handler); + + return msm_vidc_unsubscribe_event((void *)vidc_inst, sub); +} + +static int msm_v4l2_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dec) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_comm_cmd((void *)vidc_inst, (union msm_v4l2_cmd *)dec); +} + +static int msm_v4l2_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *enc) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_comm_cmd((void *)vidc_inst, (union msm_v4l2_cmd *)enc); +} +static int msm_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_comm_s_parm(vidc_inst, a); +} +static int msm_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + return 0; +} + +static int msm_v4l2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_enum_framesizes((void *)vidc_inst, fsize); +} + +static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { + .vidioc_querycap = msm_v4l2_querycap, + .vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt, + .vidioc_enum_fmt_vid_out_mplane = msm_v4l2_enum_fmt, + .vidioc_s_fmt_vid_cap_mplane = msm_v4l2_s_fmt, + .vidioc_s_fmt_vid_out_mplane = msm_v4l2_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = msm_v4l2_g_fmt, + .vidioc_g_fmt_vid_out_mplane = msm_v4l2_g_fmt, + .vidioc_reqbufs = msm_v4l2_reqbufs, + .vidioc_prepare_buf = msm_v4l2_prepare_buf, + .vidioc_qbuf = msm_v4l2_qbuf, + .vidioc_dqbuf = msm_v4l2_dqbuf, + .vidioc_streamon = msm_v4l2_streamon, + .vidioc_streamoff = msm_v4l2_streamoff, + .vidioc_s_ctrl = msm_v4l2_s_ctrl, + .vidioc_g_ctrl = msm_v4l2_g_ctrl, + .vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl, + .vidioc_subscribe_event = msm_v4l2_subscribe_event, + .vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event, + .vidioc_decoder_cmd = msm_v4l2_decoder_cmd, + .vidioc_encoder_cmd = msm_v4l2_encoder_cmd, + .vidioc_s_parm = msm_v4l2_s_parm, + .vidioc_g_parm = msm_v4l2_g_parm, + .vidioc_enum_framesizes = msm_v4l2_enum_framesizes, +}; + +static const struct v4l2_ioctl_ops msm_v4l2_enc_ioctl_ops = { +}; + +static unsigned int msm_v4l2_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, NULL); + + return msm_vidc_poll((void *)vidc_inst, filp, pt); +} + +static const struct v4l2_file_operations msm_v4l2_vidc_fops = { + .owner = THIS_MODULE, + .open = msm_v4l2_open, + .release = msm_v4l2_close, + .unlocked_ioctl = video_ioctl2, + .poll = msm_v4l2_poll, +}; + +void msm_vidc_release_video_device(struct video_device *pvdev) +{ +} + +static int read_platform_resources(struct msm_vidc_core *core, + struct platform_device *pdev) +{ + if (!core || !pdev) { + dprintk(VIDC_ERR, "%s: Invalid params %pK %pK\n", + __func__, core, pdev); + return -EINVAL; + } + + core->hfi_type = VIDC_HFI_VENUS; + core->resources.pdev = pdev; + if (pdev->dev.of_node) { + /* Target supports DT, parse from it */ + return read_platform_resources_from_dt(&core->resources); + } + dprintk(VIDC_ERR, "pdev node is NULL\n"); + return -EINVAL; +} + +static int msm_vidc_initialize_core(struct platform_device *pdev, + struct msm_vidc_core *core) +{ + int i = 0; + int rc = 0; + + if (!core) + return -EINVAL; + rc = read_platform_resources(core, pdev); + if (rc) { + dprintk(VIDC_ERR, "Failed to get platform resources\n"); + return rc; + } + + INIT_LIST_HEAD(&core->instances); + mutex_init(&core->lock); + + core->state = VIDC_CORE_UNINIT; + for (i = SYS_MSG_INDEX(SYS_MSG_START); + i <= SYS_MSG_INDEX(SYS_MSG_END); i++) { + init_completion(&core->completions[i]); + } + + INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler); + return rc; +} + +static ssize_t link_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct msm_vidc_core *core = dev_get_drvdata(dev); + + if (core) + if (dev == &core->vdev[MSM_VIDC_DECODER].vdev.dev) + return scnprintf(buf, PAGE_SIZE, "venus_dec"); + else if (dev == &core->vdev[MSM_VIDC_ENCODER].vdev.dev) + return scnprintf(buf, PAGE_SIZE, "venus_enc"); + else + return 0; + else + return 0; +} + +static DEVICE_ATTR_RO(link_name); + +static ssize_t pwr_collapse_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val = 0; + int rc = 0; + + rc = kstrtoul(buf, 0, &val); + if (rc) + return rc; + else if (!val) + return -EINVAL; + msm_vidc_pwr_collapse_delay = val; + return count; +} + +static ssize_t pwr_collapse_delay_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%u\n", msm_vidc_pwr_collapse_delay); +} + +static DEVICE_ATTR_RW(pwr_collapse_delay); + +static ssize_t thermal_level_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", vidc_driver->thermal_level); +} + +static ssize_t thermal_level_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0, val = 0; + + rc = kstrtoint(buf, 0, &val); + if (rc || val < 0) { + dprintk(VIDC_WARN, + "Invalid thermal level value: %s\n", buf); + return -EINVAL; + } + dprintk(VIDC_DBG, "Thermal level old %d new %d\n", + vidc_driver->thermal_level, val); + + if (val == vidc_driver->thermal_level) + return count; + vidc_driver->thermal_level = val; + + msm_comm_handle_thermal_event(); + return count; +} + +static DEVICE_ATTR_RW(thermal_level); + +static ssize_t platform_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", + vidc_driver->platform_version); +} + +static ssize_t platform_version_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + dprintk(VIDC_WARN, "store platform version is not allowed\n"); + return count; +} + +static DEVICE_ATTR_RW(platform_version); + +static ssize_t capability_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", + vidc_driver->capability_version); +} + +static ssize_t capability_version_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + dprintk(VIDC_WARN, "store capability version is not allowed\n"); + return count; +} + +static DEVICE_ATTR_RW(capability_version); + +static struct attribute *msm_vidc_core_attrs[] = { + &dev_attr_pwr_collapse_delay.attr, + &dev_attr_thermal_level.attr, + &dev_attr_platform_version.attr, + &dev_attr_capability_version.attr, + NULL +}; + +static struct attribute_group msm_vidc_core_attr_group = { + .attrs = msm_vidc_core_attrs, +}; + +static const struct of_device_id msm_vidc_dt_match[] = { + {.compatible = "qcom,msm-vidc"}, + {.compatible = "qcom,msm-vidc,context-bank"}, + {.compatible = "qcom,msm-vidc,bus"}, + {} +}; + +static u32 msm_vidc_read_efuse_version(struct platform_device *pdev, + struct version_table *table, const char *fuse_name) +{ + void __iomem *base; + struct resource *res; + u32 ret = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, fuse_name); + if (!res) { + dprintk(VIDC_DBG, "Failed to get resource %s\n", fuse_name); + goto exit; + } + base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!base) { + dprintk(VIDC_ERR, + "failed ioremap: res->start %#x, size %d\n", + (u32)res->start, (u32)resource_size(res)); + goto exit; + } else { + ret = readl_relaxed(base); + ret = (ret & table->version_mask) >> + table->version_shift; + +// devm_iounmap(&pdev->dev, base); + } +exit: + return ret; +} + +static int msm_vidc_probe_vidc_device(struct platform_device *pdev) +{ + int rc = 0; + struct msm_vidc_core *core; + struct device *dev; + int nr = BASE_DEVICE_NUMBER; + + if (!vidc_driver) { + dprintk(VIDC_ERR, "Invalid vidc driver\n"); + return -EINVAL; + } + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) { + dprintk(VIDC_ERR, + "Failed to allocate memory for device core\n"); + return -ENOMEM; + } + + dev_set_drvdata(&pdev->dev, core); + rc = msm_vidc_initialize_core(pdev, core); + if (rc) { + dprintk(VIDC_ERR, "Failed to init core\n"); + goto err_core_init; + } + rc = sysfs_create_group(&pdev->dev.kobj, &msm_vidc_core_attr_group); + if (rc) { + dprintk(VIDC_ERR, + "Failed to create attributes\n"); + goto err_core_init; + } + + core->id = MSM_VIDC_CORE_VENUS; + + rc = v4l2_device_register(&pdev->dev, &core->v4l2_dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to register v4l2 device\n"); + goto err_v4l2_register; + } + + /* setup the decoder device */ + core->vdev[MSM_VIDC_DECODER].vdev.release = + msm_vidc_release_video_device; + core->vdev[MSM_VIDC_DECODER].vdev.fops = &msm_v4l2_vidc_fops; + core->vdev[MSM_VIDC_DECODER].vdev.ioctl_ops = &msm_v4l2_ioctl_ops; + core->vdev[MSM_VIDC_DECODER].vdev.vfl_dir = VFL_DIR_M2M; + core->vdev[MSM_VIDC_DECODER].type = MSM_VIDC_DECODER; + core->vdev[MSM_VIDC_DECODER].vdev.v4l2_dev = &core->v4l2_dev; + rc = video_register_device(&core->vdev[MSM_VIDC_DECODER].vdev, + VFL_TYPE_GRABBER, nr); + if (rc) { + dprintk(VIDC_ERR, "Failed to register video decoder device"); + goto err_dec_register; + } + + video_set_drvdata(&core->vdev[MSM_VIDC_DECODER].vdev, core); + dev = &core->vdev[MSM_VIDC_DECODER].vdev.dev; + rc = device_create_file(dev, &dev_attr_link_name); + if (rc) { + dprintk(VIDC_ERR, + "Failed to create link name sysfs for decoder"); + goto err_dec_attr_link_name; + } + + /* setup the encoder device */ + core->vdev[MSM_VIDC_ENCODER].vdev.release = + msm_vidc_release_video_device; + core->vdev[MSM_VIDC_ENCODER].vdev.fops = &msm_v4l2_vidc_fops; + core->vdev[MSM_VIDC_ENCODER].vdev.ioctl_ops = &msm_v4l2_ioctl_ops; + core->vdev[MSM_VIDC_ENCODER].vdev.vfl_dir = VFL_DIR_M2M; + core->vdev[MSM_VIDC_ENCODER].type = MSM_VIDC_ENCODER; + core->vdev[MSM_VIDC_ENCODER].vdev.v4l2_dev = &core->v4l2_dev; + rc = video_register_device(&core->vdev[MSM_VIDC_ENCODER].vdev, + VFL_TYPE_GRABBER, nr + 1); + if (rc) { + dprintk(VIDC_ERR, "Failed to register video encoder device"); + goto err_enc_register; + } + + video_set_drvdata(&core->vdev[MSM_VIDC_ENCODER].vdev, core); + dev = &core->vdev[MSM_VIDC_ENCODER].vdev.dev; + rc = device_create_file(dev, &dev_attr_link_name); + if (rc) { + dprintk(VIDC_ERR, + "Failed to create link name sysfs for encoder"); + goto err_enc_attr_link_name; + } + + /* finish setting up the 'core' */ + mutex_lock(&vidc_driver->lock); + if (vidc_driver->num_cores + 1 > MSM_VIDC_CORES_MAX) { + mutex_unlock(&vidc_driver->lock); + dprintk(VIDC_ERR, "Maximum cores already exist, core_no = %d\n", + vidc_driver->num_cores); + goto err_cores_exceeded; + } + vidc_driver->num_cores++; + mutex_unlock(&vidc_driver->lock); + + core->device = vidc_hfi_initialize(core->hfi_type, core->id, + &core->resources, &handle_cmd_response); + if (IS_ERR_OR_NULL(core->device)) { + mutex_lock(&vidc_driver->lock); + vidc_driver->num_cores--; + mutex_unlock(&vidc_driver->lock); + + rc = PTR_ERR(core->device) ?: -EBADHANDLE; + if (rc != -EPROBE_DEFER) + dprintk(VIDC_ERR, "Failed to create HFI device\n"); + else + dprintk(VIDC_DBG, "msm_vidc: request probe defer\n"); + goto err_cores_exceeded; + } + + mutex_lock(&vidc_driver->lock); + list_add_tail(&core->list, &vidc_driver->cores); + mutex_unlock(&vidc_driver->lock); + + core->debugfs_root = msm_vidc_debugfs_init_core( + core, vidc_driver->debugfs_root); + + vidc_driver->platform_version = + msm_vidc_read_efuse_version(pdev, + core->resources.pf_ver_tbl, "efuse"); + + vidc_driver->capability_version = + msm_vidc_read_efuse_version( + pdev, core->resources.pf_cap_tbl, "efuse2"); + + rc = call_hfi_op(core->device, core_early_init, + core->device->hfi_device_data); + if (rc) { + dprintk(VIDC_ERR, "Failed to early init core\n"); + goto err_fail_sub_device_probe; + } + dprintk(VIDC_DBG, "populating sub devices\n"); + /* + * Trigger probe for each sub-device i.e. qcom,msm-vidc,context-bank. + * When msm_vidc_probe is called for each sub-device, parse the + * context-bank details and store it in core->resources.context_banks + * list. + */ + rc = of_platform_populate(pdev->dev.of_node, msm_vidc_dt_match, NULL, + &pdev->dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to trigger probe for sub-devices\n"); + call_hfi_op(core->device, core_early_release, + core->device->hfi_device_data); + goto err_fail_sub_device_probe; + } + call_hfi_op(core->device, core_early_release, + core->device->hfi_device_data); + + return rc; + +err_fail_sub_device_probe: + vidc_hfi_deinitialize(core->hfi_type, core->device); +err_cores_exceeded: + device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev, + &dev_attr_link_name); +err_enc_attr_link_name: + video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev); +err_enc_register: + device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev, + &dev_attr_link_name); +err_dec_attr_link_name: + video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev); +err_dec_register: + v4l2_device_unregister(&core->v4l2_dev); +err_v4l2_register: + sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group); +err_core_init: + dev_set_drvdata(&pdev->dev, NULL); + kfree(core); + return rc; +} + +static int msm_vidc_probe_context_bank(struct platform_device *pdev) +{ + return read_context_bank_resources_from_dt(pdev); +} + +static int msm_vidc_probe_bus(struct platform_device *pdev) +{ + return read_bus_resources_from_dt(pdev); +} + +static int msm_vidc_probe(struct platform_device *pdev) +{ + /* + * Sub devices probe will be triggered by of_platform_populate() towards + * the end of the probe function after msm-vidc device probe is + * completed. Return immediately after completing sub-device probe. + */ + if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-vidc")) { + return msm_vidc_probe_vidc_device(pdev); + } else if (of_device_is_compatible(pdev->dev.of_node, + "qcom,msm-vidc,bus")) { + return msm_vidc_probe_bus(pdev); + } else if (of_device_is_compatible(pdev->dev.of_node, + "qcom,msm-vidc,context-bank")) { + return msm_vidc_probe_context_bank(pdev); + } + /* How did we end up here? */ + WARN_ON(VIDC_DBG_WARN_ENABLE); + return -EINVAL; +} + +static int msm_vidc_remove(struct platform_device *pdev) +{ + int rc = 0; + struct msm_vidc_core *core; + + if (!pdev) { + dprintk(VIDC_ERR, "%s invalid input %pK", __func__, pdev); + return -EINVAL; + } + + core = dev_get_drvdata(&pdev->dev); + if (!core) { + dprintk(VIDC_ERR, "%s invalid core", __func__); + return -EINVAL; + } + + if (core->resources.use_non_secure_pil) + venus_boot_deinit(); + + vidc_hfi_deinitialize(core->hfi_type, core->device); + device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev); + device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev, + &dev_attr_link_name); + video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev); + v4l2_device_unregister(&core->v4l2_dev); + + msm_vidc_free_platform_resources(&core->resources); + sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group); + dev_set_drvdata(&pdev->dev, NULL); + kfree(core); + return rc; +} + +static int msm_vidc_pm_suspend(struct device *dev) +{ + int rc = 0; + struct msm_vidc_core *core; + + /* + * Bail out if + * - driver possibly not probed yet + * - not the main device. We don't support power management on + * subdevices (e.g. context banks) + */ + if (!dev || !dev->driver || + !of_device_is_compatible(dev->of_node, "qcom,msm-vidc")) + return 0; + + core = dev_get_drvdata(dev); + if (!core) { + dprintk(VIDC_ERR, "%s invalid core\n", __func__); + return -EINVAL; + } + + rc = msm_vidc_suspend(core->id); + if (rc == -ENOTSUPP) + rc = 0; + else if (rc) + dprintk(VIDC_WARN, "Failed to suspend: %d\n", rc); + + + return rc; +} + +static int msm_vidc_pm_resume(struct device *dev) +{ + dprintk(VIDC_INFO, "%s\n", __func__); + return 0; +} + +static const struct dev_pm_ops msm_vidc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msm_vidc_pm_suspend, msm_vidc_pm_resume) +}; + +MODULE_DEVICE_TABLE(of, msm_vidc_dt_match); + +static struct platform_driver msm_vidc_driver = { + .probe = msm_vidc_probe, + .remove = msm_vidc_remove, + .driver = { + .name = "msm_vidc_v4l2", + .of_match_table = msm_vidc_dt_match, + .pm = &msm_vidc_pm_ops, + }, +}; + +static int __init msm_vidc_init(void) +{ + int rc = 0; + + vidc_driver = kzalloc(sizeof(*vidc_driver), + GFP_KERNEL); + if (!vidc_driver) { + dprintk(VIDC_ERR, + "Failed to allocate memroy for msm_vidc_drv\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&vidc_driver->cores); + mutex_init(&vidc_driver->lock); + vidc_driver->debugfs_root = msm_vidc_debugfs_init_drv(); + if (!vidc_driver->debugfs_root) + dprintk(VIDC_ERR, + "Failed to create debugfs for msm_vidc\n"); + + rc = platform_driver_register(&msm_vidc_driver); + if (rc) { + dprintk(VIDC_ERR, + "Failed to register platform driver\n"); + debugfs_remove_recursive(vidc_driver->debugfs_root); + kfree(vidc_driver); + vidc_driver = NULL; + } + + return rc; +} + +static void __exit msm_vidc_exit(void) +{ + platform_driver_unregister(&msm_vidc_driver); + debugfs_remove_recursive(vidc_driver->debugfs_root); + kfree(vidc_driver); + vidc_driver = NULL; +} + +module_init(msm_vidc_init); +module_exit(msm_vidc_exit); diff --git a/drivers/media/platform/msm/vidc_3x/msm_vdec.c b/drivers/media/platform/msm/vidc_3x/msm_vdec.c new file mode 100644 index 000000000000..e41d13017db4 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vdec.c @@ -0,0 +1,2820 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "msm_vidc_internal.h" +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_dcvs.h" + +#define MSM_VDEC_DVC_NAME "msm_vdec_8974" +#define MIN_NUM_OUTPUT_BUFFERS 4 +#define MIN_NUM_OUTPUT_BUFFERS_VP9 6 +#define MIN_NUM_OUTPUT_BUFFERS_HEVC 6 +#define MIN_NUM_CAPTURE_BUFFERS 6 +#define MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS 1 +#define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME +#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8010 +#define MB_SIZE_IN_PIXEL (16 * 16) +#define MAX_OPERATING_FRAME_RATE (300 << 16) +#define OPERATING_FRAME_RATE_STEP (1 << 16) +#define SLAVE_SIDE_CP_ALIGNMENT 0x100000 +#define MASTER_SIDE_CP_ALIGNMENT 0x1000 + +static const char *const mpeg_video_vidc_divx_format[] = { + "DIVX Format 3", + "DIVX Format 4", + "DIVX Format 5", + "DIVX Format 6", + NULL +}; +static const char *const mpeg_video_stream_format[] = { + "NAL Format Start Codes", + "NAL Format One NAL Per Buffer", + "NAL Format One Byte Length", + "NAL Format Two Byte Length", + "NAL Format Four Byte Length", + NULL +}; +static const char *const mpeg_video_output_order[] = { + "Display Order", + "Decode Order", + NULL +}; +static const char *const mpeg_vidc_video_alloc_mode_type[] = { + "Buffer Allocation Static", + "Buffer Allocation Ring Buffer", + "Buffer Allocation Dynamic Buffer" +}; + +static const char *const perf_level[] = { + "Nominal", + "Performance", + "Turbo" +}; + +static const char *const h263_level[] = { + "1.0", + "2.0", + "3.0", + "4.0", + "4.5", + "5.0", + "6.0", + "7.0", +}; + +static const char *const h263_profile[] = { + "Baseline", + "H320 Coding", + "Backward Compatible", + "ISWV2", + "ISWV3", + "High Compression", + "Internet", + "Interlace", + "High Latency", +}; + +static const char *const vp8_profile_level[] = { + "Unused", + "0.0", + "1.0", + "2.0", + "3.0", +}; + +static const char *const mpeg2_profile[] = { + "Simple", + "Main", + "422", + "Snr Scalable", + "Spatial Scalable", + "High", +}; + +static const char *const mpeg2_level[] = { + "0", + "1", + "2", + "3", +}; +static const char *const mpeg_vidc_video_entropy_mode[] = { + "CAVLC Entropy Mode", + "CABAC Entropy Mode", +}; + +static const char *const mpeg_vidc_video_h264_mvc_layout[] = { + "Frame packing arrangement sequential", + "Frame packing arrangement top-bottom", +}; + +static const char *const mpeg_vidc_video_dpb_color_format[] = { + "DPB Color Format None", + "DPB Color Format UBWC", + "DPB Color Format UBWC TP10", +}; + +static struct msm_vidc_ctrl msm_vdec_ctrls[] = { + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT, + .name = "NAL Format", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES, + .maximum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH, + .default_value = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES) | + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_NAL_PER_BUFFER) | + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_BYTE_LENGTH) | + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_TWO_BYTE_LENGTH) | + (1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH) + ), + .qmenu = mpeg_video_stream_format, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER, + .name = "Output Order", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY, + .maximum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE, + .default_value = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY) | + (1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE) + ), + .qmenu = mpeg_video_output_order, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE, + .name = "Picture Type Decoding", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO, + .name = "Keep Aspect Ratio", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE, + .name = "Deblocker Mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT, + .name = "Divx Format", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4, + .maximum = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6, + .default_value = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4) | + (1 << V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_5) | + (1 << V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6) + ), + .qmenu = mpeg_video_vidc_divx_format, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MB_ERROR_MAP_REPORTING, + .name = "MB Error Map Reporting", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER, + .name = "control", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE, + .name = "Sync Frame Decode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE, + .default_value = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE, + .name = "Secure mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA, + .name = "Extradata Type", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .maximum = V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS, + .default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP) | + (1ULL << V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI) | + (1 << + V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE) | + (1ULL << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS) + ), + .qmenu = mpeg_video_vidc_extradata, + }, + { + .id = V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL, + .name = "Decoder Performance Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL, + .maximum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO, + .default_value = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL) | + (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO)), + .qmenu = perf_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_INPUT, + .name = "Buffer allocation mode for input", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_STATIC, + .maximum = V4L2_MPEG_VIDC_VIDEO_DYNAMIC, + .default_value = V4L2_MPEG_VIDC_VIDEO_STATIC, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_STATIC) | + (1 << V4L2_MPEG_VIDC_VIDEO_RING) | + (1 << V4L2_MPEG_VIDC_VIDEO_DYNAMIC) + ), + .qmenu = mpeg_vidc_video_alloc_mode_type, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_OUTPUT, + .name = "Buffer allocation mode for output", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_STATIC, + .maximum = V4L2_MPEG_VIDC_VIDEO_DYNAMIC, + .default_value = V4L2_MPEG_VIDC_VIDEO_STATIC, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_STATIC) | + (1 << V4L2_MPEG_VIDC_VIDEO_RING) | + (1 << V4L2_MPEG_VIDC_VIDEO_DYNAMIC) + ), + .qmenu = mpeg_vidc_video_alloc_mode_type, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY, + .name = "Video frame assembly", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_FRAME_ASSEMBLY_DISABLE, + .maximum = V4L2_MPEG_VIDC_FRAME_ASSEMBLY_ENABLE, + .default_value = V4L2_MPEG_VIDC_FRAME_ASSEMBLY_DISABLE, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE, + .name = "Video decoder multi stream", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY, + .maximum = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY, + .default_value = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY, + .menu_skip_mask = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + .name = "MPEG4 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, + .maximum = + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, + .default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + .name = "MPEG4 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, + .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .name = "H264 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .name = "H264 Level", + .type = V4L2_CTRL_TYPE_MENU, + .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_5_2, + .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE, + .name = "H263 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE, + .maximum = V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY, + .default_value = V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_H320CODING) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BACKWARDCOMPATIBLE) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV2) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV3) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHCOMPRESSION) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERNET) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERLACE) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY) + ), + .qmenu = h263_profile, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL, + .name = "H263 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0, + .maximum = V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0, + .default_value = V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_4_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_5_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_6_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0) + ), + .qmenu = h263_level, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + .name = "VP8 Profile Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1, + .default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED) | + (1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1) + ), + .qmenu = vp8_profile_level, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE, + .name = "MPEG2 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH, + .default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_422) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SNR_SCALABLE) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SPATIAL_SCALABLE) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH) + ), + .qmenu = mpeg2_profile, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL, + .name = "MPEG2 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0, + .maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3, + .default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3) + ), + .qmenu = mpeg2_level, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SCS_THRESHOLD, + .name = "Video start code search threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = INT_MAX, + .default_value = INT_MAX, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT, + .name = "MVC buffer layout", + .type = V4L2_CTRL_TYPE_MENU, + .maximum = V4L2_MPEG_VIDC_VIDEO_MVC_TOP_BOTTOM, + .default_value = V4L2_MPEG_VIDC_VIDEO_MVC_SEQUENTIAL, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_MVC_SEQUENTIAL) | + (1 << V4L2_MPEG_VIDC_VIDEO_MVC_TOP_BOTTOM) + ), + .qmenu = mpeg_vidc_video_h264_mvc_layout, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR, + .name = "Picture concealed color", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0x0, + .maximum = 0xffffff, + .default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT, + .name = "Buffer size limit", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE_SCALING_THRESHOLD, + .name = "Secure scaling output2 threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2, + .name = "Non-Secure output2", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT, + .name = "Video decoder dpb color format", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE, + .maximum = V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC, + .default_value = V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE) | + (1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC) | + (1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC) + ), + .qmenu = mpeg_vidc_video_dpb_color_format, + }, + { + .id = V4L2_CID_VIDC_QBUF_MODE, + .name = "Allows batching of buffers for power savings", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_VIDC_QBUF_STANDARD, + .maximum = V4L2_VIDC_QBUF_BATCHED, + .default_value = V4L2_VIDC_QBUF_STANDARD, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + .name = "Entropy Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .step = 0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) | + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + ), + .qmenu = mpeg_vidc_video_entropy_mode, + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY, + .name = "Session Priority", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE, + .default_value = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE, + .name = "Set Decoder Operating rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_OPERATING_FRAME_RATE, + .default_value = 0, + .step = OPERATING_FRAME_RATE_STEP, + }, +}; + +#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls) + +static int vdec_hal_to_v4l2(int id, int value); + +static u32 get_frame_size_nv12(int plane, + u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height); +} + +static u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_UBWC, width, height); +} + +static u32 get_frame_size_compressed(int plane, + u32 max_mbs_per_frame, u32 size_per_mb) +{ + return (max_mbs_per_frame * size_per_mb * 3/2)/2; +} + +static u32 get_frame_size_nv12_ubwc_10bit(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_BPP10_UBWC, width, height); +} + +static u32 get_frame_size(struct msm_vidc_inst *inst, + const struct msm_vidc_format *fmt, + int fmt_type, int plane) +{ + u32 frame_size = 0; + + if (fmt_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + frame_size = fmt->get_frame_size(plane, + inst->capability.mbs_per_frame.max, + MB_SIZE_IN_PIXEL); + if (inst->buffer_size_limit && + (inst->buffer_size_limit < frame_size)) { + frame_size = inst->buffer_size_limit; + dprintk(VIDC_DBG, "input buffer size limited to %d\n", + frame_size); + } else { + dprintk(VIDC_DBG, "set input buffer size to %d\n", + frame_size); + } + } else if (fmt_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + frame_size = fmt->get_frame_size(plane, + inst->capability.height.max, + inst->capability.width.max); + dprintk(VIDC_DBG, "set output buffer size to %d\n", + frame_size); + } else { + dprintk(VIDC_WARN, "Wrong format type\n"); + } + return frame_size; +} + +static u32 get_output_frame_size(struct msm_vidc_inst *inst, + const struct msm_vidc_format *fmt, + u32 height, u32 width, int plane) +{ + u32 frame_size = fmt->get_frame_size(plane, + height, width); + + if (inst->flags & VIDC_SECURE) { + u32 alignment = inst->core->resources.slave_side_cp ? + SLAVE_SIDE_CP_ALIGNMENT : MASTER_SIDE_CP_ALIGNMENT; + frame_size = MSM_MEDIA_ALIGN(frame_size, alignment); + } + return frame_size; +} + +static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst, + struct v4l2_ctrl *ctrl) +{ + int rc = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT: + if (inst->fmts[OUTPUT_PORT].fourcc != V4L2_PIX_FMT_H264_MVC) { + dprintk(VIDC_ERR, "Control %#x only valid for MVC\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264_MVC && + ctrl->val != V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) { + dprintk(VIDC_ERR, + "Profile %#x not supported for MVC\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264_MVC && + ctrl->val >= V4L2_MPEG_VIDEO_H264_LEVEL_5_2) { + dprintk(VIDC_ERR, "Level %#x not supported for MVC\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + break; + default: + break; + } + return rc; +} + +struct msm_vidc_format vdec_formats[] = { + { + .name = "YCbCr Semiplanar 4:2:0", + .description = "Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 2, + .get_frame_size = get_frame_size_nv12, + .type = CAPTURE_PORT, + }, + { + .name = "UBWC YCbCr Semiplanar 4:2:0", + .description = "UBWC Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12_UBWC, + .num_planes = 2, + .get_frame_size = get_frame_size_nv12_ubwc, + .type = CAPTURE_PORT, + }, + { + .name = "UBWC YCbCr Semiplanar 4:2:0 10bit", + .description = "UBWC Y/CbCr 4:2:0 10bit", + .fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC, + .num_planes = 2, + .get_frame_size = get_frame_size_nv12_ubwc_10bit, + .type = CAPTURE_PORT, + }, + { + .name = "Mpeg4", + .description = "Mpeg4 compressed format", + .fourcc = V4L2_PIX_FMT_MPEG4, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "Mpeg2", + .description = "Mpeg2 compressed format", + .fourcc = V4L2_PIX_FMT_MPEG2, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "H263", + .description = "H263 compressed format", + .fourcc = V4L2_PIX_FMT_H263, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "VC1", + .description = "VC-1 compressed format", + .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "VC1 SP", + .description = "VC-1 compressed format G", + .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "H264", + .description = "H264 compressed format", + .fourcc = V4L2_PIX_FMT_H264, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "H264_MVC", + .description = "H264_MVC compressed format", + .fourcc = V4L2_PIX_FMT_H264_MVC, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "HEVC", + .description = "HEVC compressed format", + .fourcc = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "HEVC_HYBRID", + .description = "HEVC compressed format", + .fourcc = V4L2_PIX_FMT_HEVC_HYBRID, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "VP8", + .description = "VP8 compressed format", + .fourcc = V4L2_PIX_FMT_VP8, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "VP9", + .description = "VP9 compressed format", + .fourcc = V4L2_PIX_FMT_VP9, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "DIVX 311", + .description = "DIVX 311 compressed format", + .fourcc = V4L2_PIX_FMT_DIVX_311, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + }, + { + .name = "DIVX", + .description = "DIVX 4/5/6 compressed format", + .fourcc = V4L2_PIX_FMT_DIVX, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = OUTPUT_PORT, + } +}; + +int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i) +{ + int rc = 0; + struct buf_queue *q; + + q = msm_comm_get_vb2q(inst, i); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", i); + return -EINVAL; + } + dprintk(VIDC_DBG, "Calling streamon\n"); + mutex_lock(&q->lock); + rc = vb2_streamon(&q->vb2_bufq, i); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_ERR, "streamon failed on port: %d\n", i); + return rc; +} + +int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i) +{ + int rc = 0; + struct buf_queue *q; + + q = msm_comm_get_vb2q(inst, i); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", i); + return -EINVAL; + } + dprintk(VIDC_DBG, "Calling streamoff\n"); + mutex_lock(&q->lock); + rc = vb2_streamoff(&q->vb2_bufq, i); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_ERR, "streamoff failed on port: %d\n", i); + return rc; +} + +int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b) +{ + int rc = 0; + struct vidc_buffer_addr_info buffer_info; + int extra_idx = 0; + int i; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core %pK in bad state, ignoring prepare buf\n", + inst->core); + goto exit; + } + + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (b->length != inst->fmts[CAPTURE_PORT].num_planes) { + dprintk(VIDC_ERR, + "Planes mismatch: needed: %d, allocated: %d\n", + inst->fmts[CAPTURE_PORT].num_planes, + b->length); + rc = -EINVAL; + break; + } + for (i = 0; i < min_t(int, b->length, VIDEO_MAX_PLANES); ++i) { + dprintk(VIDC_DBG, + "prepare plane: %d, device_addr = %#lx, size = %d\n", + i, b->m.planes[i].m.userptr, + b->m.planes[i].length); + } + + buffer_info.buffer_size = b->m.planes[0].length; + buffer_info.buffer_type = msm_comm_get_hal_output_buffer(inst); + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = b->m.planes[0].m.userptr; + + extra_idx = EXTRADATA_IDX(b->length); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES && + b->m.planes[extra_idx].m.userptr) { + buffer_info.extradata_addr = + b->m.planes[extra_idx].m.userptr; + buffer_info.extradata_size = + b->m.planes[extra_idx].length; + dprintk(VIDC_DBG, "extradata: %pa, length = %d\n", + &buffer_info.extradata_addr, + buffer_info.extradata_size); + } else { + buffer_info.extradata_addr = 0; + buffer_info.extradata_size = 0; + } + + 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"); + } + break; + default: + dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type); + break; + } +exit: + return rc; +} + +int msm_vdec_release_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b) +{ + int rc = 0; + struct vidc_buffer_addr_info buffer_info; + struct msm_vidc_core *core; + int extra_idx = 0; + int i; + 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 = inst->core->device; + + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core %pK in bad state, ignoring release output buf\n", + core); + goto exit; + } + + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (b->length != inst->fmts[CAPTURE_PORT].num_planes) { + dprintk(VIDC_ERR, + "Planes mismatch: needed: %d, to release: %d\n", + inst->fmts[CAPTURE_PORT].num_planes, b->length); + rc = -EINVAL; + break; + } + + for (i = 0; i < b->length; ++i) { + dprintk(VIDC_DBG, + "Release plane: %d device_addr = %#lx, size = %d\n", + i, b->m.planes[i].m.userptr, + b->m.planes[i].length); + } + + buffer_info.buffer_size = b->m.planes[0].length; + buffer_info.buffer_type = msm_comm_get_hal_output_buffer(inst); + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = b->m.planes[0].m.userptr; + buffer_info.response_required = false; + + extra_idx = EXTRADATA_IDX(b->length); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES + && b->m.planes[extra_idx].m.userptr) + buffer_info.extradata_addr = + b->m.planes[extra_idx].m.userptr; + else + buffer_info.extradata_addr = 0; + + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (rc) + dprintk(VIDC_ERR, + "vidc_hal_session_release_buffers failed\n"); + break; + default: + dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type); + break; + } +exit: + return rc; +} + +int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + struct buf_queue *q = NULL; + int rc = 0; + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, "Failed to find buffer queue for type = %d\n" + , b->type); + return -EINVAL; + } + + mutex_lock(&q->lock); + rc = vb2_qbuf(&q->vb2_bufq, b); + mutex_unlock(&q->lock); + + if (rc) + dprintk(VIDC_ERR, "Failed to qbuf, %d\n", rc); + return rc; +} + +int msm_vdec_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + struct buf_queue *q = NULL; + int rc = 0; + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, "Failed to find buffer queue for type = %d\n" + , b->type); + return -EINVAL; + } + mutex_lock(&q->lock); + rc = vb2_dqbuf(&q->vb2_bufq, b, true); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc); + return rc; +} + +int msm_vdec_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b) +{ + struct buf_queue *q = NULL; + int rc = 0; + + if (!inst || !b) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, buffer = %pK\n", inst, b); + return -EINVAL; + } + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, "Failed to find buffer queue for type = %d\n" + , b->type); + return -EINVAL; + } + + mutex_lock(&q->lock); + rc = vb2_reqbufs(&q->vb2_bufq, b); + mutex_unlock(&q->lock); + + if (rc) + dprintk(VIDC_DBG, "Failed to get reqbufs, %d\n", rc); + return rc; +} + +int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + const struct msm_vidc_format *fmt = NULL; + struct hfi_device *hdev; + int rc = 0, i = 0, stride = 0, scanlines = 0, color_format = 0; + unsigned int *plane_sizes = NULL, extra_idx = 0; + + if (!inst || !f || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, format = %pK\n", inst, f); + return -EINVAL; + } + + hdev = inst->core->device; + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fmt = &inst->fmts[CAPTURE_PORT]; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + fmt = &inst->fmts[OUTPUT_PORT]; + else + return -ENOTSUPP; + + f->fmt.pix_mp.pixelformat = fmt->fourcc; + f->fmt.pix_mp.num_planes = fmt->num_planes; + if (inst->in_reconfig) { + inst->prop.height[OUTPUT_PORT] = inst->reconfig_height; + inst->prop.width[OUTPUT_PORT] = inst->reconfig_width; + + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: unsupported session\n", __func__); + goto exit; + } + } + + f->fmt.pix_mp.height = inst->prop.height[CAPTURE_PORT]; + f->fmt.pix_mp.width = inst->prop.width[CAPTURE_PORT]; + stride = inst->prop.width[CAPTURE_PORT]; + scanlines = inst->prop.height[CAPTURE_PORT]; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + plane_sizes = &inst->bufq[OUTPUT_PORT].plane_sizes[0]; + for (i = 0; i < fmt->num_planes; ++i) { + if (!plane_sizes[i]) { + f->fmt.pix_mp.plane_fmt[i].sizeimage = + get_frame_size(inst, fmt, f->type, i); + plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } else + f->fmt.pix_mp.plane_fmt[i].sizeimage = + plane_sizes[i]; + } + f->fmt.pix_mp.height = inst->prop.height[OUTPUT_PORT]; + f->fmt.pix_mp.width = inst->prop.width[OUTPUT_PORT]; + f->fmt.pix_mp.plane_fmt[0].bytesperline = + (__u16)inst->prop.width[OUTPUT_PORT]; + f->fmt.pix_mp.plane_fmt[0].reserved[0] = + (__u16)inst->prop.height[OUTPUT_PORT]; + } else { + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + color_format = COLOR_FMT_NV12; + break; + case V4L2_PIX_FMT_NV12_UBWC: + color_format = COLOR_FMT_NV12_UBWC; + break; + case V4L2_PIX_FMT_NV12_TP10_UBWC: + color_format = COLOR_FMT_NV12_BPP10_UBWC; + break; + default: + dprintk(VIDC_WARN, "Color format not recognized\n"); + rc = -ENOTSUPP; + goto exit; + } + + stride = VENUS_Y_STRIDE(color_format, + inst->prop.width[CAPTURE_PORT]); + scanlines = VENUS_Y_SCANLINES(color_format, + inst->prop.height[CAPTURE_PORT]); + + f->fmt.pix_mp.plane_fmt[0].sizeimage = + get_output_frame_size(inst, fmt, + f->fmt.pix_mp.height, f->fmt.pix_mp.width, 0); + + extra_idx = EXTRADATA_IDX(fmt->num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + VENUS_EXTRADATA_SIZE( + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); + } + + for (i = 0; i < fmt->num_planes; ++i) + inst->bufq[CAPTURE_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + + f->fmt.pix_mp.height = inst->prop.height[CAPTURE_PORT]; + f->fmt.pix_mp.width = inst->prop.width[CAPTURE_PORT]; + f->fmt.pix_mp.plane_fmt[0].bytesperline = + (__u16)stride; + f->fmt.pix_mp.plane_fmt[0].reserved[0] = + (__u16)scanlines; + } + +exit: + return rc; +} + +static int set_default_properties(struct msm_vidc_inst *inst) +{ + struct hfi_device *hdev; + struct v4l2_control ctrl = {0}; + enum hal_default_properties defaults; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s - invalid params\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + defaults = call_hfi_op(hdev, get_default_properties, + hdev->hfi_device_data); + + if (defaults & HAL_VIDEO_DYNAMIC_BUF_MODE) { + dprintk(VIDC_DBG, "Enable dynamic buffer mode\n"); + ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_OUTPUT; + ctrl.value = V4L2_MPEG_VIDC_VIDEO_DYNAMIC; + rc = msm_comm_s_ctrl(inst, &ctrl); + if (rc) + dprintk(VIDC_ERR, + "Failed to enable dynamic buffer mode by default: %d\n", + rc); + } + + return rc; +} + +int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + struct msm_vidc_format *fmt = NULL; + struct hal_frame_size frame_sz; + unsigned int extra_idx = 0; + int rc = 0; + int ret = 0; + int i; + int max_input_size = 0; + + if (!inst || !inst->core || !f) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, + ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat, + CAPTURE_PORT); + if (!fmt || fmt->type != CAPTURE_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto err_invalid_fmt; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width; + inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height; + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + msm_comm_set_color_format(inst, HAL_BUFFER_OUTPUT2, + f->fmt.pix_mp.pixelformat); + dprintk(VIDC_DBG, + "buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + ret = msm_comm_try_set_prop(inst, + HAL_PARAM_FRAME_SIZE, &frame_sz); + } else { + msm_comm_set_color_format(inst, HAL_BUFFER_OUTPUT, + f->fmt.pix_mp.pixelformat); + } + + f->fmt.pix_mp.plane_fmt[0].sizeimage = + get_output_frame_size(inst, &inst->fmts[fmt->type], + f->fmt.pix_mp.height, f->fmt.pix_mp.width, 0); + + extra_idx = EXTRADATA_IDX(inst->fmts[fmt->type].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + VENUS_EXTRADATA_SIZE( + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); + } + + f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes; + for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) { + inst->bufq[CAPTURE_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; + inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; + + fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats, + ARRAY_SIZE(vdec_formats), + f->fmt.pix_mp.pixelformat, + OUTPUT_PORT); + if (!fmt || fmt->type != OUTPUT_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on OUTPUT port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto err_invalid_fmt; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to initialize instance\n"); + goto err_invalid_fmt; + } + + if (!(get_hal_codec(inst->fmts[fmt->type].fourcc) & + inst->core->dec_codec_supported)) { + dprintk(VIDC_ERR, + "Codec(%#x) is not present in the supported codecs list(%#x)\n", + get_hal_codec(inst->fmts[fmt->type].fourcc), + inst->core->dec_codec_supported); + rc = -EINVAL; + goto err_invalid_fmt; + } + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + goto err_invalid_fmt; + } + + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto err_invalid_fmt; + } + + frame_sz.buffer_type = HAL_BUFFER_INPUT; + frame_sz.width = inst->prop.width[OUTPUT_PORT]; + frame_sz.height = inst->prop.height[OUTPUT_PORT]; + dprintk(VIDC_DBG, + "buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); + + max_input_size = get_frame_size(inst, + &inst->fmts[fmt->type], f->type, 0); + if (f->fmt.pix_mp.plane_fmt[0].sizeimage > max_input_size || + !f->fmt.pix_mp.plane_fmt[0].sizeimage) { + f->fmt.pix_mp.plane_fmt[0].sizeimage = max_input_size; + } + + f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes; + for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) { + inst->bufq[OUTPUT_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + set_default_properties(inst); + } +err_invalid_fmt: + return rc; +} + +int msm_vdec_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap) +{ + if (!inst || !cap) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, cap = %pK\n", inst, cap); + return -EINVAL; + } + strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->card, MSM_VDEC_DVC_NAME, sizeof(cap->card)); + cap->bus_info[0] = 0; + //cap->version = MSM_VIDC_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_STREAMING; + memset(cap->reserved, 0, sizeof(cap->reserved)); + return 0; +} + +int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f) +{ + const struct msm_vidc_format *fmt = NULL; + int rc = 0; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, f = %pK\n", inst, f); + return -EINVAL; + } + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(vdec_formats, + ARRAY_SIZE(vdec_formats), f->index, CAPTURE_PORT); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(vdec_formats, + ARRAY_SIZE(vdec_formats), f->index, OUTPUT_PORT); + f->flags = V4L2_FMT_FLAG_COMPRESSED; + } + + memset(f->reserved, 0, sizeof(f->reserved)); + if (fmt) { + strlcpy(f->description, fmt->description, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + } else { + dprintk(VIDC_DBG, "No more formats found\n"); + rc = -EINVAL; + } + return rc; +} + +static int set_actual_buffer_count(struct msm_vidc_inst *inst, + int count, enum hal_buffer type) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_buffer_count_actual buf_count; + + hdev = inst->core->device; + + buf_count.buffer_type = type; + buf_count.buffer_count_actual = count; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_COUNT_ACTUAL, &buf_count); + if (rc) + dprintk(VIDC_ERR, + "Failed to set actual buffer count %d for buffer type %d\n", + count, type); + return rc; +} + +static int msm_vdec_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + int i, rc = 0; + struct msm_vidc_inst *inst; + struct hal_buffer_requirements *bufreq; + int extra_idx = 0; + int min_buff_count = 0; + + if (!q || !num_buffers || !num_planes + || !sizes || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK, %pK, %pK\n", + q, num_buffers, num_planes); + return -EINVAL; + } + inst = q->drv_priv; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed : Buffer requirements\n", __func__); + goto exit; + } + + + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *num_planes = inst->fmts[OUTPUT_PORT].num_planes; + if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS || + *num_buffers > MAX_NUM_OUTPUT_BUFFERS) + *num_buffers = MIN_NUM_OUTPUT_BUFFERS; + /* + * Increase input buffer count to 6 as for some + * vp9 clips which have superframes with more + * than 4 subframes requires more than 4 + * reference frames to decode. + */ + if (inst->fmts[OUTPUT_PORT].fourcc == + V4L2_PIX_FMT_VP9 && + *num_buffers < MIN_NUM_OUTPUT_BUFFERS_VP9) + *num_buffers = MIN_NUM_OUTPUT_BUFFERS_VP9; + else if (inst->fmts[OUTPUT_PORT].fourcc == + V4L2_PIX_FMT_HEVC && + *num_buffers < MIN_NUM_OUTPUT_BUFFERS_HEVC) + *num_buffers = MIN_NUM_OUTPUT_BUFFERS_HEVC; + + for (i = 0; i < *num_planes; i++) { + sizes[i] = get_frame_size(inst, + &inst->fmts[OUTPUT_PORT], q->type, i); + } + rc = set_actual_buffer_count(inst, *num_buffers, + HAL_BUFFER_INPUT); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + dprintk(VIDC_DBG, "Getting bufreqs on capture plane\n"); + *num_planes = inst->fmts[CAPTURE_PORT].num_planes; + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + break; + } + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get buffer requirements: %d\n", rc); + break; + } + + bufreq = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + if (!bufreq) { + dprintk(VIDC_ERR, + "No buffer requirement for buffer type %x\n", + HAL_BUFFER_OUTPUT); + rc = -EINVAL; + break; + } + + /* Pretend as if FW itself is asking for + * additional buffers. + * *num_buffers += MSM_VIDC_ADDITIONAL_BUFS_FOR_DCVS + * is wrong since it will end up increasing the count + * on every call to reqbufs if *num_bufs is larger + * than min requirement. + */ + *num_buffers = max(*num_buffers, bufreq->buffer_count_min + + msm_dcvs_get_extra_buff_count(inst)); + + min_buff_count = (!!(inst->flags & VIDC_THUMBNAIL)) ? + MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS : + MIN_NUM_CAPTURE_BUFFERS; + + *num_buffers = clamp_val(*num_buffers, + min_buff_count, VB2_MAX_FRAME); + + dprintk(VIDC_DBG, "Set actual output buffer count: %d\n", + *num_buffers); + rc = set_actual_buffer_count(inst, *num_buffers, + msm_comm_get_hal_output_buffer(inst)); + if (rc) + break; + + if (*num_buffers != bufreq->buffer_count_actual) { + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_WARN, + "Failed to get buf req, %d\n", rc); + break; + } + } + dprintk(VIDC_DBG, "count = %d, size = %d, alignment = %d\n", + inst->buff_req.buffer[1].buffer_count_actual, + inst->buff_req.buffer[1].buffer_size, + inst->buff_req.buffer[1].buffer_alignment); + sizes[0] = inst->bufq[CAPTURE_PORT].plane_sizes[0]; + + + /* Set actual buffer count to firmware for DPB buffers. + * Firmware mandates setting of minimum buffer size + * and actual buffer count for both OUTPUT and OUTPUT2. + * Hence we are setting back the same buffer size + * information back to firmware. + */ + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + if (!bufreq) { + rc = -EINVAL; + break; + } + + rc = set_actual_buffer_count(inst, + bufreq->buffer_count_actual, + HAL_BUFFER_OUTPUT); + if (rc) + break; + } + + extra_idx = + EXTRADATA_IDX(inst->fmts[CAPTURE_PORT].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + sizes[extra_idx] = + VENUS_EXTRADATA_SIZE( + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); + } + break; + default: + dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type); + rc = -EINVAL; + break; + } +exit: + return rc; +} + +static inline int set_max_internal_buffers_size(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct { + enum hal_buffer type; + struct hal_buffer_requirements *req; + size_t size; + } internal_buffers[] = { + { HAL_BUFFER_INTERNAL_SCRATCH, NULL, 0}, + { HAL_BUFFER_INTERNAL_SCRATCH_1, NULL, 0}, + { HAL_BUFFER_INTERNAL_SCRATCH_2, NULL, 0}, + { HAL_BUFFER_INTERNAL_PERSIST, NULL, 0}, + { HAL_BUFFER_INTERNAL_PERSIST_1, NULL, 0}, + }; + + struct hal_frame_size frame_sz; + int i; + + frame_sz.buffer_type = HAL_BUFFER_INPUT; + frame_sz.width = inst->capability.width.max; + frame_sz.height = + (inst->capability.mbs_per_frame.max * 256) / + inst->capability.width.max; + + dprintk(VIDC_DBG, + "Max buffer reqs, buffer type = %d width = %d, height = %d, max_mbs_per_frame = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height, inst->capability.mbs_per_frame.max); + + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed to get max buf req, %d\n", __func__, rc); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(internal_buffers); i++) { + internal_buffers[i].req = + get_buff_req_buffer(inst, internal_buffers[i].type); + internal_buffers[i].size = internal_buffers[i].req ? + internal_buffers[i].req->buffer_size : 0; + } + + frame_sz.buffer_type = HAL_BUFFER_INPUT; + frame_sz.width = inst->prop.width[OUTPUT_PORT]; + frame_sz.height = inst->prop.height[OUTPUT_PORT]; + + msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed to get back old buf req, %d\n", + __func__, rc); + return rc; + } + + dprintk(VIDC_DBG, + "Old buffer reqs, buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + + for (i = 0; i < ARRAY_SIZE(internal_buffers); i++) { + if (internal_buffers[i].req) { + internal_buffers[i].req->buffer_size = + internal_buffers[i].size; + dprintk(VIDC_DBG, + "Changing buffer type : %d size to : %zd\n", + internal_buffers[i].type, + internal_buffers[i].size); + } + } + return 0; +} + +static inline int start_streaming(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct hfi_device *hdev; + bool slave_side_cp = inst->core->resources.slave_side_cp; + struct hal_buffer_size_minimum b; + unsigned int buffer_size; + struct msm_vidc_format *fmt = NULL; + + fmt = &inst->fmts[CAPTURE_PORT]; + buffer_size = get_output_frame_size(inst, fmt, + inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT], 0); + hdev = inst->core->device; + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + rc = msm_vidc_check_scaling_supported(inst); + b.buffer_type = HAL_BUFFER_OUTPUT2; + } else { + b.buffer_type = HAL_BUFFER_OUTPUT; + } + + b.buffer_size = buffer_size; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, + &b); + if (rc) { + dprintk(VIDC_ERR, "H/w scaling is not in valid range\n"); + return -EINVAL; + } + if ((inst->flags & VIDC_SECURE) && !inst->in_reconfig && + !slave_side_cp) { + rc = set_max_internal_buffers_size(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set max scratch buffer size: %d\n", + rc); + goto fail_start; + } + } + rc = msm_comm_set_scratch_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set scratch buffers: %d\n", rc); + goto fail_start; + } + rc = msm_comm_set_persist_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set persist buffers: %d\n", rc); + goto fail_start; + } + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + rc = msm_comm_set_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set output buffers: %d\n", rc); + goto fail_start; + } + } + + /* + * For seq_changed_insufficient, driver should set session_continue + * to firmware after the following sequence + * - driver raises insufficient event to v4l2 client + * - all output buffers have been flushed and freed + * - v4l2 client queries buffer requirements and splits/combines OPB-DPB + * - v4l2 client sets new set of buffers to firmware + * - v4l2 client issues CONTINUE to firmware to resume decoding of + * submitted ETBs. + */ + if (inst->in_reconfig) { + dprintk(VIDC_DBG, "send session_continue after reconfig\n"); + rc = call_hfi_op(hdev, session_continue, + (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "%s - failed to send session_continue\n", + __func__); + goto fail_start; + } + } + inst->in_reconfig = false; + + msm_comm_scale_clocks_and_bus(inst); + + rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + goto fail_start; + } + msm_dcvs_init_load(inst); + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + rc = msm_comm_queue_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue output buffers: %d\n", rc); + goto fail_start; + } + } + +fail_start: + return rc; +} + +static inline int stop_streaming(struct msm_vidc_inst *inst) +{ + int rc = 0; + + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + return rc; +} + + +static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct msm_vidc_inst *inst; + int rc = 0; + struct hfi_device *hdev; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); + return -EINVAL; + } + inst = q->drv_priv; + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n", + q->type, inst); + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming) + rc = start_streaming(inst); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming) + rc = start_streaming(inst); + break; + default: + dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type); + rc = -EINVAL; + goto stream_start_failed; + } + if (rc) { + dprintk(VIDC_ERR, + "Streamon failed on: %d capability for inst: %pK\n", + q->type, inst); + goto stream_start_failed; + } + + rc = msm_comm_qbuf(inst, NULL); + if (rc) { + dprintk(VIDC_ERR, + "Failed to commit buffers queued before STREAM_ON to hardware: %d\n", + rc); + goto stream_start_failed; + } + +stream_start_failed: + return rc; +} + +static void msm_vdec_stop_streaming(struct vb2_queue *q) +{ + struct msm_vidc_inst *inst; + int rc = 0; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); + return; + } + + inst = q->drv_priv; + dprintk(VIDC_DBG, "Streamoff called on: %d capability\n", q->type); + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (!inst->bufq[CAPTURE_PORT].vb2_bufq.streaming) + rc = stop_streaming(inst); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (!inst->bufq[OUTPUT_PORT].vb2_bufq.streaming) + rc = stop_streaming(inst); + break; + default: + dprintk(VIDC_ERR, + "Q-type is not supported: %d\n", q->type); + rc = -EINVAL; + break; + } + + msm_comm_scale_clocks_and_bus(inst); + + if (rc) + dprintk(VIDC_ERR, + "Failed to move inst: %pK, cap = %d to state: %d\n", + inst, q->type, MSM_VIDC_RELEASE_RESOURCES_DONE); +} + +static void msm_vdec_buf_queue(struct vb2_buffer *vb) +{ + int rc = msm_comm_qbuf(vb2_get_drv_priv(vb->vb2_queue), vb); + + if (rc) + dprintk(VIDC_ERR, "Failed to queue buffer: %d\n", rc); +} + +static const struct vb2_ops msm_vdec_vb2q_ops = { + .queue_setup = msm_vdec_queue_setup, + .start_streaming = msm_vdec_start_streaming, + .buf_queue = msm_vdec_buf_queue, + .stop_streaming = msm_vdec_stop_streaming, +}; + +const struct vb2_ops *msm_vdec_get_vb2q_ops(void) +{ + return &msm_vdec_vb2q_ops; +} + +int msm_vdec_inst_init(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid input = %pK\n", inst); + return -EINVAL; + } + inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT; + inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH; + inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT; + inst->prop.width[OUTPUT_PORT] = DEFAULT_WIDTH; + inst->capability.height.min = MIN_SUPPORTED_HEIGHT; + inst->capability.height.max = DEFAULT_HEIGHT; + inst->capability.width.min = MIN_SUPPORTED_WIDTH; + inst->capability.width.max = DEFAULT_WIDTH; + inst->capability.alloc_mode_in = HAL_BUFFER_MODE_STATIC; + inst->capability.alloc_mode_out = HAL_BUFFER_MODE_STATIC; + inst->capability.secure_output2_threshold.min = 0; + inst->capability.secure_output2_threshold.max = 0; + inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC; + inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC; + inst->prop.fps = DEFAULT_FPS; + inst->prop.operating_rate = 0; + memcpy(&inst->fmts[OUTPUT_PORT], &vdec_formats[2], + sizeof(struct msm_vidc_format)); + memcpy(&inst->fmts[CAPTURE_PORT], &vdec_formats[0], + sizeof(struct msm_vidc_format)); + return rc; +} + +static inline enum buffer_mode_type get_buf_type(int val) +{ + switch (val) { + case V4L2_MPEG_VIDC_VIDEO_STATIC: + return HAL_BUFFER_MODE_STATIC; + case V4L2_MPEG_VIDC_VIDEO_RING: + return HAL_BUFFER_MODE_RING; + case V4L2_MPEG_VIDC_VIDEO_DYNAMIC: + return HAL_BUFFER_MODE_DYNAMIC; + default: + dprintk(VIDC_ERR, "%s: invalid buf type: %d\n", __func__, val); + } + return 0; +} + +static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0; + struct hfi_device *hdev; + union hal_get_property hprop; + + if (!inst || !inst->core || !inst->core->device || !ctrl) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + /* + * HACK: unlock the control prior to querying the hardware. Otherwise + * lower level code that attempts to do g_ctrl() will end up deadlocking + * us. + */ + v4l2_ctrl_unlock(ctrl); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE: + rc = msm_comm_try_get_prop(inst, + HAL_PARAM_PROFILE_LEVEL_CURRENT, &hprop); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed getting profile: %d", + __func__, rc); + break; + } + ctrl->val = vdec_hal_to_v4l2(ctrl->id, + hprop.profile_level.profile); + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL: + rc = msm_comm_try_get_prop(inst, + HAL_PARAM_PROFILE_LEVEL_CURRENT, &hprop); + if (rc) { + dprintk(VIDC_ERR, "%s: Failed getting level: %d", + __func__, rc); + break; + } + + ctrl->val = vdec_hal_to_v4l2(ctrl->id, + hprop.profile_level.level); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE_SCALING_THRESHOLD: + dprintk(VIDC_DBG, "Secure scaling threshold is: %d", + inst->capability.secure_output2_threshold.max); + ctrl->val = inst->capability.secure_output2_threshold.max; + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264) { + rc = msm_comm_try_get_prop(inst, + HAL_CONFIG_VDEC_ENTROPY, &hprop); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed getting entropy type: %d", + __func__, rc); + break; + } + switch (hprop.h264_entropy) { + case HAL_H264_ENTROPY_CAVLC: + ctrl->val = + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC; + break; + case HAL_H264_ENTROPY_CABAC: + ctrl->val = + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC; + break; + case HAL_UNUSED_ENTROPY: + rc = -ENOTSUPP; + break; + } + } + break; + default: + /* Other controls aren't really volatile, shouldn't need to + * modify ctrl->value + */ + break; + } + v4l2_ctrl_lock(ctrl); + + return rc; +} + +static int vdec_v4l2_to_hal(int id, int value) +{ + switch (id) { + /* H264 */ + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return HAL_H264_PROFILE_BASELINE; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + return HAL_H264_PROFILE_CONSTRAINED_BASE; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH: + return HAL_H264_PROFILE_CONSTRAINED_HIGH; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return HAL_H264_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + return HAL_H264_PROFILE_EXTENDED; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return HAL_H264_PROFILE_HIGH; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10: + return HAL_H264_PROFILE_HIGH10; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422: + return HAL_H264_PROFILE_HIGH422; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE: + return HAL_H264_PROFILE_HIGH444; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return HAL_H264_LEVEL_1; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return HAL_H264_LEVEL_1b; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return HAL_H264_LEVEL_11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return HAL_H264_LEVEL_12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return HAL_H264_LEVEL_13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return HAL_H264_LEVEL_2; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return HAL_H264_LEVEL_21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return HAL_H264_LEVEL_22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return HAL_H264_LEVEL_3; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return HAL_H264_LEVEL_31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return HAL_H264_LEVEL_32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return HAL_H264_LEVEL_4; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return HAL_H264_LEVEL_41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return HAL_H264_LEVEL_42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return HAL_H264_LEVEL_5; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return HAL_H264_LEVEL_51; + default: + goto unknown_value; + } + } +unknown_value: + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); + return -EINVAL; +} + +static int vdec_hal_to_v4l2(int id, int value) +{ + switch (id) { + /* H264 */ + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case HAL_H264_PROFILE_BASELINE: + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + case HAL_H264_PROFILE_CONSTRAINED_BASE: + return + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE; + case HAL_H264_PROFILE_MAIN: + return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + case HAL_H264_PROFILE_EXTENDED: + return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; + case HAL_H264_PROFILE_HIGH: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + case HAL_H264_PROFILE_HIGH10: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10; + case HAL_H264_PROFILE_HIGH422: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422; + case HAL_H264_PROFILE_HIGH444: + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case HAL_H264_LEVEL_1: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + case HAL_H264_LEVEL_1b: + return V4L2_MPEG_VIDEO_H264_LEVEL_1B; + case HAL_H264_LEVEL_11: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + case HAL_H264_LEVEL_12: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + case HAL_H264_LEVEL_13: + return V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + case HAL_H264_LEVEL_2: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + case HAL_H264_LEVEL_21: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + case HAL_H264_LEVEL_22: + return V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + case HAL_H264_LEVEL_3: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + case HAL_H264_LEVEL_31: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + case HAL_H264_LEVEL_32: + return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + case HAL_H264_LEVEL_4: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + case HAL_H264_LEVEL_41: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case HAL_H264_LEVEL_42: + return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case HAL_H264_LEVEL_5: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case HAL_H264_LEVEL_51: + return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL: + /* + * Extremely dirty hack: we haven't implemented g_ctrl of + * any of these controls and have no intention of doing + * so in the near future. So just return 0 so that we + * don't see the annoying "Unknown control" errors at the + * bottom of this function. + */ + return 0; + } + +unknown_value: + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); + return -EINVAL; +} + +static struct v4l2_ctrl *get_ctrl_from_cluster(int id, + struct v4l2_ctrl **cluster, int ncontrols) +{ + int c; + + for (c = 0; c < ncontrols; ++c) + if (cluster[c]->id == id) + return cluster[c]; + return NULL; +} + +static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0; + struct hal_nal_stream_format_supported stream_format; + struct hal_enable_picture enable_picture; + struct hal_enable hal_property; + enum hal_property property_id = 0; + u32 property_val = 0; + void *pdata = NULL; + struct hfi_device *hdev; + struct hal_extradata_enable extra; + struct hal_buffer_alloc_mode alloc_mode; + struct hal_multi_stream multi_stream; + struct hal_scs_threshold scs_threshold; + struct hal_mvc_buffer_layout layout; + struct v4l2_ctrl *temp_ctrl = NULL; + struct hal_profile_level profile_level; + struct hal_frame_size frame_sz; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + rc = is_ctrl_valid_for_codec(inst, ctrl); + if (rc) + return rc; + + /* Small helper macro for quickly getting a control and err checking */ +#define TRY_GET_CTRL(__ctrl_id) ({ \ + struct v4l2_ctrl *__temp; \ + __temp = get_ctrl_from_cluster( \ + __ctrl_id, \ + ctrl->cluster, ctrl->ncontrols); \ + if (!__temp) { \ + dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \ + #__ctrl_id, __ctrl_id); \ + /* Clusters are hardcoded, if we can't find */ \ + /* something then things are massively screwed up */ \ + WARN_ON(VIDC_DBG_WARN_ENABLE); \ + } \ + __temp; \ + }) + + v4l2_ctrl_unlock(ctrl); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT: + property_id = HAL_PARAM_NAL_STREAM_FORMAT_SELECT; + stream_format.nal_stream_format_supported = BIT(ctrl->val); + pdata = &stream_format; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER: + property_id = HAL_PARAM_VDEC_OUTPUT_ORDER; + property_val = ctrl->val; + pdata = &property_val; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE: + property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE; + if (ctrl->val == + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON) + enable_picture.picture_type = HAL_PICTURE_I; + else + enable_picture.picture_type = HAL_PICTURE_I | + HAL_PICTURE_P | HAL_PICTURE_B | + HAL_PICTURE_IDR; + pdata = &enable_picture; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO: + property_id = HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO; + hal_property.enable = ctrl->val; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE: + property_id = HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + hal_property.enable = ctrl->val; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT: + property_id = HAL_PARAM_DIVX_FORMAT; + property_val = ctrl->val; + pdata = &property_val; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MB_ERROR_MAP_REPORTING: + property_id = HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING; + hal_property.enable = ctrl->val; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER: + property_id = HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER; + hal_property.enable = ctrl->val; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE: + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE: + inst->flags &= ~VIDC_THUMBNAIL; + break; + case V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE: + inst->flags |= VIDC_THUMBNAIL; + break; + } + + property_id = HAL_PARAM_VDEC_SYNC_FRAME_DECODE; + hal_property.enable = ctrl->val; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE: + inst->flags |= VIDC_SECURE; + dprintk(VIDC_DBG, "Setting secure mode to: %d\n", + !!(inst->flags & VIDC_SECURE)); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA: + property_id = HAL_PARAM_INDEX_EXTRADATA; + extra.index = msm_comm_get_hal_extradata_index(ctrl->val); + extra.enable = 1; + pdata = &extra; + break; + case V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL: + switch (ctrl->val) { + case V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL: + inst->flags &= ~VIDC_TURBO; + break; + case V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO: + inst->flags |= VIDC_TURBO; + break; + default: + dprintk(VIDC_ERR, "Perf mode %x not supported\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + msm_comm_scale_clocks_and_bus(inst); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_INPUT: + if (ctrl->val == V4L2_MPEG_VIDC_VIDEO_DYNAMIC) { + rc = -ENOTSUPP; + break; + } + property_id = HAL_PARAM_BUFFER_ALLOC_MODE; + alloc_mode.buffer_mode = get_buf_type(ctrl->val); + alloc_mode.buffer_type = HAL_BUFFER_INPUT; + inst->buffer_mode_set[OUTPUT_PORT] = alloc_mode.buffer_mode; + pdata = &alloc_mode; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY: + property_id = HAL_PARAM_VDEC_FRAME_ASSEMBLY; + hal_property.enable = ctrl->val; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_OUTPUT: + property_id = HAL_PARAM_BUFFER_ALLOC_MODE; + alloc_mode.buffer_mode = get_buf_type(ctrl->val); + + if (!(alloc_mode.buffer_mode & + inst->capability.alloc_mode_out)) { + dprintk(VIDC_WARN, + "buffer mode[%d] not supported for capture port[0x%x]\n", + ctrl->val, inst->capability.alloc_mode_out); + rc = -ENOTSUPP; + break; + } + + alloc_mode.buffer_type = HAL_BUFFER_OUTPUT; + pdata = &alloc_mode; + inst->buffer_mode_set[CAPTURE_PORT] = alloc_mode.buffer_mode; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE: + if (ctrl->val && !(inst->capability.pixelprocess_capabilities & + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)) { + dprintk(VIDC_ERR, "Downscaling not supported: %#x\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + switch (ctrl->val) { + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY: + multi_stream.buffer_type = HAL_BUFFER_OUTPUT; + multi_stream.enable = true; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed : Enabling OUTPUT port : %d\n", + rc); + break; + } + multi_stream.buffer_type = HAL_BUFFER_OUTPUT2; + multi_stream.enable = false; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) + dprintk(VIDC_ERR, + "Failed:Disabling OUTPUT2 port : %d\n", + rc); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY: + multi_stream.buffer_type = HAL_BUFFER_OUTPUT2; + multi_stream.enable = true; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed :Enabling OUTPUT2 port : %d\n", + rc); + break; + } + multi_stream.buffer_type = HAL_BUFFER_OUTPUT; + multi_stream.enable = false; + pdata = &multi_stream; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_VDEC_MULTI_STREAM, + pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed disabling OUTPUT port : %d\n", + rc); + break; + } + + frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + pdata = &frame_sz; + dprintk(VIDC_DBG, + "buffer type = %d width = %d, height = %d\n", + frame_sz.buffer_type, frame_sz.width, + frame_sz.height); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, pdata); + if (rc) + dprintk(VIDC_ERR, + "Failed setting OUTPUT2 size : %d\n", + rc); + + alloc_mode.buffer_mode = + inst->buffer_mode_set[CAPTURE_PORT]; + alloc_mode.buffer_type = HAL_BUFFER_OUTPUT2; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_ALLOC_MODE, + &alloc_mode); + if (rc) + dprintk(VIDC_ERR, + "Failed to set alloc_mode on OUTPUT2: %d\n", + rc); + break; + default: + dprintk(VIDC_ERR, + "Failed : Unsupported multi stream setting\n"); + rc = -ENOTSUPP; + break; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SCS_THRESHOLD: + property_id = HAL_PARAM_VDEC_SCS_THRESHOLD; + scs_threshold.threshold_value = ctrl->val; + pdata = &scs_threshold; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT: + property_id = HAL_PARAM_MVC_BUFFER_LAYOUT; + layout.layout_type = msm_comm_get_hal_buffer_layout(ctrl->val); + layout.bright_view_first = 0; + layout.ngap = 0; + pdata = &layout; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR: + property_id = HAL_PARAM_VDEC_CONCEAL_COLOR; + property_val = ctrl->val; + pdata = &property_val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_LEVEL); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control\n"); + return -EINVAL; + } + property_id = + HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = vdec_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = vdec_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_PROFILE); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control\n"); + return -EINVAL; + } + property_id = + HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = vdec_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = vdec_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT: + dprintk(VIDC_DBG, + "Limiting input buffer size from %u to %u\n", + inst->buffer_size_limit, ctrl->val); + inst->buffer_size_limit = ctrl->val; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2: + property_id = HAL_PARAM_VDEC_NON_SECURE_OUTPUT2; + hal_property.enable = ctrl->val; + dprintk(VIDC_DBG, "%s non_secure output2\n", + ctrl->val ? "Enabling" : "Disabling"); + pdata = &hal_property; + break; + case V4L2_CID_VIDC_QBUF_MODE: + property_id = HAL_PARAM_SYNC_BASED_INTERRUPT; + hal_property.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED; + pdata = &hal_property; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY: + property_id = HAL_CONFIG_REALTIME; + /* firmware has inverted values for realtime and + * non-realtime priority + */ + hal_property.enable = !(ctrl->val); + pdata = &hal_property; + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE: + inst->flags &= ~VIDC_REALTIME; + break; + case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE: + inst->flags |= VIDC_REALTIME; + break; + default: + dprintk(VIDC_WARN, + "inst(%pK) invalid priority ctrl value %#x\n", + inst, ctrl->val); + break; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE: + if ((ctrl->val >> 16) < inst->capability.frame_rate.min || + (ctrl->val >> 16) > inst->capability.frame_rate.max) { + dprintk(VIDC_ERR, "Invalid operating rate %u\n", + (ctrl->val >> 16)); + rc = -ENOTSUPP; + } else { + dprintk(VIDC_DBG, + "inst(%pK) operating rate changed from %d to %d\n", + inst, inst->prop.operating_rate >> 16, + ctrl->val >> 16); + inst->prop.operating_rate = ctrl->val; + } + break; + default: + break; + } + + v4l2_ctrl_lock(ctrl); +#undef TRY_GET_CTRL + + if (!rc && property_id) { + dprintk(VIDC_DBG, + "Control: HAL property=%#x,ctrl: id=%#x,value=%#x\n", + property_id, ctrl->id, ctrl->val); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, property_id, pdata); + } + + return rc; +} + +static int try_set_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0, i = 0, fourcc = 0; + struct v4l2_ext_control *ext_control; + struct v4l2_control control; + u32 old_mode = 0; + bool mode_changed = false; + enum mode { + PRIMARY = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY, + SECONDARY = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY + }; + + if (!inst || !inst->core || !ctrl) { + dprintk(VIDC_ERR, + "%s invalid parameters\n", __func__); + return -EINVAL; + } + + ext_control = ctrl->controls; + control.id = + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE; + old_mode = msm_comm_g_ctrl_for_id(inst, control.id); + + for (i = 0; i < ctrl->count; i++) { + switch (ext_control[i].id) { + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE: + control.value = ext_control[i].value; + + rc = msm_comm_s_ctrl(inst, &control); + if (rc) + dprintk(VIDC_ERR, + "%s Failed setting stream output mode : %d\n", + __func__, rc); + + if (old_mode == SECONDARY && control.value == PRIMARY) + mode_changed = true; + + break; + case V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT: + switch (ext_control[i].value) { + case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE: + if (!msm_comm_g_ctrl_for_id(inst, control.id)) { + rc = msm_comm_release_output_buffers( + inst); + if (rc) + dprintk(VIDC_ERR, + "%s Release output buffers failed\n", + __func__); + } + + /* Update buffer reqmt for split to comb mode */ + if (mode_changed) { + fourcc = + inst->fmts[CAPTURE_PORT].fourcc; + msm_comm_set_color_format(inst, + HAL_BUFFER_OUTPUT, fourcc); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting output color format : %d\n", + __func__, rc); + break; + } + rc = msm_comm_try_get_bufreqs(inst); + if (rc) + dprintk(VIDC_ERR, + "%s Failed to get buffer requirements : %d\n", + __func__, rc); + } + break; + case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC: + case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC: + if (ext_control[i].value == + V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC) + fourcc = V4L2_PIX_FMT_NV12_UBWC; + else + fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC; + if (msm_comm_g_ctrl_for_id(inst, control.id)) { + rc = msm_comm_set_color_format(inst, + HAL_BUFFER_OUTPUT, fourcc); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting output color format : %d\n", + __func__, rc); + break; + } + rc = msm_comm_try_get_bufreqs(inst); + if (rc) + dprintk(VIDC_ERR, + "%s Failed to get buffer requirements : %d\n", + __func__, rc); + } + break; + default: + dprintk(VIDC_ERR, + "%s Unsupported output color format\n", + __func__); + rc = -ENOTSUPP; + break; + } + break; + default: + dprintk(VIDC_ERR + , "%s Unsupported set control %d", + __func__, ext_control[i].id); + rc = -ENOTSUPP; + break; + } + } + + return rc; +} + +static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int rc = 0, c = 0; + struct msm_vidc_inst *inst = container_of(ctrl->handler, + struct msm_vidc_inst, ctrl_handler); + if (!inst) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + goto failed_open_done; + } + + for (c = 0; c < ctrl->ncontrols; ++c) { + if (ctrl->cluster[c]->is_new) { + rc = try_set_ctrl(inst, ctrl->cluster[c]); + if (rc) { + dprintk(VIDC_ERR, "Failed setting %x\n", + ctrl->cluster[c]->id); + break; + } + } + } + +failed_open_done: + return rc; +} + +static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int rc = 0, c = 0; + struct msm_vidc_inst *inst = container_of(ctrl->handler, + struct msm_vidc_inst, ctrl_handler); + struct v4l2_ctrl *master = ctrl->cluster[0]; + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + goto failed_open_done; + } + for (c = 0; c < master->ncontrols; ++c) { + int d = 0; + + for (d = 0; d < NUM_CTRLS; ++d) { + if (master->cluster[c]->id == inst->ctrls[d]->id && + inst->ctrls[d]->flags & + V4L2_CTRL_FLAG_VOLATILE) { + rc = try_get_ctrl(inst, master->cluster[c]); + if (rc) { + dprintk(VIDC_ERR, "Failed getting %x\n", + master->cluster[c]->id); + return rc; + } + break; + } + } + } + return rc; + +failed_open_done: + if (rc) + dprintk(VIDC_ERR, "Failed to get hal property\n"); + return rc; +} + +static const struct v4l2_ctrl_ops msm_vdec_ctrl_ops = { + + .s_ctrl = msm_vdec_op_s_ctrl, + .g_volatile_ctrl = msm_vdec_op_g_volatile_ctrl, +}; + +const struct v4l2_ctrl_ops *msm_vdec_get_ctrl_ops(void) +{ + return &msm_vdec_ctrl_ops; +} + +int msm_vdec_s_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0; + + rc = try_set_ext_ctrl(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Error setting extended control\n"); + return rc; + } + return rc; +} + +int msm_vdec_ctrl_init(struct msm_vidc_inst *inst) +{ + return msm_comm_ctrl_init(inst, msm_vdec_ctrls, + ARRAY_SIZE(msm_vdec_ctrls), &msm_vdec_ctrl_ops); +} + + diff --git a/drivers/media/platform/msm/vidc_3x/msm_vdec.h b/drivers/media/platform/msm/vidc_3x/msm_vdec.h new file mode 100644 index 000000000000..db2127600494 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vdec.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012, 2015, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MSM_VDEC_H_ +#define _MSM_VDEC_H_ + +#include +#include "msm_vidc_internal.h" + +int msm_vdec_inst_init(struct msm_vidc_inst *inst); +int msm_vdec_ctrl_init(struct msm_vidc_inst *inst); +int msm_vdec_querycap(void *instance, struct v4l2_capability *cap); +int msm_vdec_enum_fmt(void *instance, struct v4l2_fmtdesc *f); +int msm_vdec_s_fmt(void *instance, struct v4l2_format *f); +int msm_vdec_g_fmt(void *instance, struct v4l2_format *f); +int msm_vdec_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); +int msm_vdec_reqbufs(void *instance, struct v4l2_requestbuffers *b); +int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_vdec_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_vdec_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i); +int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i); +int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec); +int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a); +struct vb2_ops *msm_vdec_get_vb2q_ops(void); + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_venc.c b/drivers/media/platform/msm/vidc_3x/msm_venc.c new file mode 100644 index 000000000000..416d211b809f --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_venc.c @@ -0,0 +1,4659 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include "msm_vidc_internal.h" +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_dcvs.h" + +#define MSM_VENC_DVC_NAME "msm_venc_8974" +#define MIN_NUM_OUTPUT_BUFFERS 4 +#define MIN_NUM_CAPTURE_BUFFERS 4 +#define MIN_BIT_RATE 32000 +#define MAX_BIT_RATE 300000000 +#define DEFAULT_BIT_RATE 64000 +#define BIT_RATE_STEP 100 +#define DEFAULT_FRAME_RATE 15 +#define MAX_OPERATING_FRAME_RATE (300 << 16) +#define OPERATING_FRAME_RATE_STEP (1 << 16) +#define MAX_SLICE_BYTE_SIZE ((MAX_BIT_RATE)>>3) +#define MIN_SLICE_BYTE_SIZE 512 +#define MAX_SLICE_MB_SIZE ((4096 * 2304) >> 8) +#define I_FRAME_QP 26 +#define P_FRAME_QP 28 +#define B_FRAME_QP 30 +#define MAX_INTRA_REFRESH_MBS ((4096 * 2304) >> 8) +#define MAX_NUM_B_FRAMES 4 +#define MAX_LTR_FRAME_COUNT 10 +#define MAX_HYBRID_HIER_P_LAYERS 6 + +#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY +#define CODING V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY +#define BITSTREAM_RESTRICT_ENABLED \ + V4L2_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT_ENABLED +#define BITSTREAM_RESTRICT_DISABLED \ + V4L2_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT_DISABLED +#define MIN_TIME_RESOLUTION 1 +#define MAX_TIME_RESOLUTION 0xFFFFFF +#define DEFAULT_TIME_RESOLUTION 0x7530 + +/* + * Default 601 to 709 conversion coefficients for resolution: 176x144 negative + * coeffs are converted to s4.9 format (e.g. -22 converted to ((1 << 13) - 22) + * 3x3 transformation matrix coefficients in s4.9 fixed point format + */ +static u32 vpe_csc_601_to_709_matrix_coeff[HAL_MAX_MATRIX_COEFFS] = { + 470, 8170, 8148, 0, 490, 50, 0, 34, 483 +}; + +/* offset coefficients in s9 fixed point format */ +static u32 vpe_csc_601_to_709_bias_coeff[HAL_MAX_BIAS_COEFFS] = { + 34, 0, 4 +}; + +/* clamping value for Y/U/V([min,max] for Y/U/V) */ +static u32 vpe_csc_601_to_709_limit_coeff[HAL_MAX_LIMIT_COEFFS] = { + 16, 235, 16, 240, 16, 240 +}; + +static const char *const mpeg_video_rate_control[] = { + "No Rate Control", + "VBR VFR", + "VBR CFR", + "CBR VFR", + "CBR CFR", + "MBR CFR", + "MBR VFR", + NULL +}; + +static const char *const mpeg_video_rotation[] = { + "No Rotation", + "90 Degree Rotation", + "180 Degree Rotation", + "270 Degree Rotation", + NULL +}; + +static const char *const h264_video_entropy_cabac_model[] = { + "Model 0", + "Model 1", + "Model 2", + NULL +}; + +static const char *const h263_level[] = { + "1.0", + "2.0", + "3.0", + "4.0", + "4.5", + "5.0", + "6.0", + "7.0", +}; + +static const char *const h263_profile[] = { + "Baseline", + "H320 Coding", + "Backward Compatible", + "ISWV2", + "ISWV3", + "High Compression", + "Internet", + "Interlace", + "High Latency", +}; + +static const char *const hevc_tier_level[] = { + "Main Tier Level 1", + "Main Tier Level 2", + "Main Tier Level 2.1", + "Main Tier Level 3", + "Main Tier Level 3.1", + "Main Tier Level 4", + "Main Tier Level 4.1", + "Main Tier Level 5", + "Main Tier Level 5.1", + "Main Tier Level 5.2", + "Main Tier Level 6", + "Main Tier Level 6.1", + "Main Tier Level 6.2", + "High Tier Level 1", + "High Tier Level 2", + "High Tier Level 2.1", + "High Tier Level 3", + "High Tier Level 3.1", + "High Tier Level 4", + "High Tier Level 4.1", + "High Tier Level 5", + "High Tier Level 5.1", + "High Tier Level 5.2", + "High Tier Level 6", + "High Tier Level 6.1", + "High Tier Level 6.2", +}; + +static const char *const hevc_profile[] = { + "Main", + "Main10", + "Main Still Pic", +}; + +static const char *const vp8_profile_level[] = { + "Unused", + "0.0", + "1.0", + "2.0", + "3.0", +}; + +static const char *const perf_level[] = { + "Nominal", + "Performance", + "Turbo" +}; + +static const char *const mbi_statistics[] = { + "Camcorder Default", + "Mode 1", + "Mode 2", + "Mode 3" +}; + +static const char *const intra_refresh_modes[] = { + "None", + "Cyclic", + "Adaptive", + "Cyclic Adaptive", + "Random" +}; + +static const char *const timestamp_mode[] = { + "Honor", + "Ignore", +}; + +static const char *const iframe_sizes[] = { + "Default", + "Medium", + "Huge", + "Unlimited" +}; + +static struct msm_vidc_ctrl msm_venc_ctrls[] = { + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD, + .name = "IDR Period", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = INT_MAX, + .default_value = DEFAULT_FRAME_RATE, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES, + .name = "Intra Period for P frames", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 2*DEFAULT_FRAME_RATE-1, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES, + .name = "Intra Period for B frames", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = INT_MAX, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME, + .name = "Request I Frame", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL, + .name = "Video Framerate and Bitrate Control", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF, + .step = 0, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR) + ), + .qmenu = mpeg_video_rate_control, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + .name = "Bitrate Control", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .maximum = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + .default_value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .step = 0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + ), + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE, + .name = "Bit Rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_BIT_RATE, + .maximum = MAX_BIT_RATE, + .default_value = DEFAULT_BIT_RATE, + .step = BIT_RATE_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + .name = "Peak Bit Rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_BIT_RATE, + .maximum = MAX_BIT_RATE, + .default_value = DEFAULT_BIT_RATE, + .step = BIT_RATE_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + .name = "Entropy Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) | + (1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) + ), + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL, + .name = "CABAC Model", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2) + ), + .qmenu = h264_video_entropy_cabac_model, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + .name = "MPEG4 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, + .maximum = CODING, + .default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + .name = "MPEG4 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, + .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, + .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .name = "H264 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .name = "H264 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_5_2, + .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .menu_skip_mask = 0, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE, + .name = "H263 Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE, + .maximum = V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY, + .default_value = V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_H320CODING) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BACKWARDCOMPATIBLE) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV2) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV3) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHCOMPRESSION) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERNET) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERLACE) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY) + ), + .qmenu = h263_profile, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL, + .name = "H263 Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0, + .maximum = V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0, + .default_value = V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_4_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_5_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_6_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0) + ), + .qmenu = h263_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + .name = "VP8 Profile Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1, + .default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED) | + (1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0) | + (1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1) + ), + .qmenu = vp8_profile_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, + .name = "HEVC Profile", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN, + .maximum = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC, + .default_value = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC) + ), + .qmenu = hevc_profile, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, + .name = "HEVC Tier and Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1, + .maximum = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2, + .default_value = + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5) | + (1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1) + ), + .qmenu = hevc_tier_level, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION, + .name = "Rotation", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270) + ), + .qmenu = mpeg_video_rotation, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + .name = "H264 I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + .name = "H264 P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + .name = "H264 B Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = B_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP, + .name = "H263 I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP, + .name = "H263 P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP, + .name = "H263 B Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = B_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP, + .name = "VPX I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP, + .name = "VPX P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + .name = "H264 Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + .name = "H264 Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 51, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, + .name = "VPX Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, + .name = "VPX Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 127, + .default_value = 127, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP, + .name = "VP8 Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 128, + .default_value = 1, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP, + .name = "VP8 Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 128, + .default_value = 128, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP, + .name = "MPEG4 Minimum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = 1, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP, + .name = "MPEG4 Maximum QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 31, + .default_value = 31, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED, + .name = "H264 Minimum QP PACKED", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0x00010101, + .maximum = 0x00333333, + .default_value = 0x00010101, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED, + .name = "H264 Maximum QP PACKED", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0x00010101, + .maximum = 0x00333333, + .default_value = 0x00333333, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, + .name = "Slice Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, + .maximum = V4L2_MPEG_VIDEO_MULTI_SLICE_GOB, + .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE) | + (1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) | + (1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) | + (1 << V4L2_MPEG_VIDEO_MULTI_SLICE_GOB) + ), + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, + .name = "Slice Byte Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_SLICE_BYTE_SIZE, + .maximum = MAX_SLICE_BYTE_SIZE, + .default_value = MIN_SLICE_BYTE_SIZE, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, + .name = "Slice MB Size", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = MAX_SLICE_MB_SIZE, + .default_value = 1, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB, + .name = "Slice GOB", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = MAX_SLICE_MB_SIZE, + .default_value = 1, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE, + .name = "Slice delivery mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 1, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE, + .name = "Intra Refresh Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOMMODE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_ADAPTIVE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOMMODE) + ), + .qmenu = intra_refresh_modes, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS, + .name = "Intra Refresh AIR MBS", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_INTRA_REFRESH_MBS, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF, + .name = "Intra Refresh AIR REF", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_INTRA_REFRESH_MBS, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS, + .name = "Intra Refresh CIR MBS", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_INTRA_REFRESH_MBS, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, + .name = "H.264 Loop Filter Alpha Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = -6, + .maximum = 6, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, + .name = "H.264 Loop Filter Beta Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = -6, + .maximum = 6, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + .name = "H.264 Loop Filter Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED, + .maximum = L_MODE, + .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED) | + (1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED) | + (1 << L_MODE) + ), + }, + { + .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, + .name = "Sequence Header Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME, + .default_value = + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | + (1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME) + ), + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE, + .name = "Secure mode", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA, + .name = "Extradata Type", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .maximum = V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS, + .default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP) | + (1ULL << V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) | + (1ULL << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)| + (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO) | + (1ULL << V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP) + ), + .qmenu = mpeg_video_vidc_extradata, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO, + .name = "H264 VUI Timing Info", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED, + .maximum = V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED, + .default_value = + V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER, + .name = "H264 AU Delimiter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED, + .maximum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED, + .step = 1, + .default_value = + V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED, + }, + { + .id = V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL, + .name = "Encoder Performance Level", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL, + .maximum = V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO, + .default_value = V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL) | + (1 << V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO)), + .qmenu = perf_level, + }, + { + .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, + .name = "Intra Refresh CIR MBS", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_INTRA_REFRESH_MBS, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT, + .name = "H264 VUI Timing Info", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = BITSTREAM_RESTRICT_DISABLED, + .maximum = BITSTREAM_RESTRICT_ENABLED, + .default_value = BITSTREAM_RESTRICT_ENABLED, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY, + .name = "Preserve Text Qualty", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_DISABLED, + .maximum = V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_ENABLED, + .default_value = + V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_DISABLED, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE, + .name = "Deinterlace for encoder", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG4_TIME_RESOLUTION, + .name = "Vop time increment resolution", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_TIME_RESOLUTION, + .maximum = MAX_TIME_RESOLUTION, + .default_value = DEFAULT_TIME_RESOLUTION, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_SEQ_HEADER, + .name = "Request Seq Header", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME, + .name = "H264 Use LTR", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = (MAX_LTR_FRAME_COUNT - 1), + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT, + .name = "Ltr Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_LTR_FRAME_COUNT, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE, + .name = "Ltr Mode", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL, + .default_value = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME, + .name = "H264 Mark LTR", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = (MAX_LTR_FRAME_COUNT - 1), + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS, + .name = "Set Hier P num layers", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE, + .name = "Encoder Timestamp Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = + V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_HONOR, + .maximum = + V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_IGNORE, + .default_value = + V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_HONOR, + .menu_skip_mask = ~( + (1 << V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_HONOR) | + (1 << V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_IGNORE)), + .qmenu = timestamp_mode, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE, + .name = "VP8 Error Resilience mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_DISABLED, + .maximum = V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_ENABLED, + .default_value = + V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_DISABLED, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP, + .name = "Enable setting initial QP", + .type = V4L2_CTRL_TYPE_BITMASK, + .minimum = 0, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_IFRAME | + V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_PFRAME | + V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_BFRAME, + .default_value = 0, + .step = 0, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP, + .name = "Iframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP, + .name = "Pframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP, + .name = "Bframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP, + .name = "Iframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP, + .name = "Pframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP, + .name = "Bframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE, + .name = "I-Frame X coordinate search range", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 4, + .maximum = 128, + .default_value = 4, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE, + .name = "I-Frame Y coordinate search range", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 4, + .maximum = 128, + .default_value = 4, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE, + .name = "P-Frame X coordinate search range", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 4, + .maximum = 128, + .default_value = 4, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE, + .name = "P-Frame Y coordinate search range", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 4, + .maximum = 128, + .default_value = 4, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE, + .name = "B-Frame X coordinate search range", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 4, + .maximum = 128, + .default_value = 4, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE, + .name = "B-Frame Y coordinate search range", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 4, + .maximum = 128, + .default_value = 4, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC, + .name = "Enable H264 SVC NAL", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC_DISABLED, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC_ENABLED, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC_DISABLED, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PERF_MODE, + .name = "Set Encoder performance mode", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = V4L2_MPEG_VIDC_VIDEO_PERF_MAX_QUALITY, + .maximum = V4L2_MPEG_VIDC_VIDEO_PERF_POWER_SAVE, + .default_value = V4L2_MPEG_VIDC_VIDEO_PERF_MAX_QUALITY, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS, + .name = "Set Hier B num layers", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 3, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE, + .name = "Set Hybrid Hier P mode", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 5, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MBI_STATISTICS_MODE, + .name = "MBI Statistics Mode", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_3, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_1) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_2) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_3)), + .qmenu = mbi_statistics, + }, + { + .id = V4L2_CID_VIDC_QBUF_MODE, + .name = "Allows batching of buffers for power savings", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_VIDC_QBUF_STANDARD, + .maximum = V4L2_VIDC_QBUF_BATCHED, + .default_value = V4L2_VIDC_QBUF_STANDARD, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS, + .name = "Set Max Hier P num layers sessions", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID, + .name = "Set Base Layer ID for Hier-P", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 6, + .default_value = 0, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_CONFIG_QP, + .name = "Set frame level QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 127, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH, + .name = "SAR Width", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 4096, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT, + .name = "SAR Height", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 2160, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY, + .name = "Session Priority", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE, + .default_value = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI, + .name = "VQZIP SEI", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_DISABLE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_ENABLE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE, + .name = "Layer wise bitrate for H264/H265 Hybrid HP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MIN_BIT_RATE, + .maximum = MAX_BIT_RATE, + .default_value = DEFAULT_BIT_RATE, + .step = BIT_RATE_STEP, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE, + .name = "Set Encoder Operating rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = MAX_OPERATING_FRAME_RATE, + .default_value = 0, + .step = OPERATING_FRAME_RATE_STEP, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE, + .name = "BITRATE TYPE", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_DISABLE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_PIC_ORDER_CNT, + .name = "Set H264 Picture Order Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 2, + .default_value = 0, + .step = 2, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC, + .name = "Set VPE Color space conversion coefficients", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_DISABLE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE, + .name = "Low Latency Mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH, + .name = "Set Blur width", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 2048, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT, + .name = "Set Blur height", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 2048, + .default_value = 0, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE, + .name = "Set Color space", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MSM_VIDC_BT709_5, + .maximum = MSM_VIDC_BT2020, + .default_value = MSM_VIDC_BT601_6_625, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE, + .name = "Set Color space range", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_ENABLE, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS, + .name = "Set Color space transfer characterstics", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MSM_VIDC_TRANSFER_BT709_5, + .maximum = MSM_VIDC_TRANSFER_BT_2020_12, + .default_value = MSM_VIDC_TRANSFER_601_6_625, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS, + .name = "Set Color space matrix coefficients", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = MSM_VIDC_MATRIX_BT_709_5, + .maximum = MSM_VIDC_MATRIX_BT_2020_CONST, + .default_value = MSM_VIDC_MATRIX_601_6_625, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8, + .name = "Transform 8x8", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE, + .maximum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE, + .default_value = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE, + .step = 1, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE, + .name = "Bounds of I-frame size", + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT, + .maximum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT, + .menu_skip_mask = ~( + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE) | + (1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED)), + .qmenu = iframe_sizes, + }, +}; + +#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls) + +static u32 get_frame_size_nv12(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height); +} + +static u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_UBWC, width, height); +} + +static u32 get_frame_size_rgba(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_RGBA8888, width, height); +} + +static u32 get_frame_size_rgba_ubwc(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_RGBA8888_UBWC, width, height); +} + +static u32 get_frame_size_nv21(int plane, u32 height, u32 width) +{ + return VENUS_BUFFER_SIZE(COLOR_FMT_NV21, width, height); +} + +static u32 get_frame_size_compressed(int plane, u32 height, u32 width) +{ + int sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2; + + return ALIGN(sz, SZ_4K); +} + +static struct msm_vidc_format venc_formats[] = { + { + .name = "YCbCr Semiplanar 4:2:0", + .description = "Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .get_frame_size = get_frame_size_nv12, + .type = OUTPUT_PORT, + }, + { + .name = "UBWC YCbCr Semiplanar 4:2:0", + .description = "UBWC Y/CbCr 4:2:0", + .fourcc = V4L2_PIX_FMT_NV12_UBWC, + .num_planes = 1, + .get_frame_size = get_frame_size_nv12_ubwc, + .type = OUTPUT_PORT, + }, + { + .name = "RGBA 8:8:8:8", + .description = "RGBA 8:8:8:8", + .fourcc = V4L2_PIX_FMT_RGB32, + .num_planes = 1, + .get_frame_size = get_frame_size_rgba, + .type = OUTPUT_PORT, + }, + { + .name = "UBWC RGBA 8:8:8:8", + .description = "UBWC RGBA 8:8:8:8", + .fourcc = V4L2_PIX_FMT_RGBA8888_UBWC, + .num_planes = 1, + .get_frame_size = get_frame_size_rgba_ubwc, + .type = OUTPUT_PORT, + }, + { + .name = "Mpeg4", + .description = "Mpeg4 compressed format", + .fourcc = V4L2_PIX_FMT_MPEG4, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + }, + { + .name = "H263", + .description = "H263 compressed format", + .fourcc = V4L2_PIX_FMT_H263, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + }, + { + .name = "H264", + .description = "H264 compressed format", + .fourcc = V4L2_PIX_FMT_H264, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + }, + { + .name = "VP8", + .description = "VP8 compressed format", + .fourcc = V4L2_PIX_FMT_VP8, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + }, + { + .name = "HEVC", + .description = "HEVC compressed format", + .fourcc = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .get_frame_size = get_frame_size_compressed, + .type = CAPTURE_PORT, + }, + { + .name = "YCrCb Semiplanar 4:2:0", + .description = "Y/CrCb 4:2:0", + .fourcc = V4L2_PIX_FMT_NV21, + .num_planes = 1, + .get_frame_size = get_frame_size_nv21, + .type = OUTPUT_PORT, + }, +}; + +static void msm_venc_update_plane_count(struct msm_vidc_inst *inst, int type) +{ + struct v4l2_ctrl *ctrl = NULL; + u32 extradata = 0; + + if (!inst) + return; + + inst->fmts[type].num_planes = 1; + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA); + + if (ctrl) + extradata = v4l2_ctrl_g_ctrl(ctrl); + + if (type == CAPTURE_PORT) { + switch (extradata) { + case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO: + case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB: + case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER: + case V4L2_MPEG_VIDC_EXTRADATA_LTR: + case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP: + case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI: + inst->fmts[CAPTURE_PORT].num_planes = 2; + default: + break; + } + } else if (type == OUTPUT_PORT) { + switch (extradata) { + case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP: + case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM: + case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: + case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS: + case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP: + case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO: + inst->fmts[OUTPUT_PORT].num_planes = 2; + break; + default: + break; + } + } +} + +static int msm_venc_set_csc(struct msm_vidc_inst *inst); + +static int msm_venc_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + int i, temp, rc = 0; + struct msm_vidc_inst *inst; + struct hal_buffer_count_actual new_buf_count; + enum hal_property property_id; + struct hfi_device *hdev; + struct hal_buffer_requirements *buff_req; + u32 extra_idx = 0; + struct hal_buffer_requirements *buff_req_buffer = NULL; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input\n"); + return -EINVAL; + } + inst = q->drv_priv; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + return rc; + } + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get buffer requirements: %d\n", rc); + return rc; + } + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + *num_planes = 1; + + buff_req = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + if (buff_req) { + + /* Pretend as if the FW itself is asking for additional + * buffers, which are required for DCVS + */ + unsigned int min_req_buffers = + buff_req->buffer_count_min + + msm_dcvs_get_extra_buff_count(inst); + *num_buffers = max(*num_buffers, min_req_buffers); + } + + if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS || + *num_buffers > VB2_MAX_FRAME) { + int temp = *num_buffers; + + *num_buffers = clamp_val(*num_buffers, + MIN_NUM_CAPTURE_BUFFERS, + VB2_MAX_FRAME); + dprintk(VIDC_INFO, + "Changing buffer count on CAPTURE_MPLANE from %d to %d for best effort encoding\n", + temp, *num_buffers); + } + + msm_venc_update_plane_count(inst, CAPTURE_PORT); + *num_planes = inst->fmts[CAPTURE_PORT].num_planes; + + for (i = 0; i < *num_planes; i++) { + int extra_idx = EXTRADATA_IDX(*num_planes); + + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + + sizes[i] = buff_req_buffer ? + buff_req_buffer->buffer_size : 0; + + if (extra_idx && i == extra_idx && + extra_idx < VIDEO_MAX_PLANES) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_OUTPUT); + if (!buff_req_buffer) { + dprintk(VIDC_ERR, + "%s: failed - invalid buffer req\n", + __func__); + return -EINVAL; + } + + sizes[i] = buff_req_buffer->buffer_size; + } + } + + dprintk(VIDC_DBG, "actual output buffer count set to fw = %d\n", + *num_buffers); + property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; + new_buf_count.buffer_type = HAL_BUFFER_OUTPUT; + new_buf_count.buffer_count_actual = *num_buffers; + rc = call_hfi_op(hdev, session_set_property, inst->session, + property_id, &new_buf_count); + + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + *num_planes = 1; + + *num_buffers = inst->buff_req.buffer[0].buffer_count_actual = + max(*num_buffers, + inst->buff_req.buffer[0].buffer_count_min); + + temp = *num_buffers; + + *num_buffers = clamp_val(*num_buffers, + MIN_NUM_OUTPUT_BUFFERS, + VB2_MAX_FRAME); + dprintk(VIDC_INFO, + "Changing buffer count on OUTPUT_MPLANE from %d to %d for best effort encoding\n", + temp, *num_buffers); + + property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; + new_buf_count.buffer_type = HAL_BUFFER_INPUT; + new_buf_count.buffer_count_actual = *num_buffers; + + dprintk(VIDC_DBG, "actual input buffer count set to fw = %d\n", + *num_buffers); + msm_venc_update_plane_count(inst, OUTPUT_PORT); + *num_planes = inst->fmts[OUTPUT_PORT].num_planes; + + rc = call_hfi_op(hdev, session_set_property, inst->session, + property_id, &new_buf_count); + if (rc) + dprintk(VIDC_ERR, "failed to set count to fw\n"); + + dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n", + inst->buff_req.buffer[0].buffer_size, + inst->buff_req.buffer[0].buffer_alignment, + inst->buff_req.buffer[0].buffer_count_actual); + sizes[0] = inst->fmts[OUTPUT_PORT].get_frame_size( + 0, inst->prop.height[OUTPUT_PORT], + inst->prop.width[OUTPUT_PORT]); + + extra_idx = + EXTRADATA_IDX(inst->fmts[OUTPUT_PORT].num_planes); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { + buff_req_buffer = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_INPUT); + if (!buff_req_buffer) { + dprintk(VIDC_ERR, + "%s: failed - invalid buffer req\n", + __func__); + return -EINVAL; + } + + sizes[extra_idx] = buff_req_buffer->buffer_size; + } + + break; + default: + dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type); + rc = -EINVAL; + break; + } + return rc; +} + +static int msm_venc_toggle_hier_p(struct msm_vidc_inst *inst, int layers) +{ + int num_enh_layers = 0; + u32 property_id = 0; + struct hfi_device *hdev = NULL; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_VP8) + return 0; + + num_enh_layers = layers ? : 0; + dprintk(VIDC_DBG, "%s Hier-P in firmware\n", + num_enh_layers ? "Enable" : "Disable"); + + hdev = inst->core->device; + property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, + (void *)&num_enh_layers); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed with error = %d\n", __func__, rc); + } + return rc; +} + +static inline int msm_venc_power_save_mode_enable(struct msm_vidc_inst *inst) +{ + u32 rc = 0; + u32 prop_id = 0, power_save_min = 0, power_save_max = 0, inst_load = 0; + void *pdata = NULL; + struct hfi_device *hdev = NULL; + enum hal_perf_mode venc_mode; + enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD | + LOAD_CALC_IGNORE_THUMBNAIL_LOAD; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + inst_load = msm_comm_get_inst_load(inst, quirks); + power_save_min = inst->capability.mbs_per_sec_power_save.min; + power_save_max = inst->capability.mbs_per_sec_power_save.max; + + dprintk(VIDC_DBG, + "Power Save Mode min mb's %d max mb's %d inst load %d\n", + power_save_min, power_save_max, inst_load); + + if (!power_save_min || !power_save_max) + return rc; + + hdev = inst->core->device; + if (inst_load >= power_save_min) { + prop_id = HAL_CONFIG_VENC_PERF_MODE; + venc_mode = HAL_PERF_MODE_POWER_SAVE; + pdata = &venc_mode; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, prop_id, pdata); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set power save mode for inst: %pK\n", + __func__, inst); + goto fail_power_mode_set; + } + inst->flags |= VIDC_LOW_POWER; + msm_dcvs_enc_set_power_save_mode(inst, true); + dprintk(VIDC_INFO, "Power Save Mode set for inst: %pK\n", inst); + } + +fail_power_mode_set: + return rc; +} + +static inline int start_streaming(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; + } + msm_venc_power_save_mode_enable(inst); + if (inst->capability.pixelprocess_capabilities & + HAL_VIDEO_ENCODER_SCALING_CAPABILITY) + rc = msm_vidc_check_scaling_supported(inst); + if (rc) { + dprintk(VIDC_ERR, "H/w scaling is not in valid range\n"); + return -EINVAL; + } + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to get Buffer Requirements : %d\n", rc); + goto fail_start; + } + rc = msm_comm_set_scratch_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, "Failed to set scratch buffers: %d\n", rc); + goto fail_start; + } + rc = msm_comm_set_persist_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, "Failed to set persist buffers: %d\n", rc); + goto fail_start; + } + + msm_comm_scale_clocks_and_bus(inst); + + rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + goto fail_start; + } + msm_dcvs_init_load(inst); + +fail_start: + return rc; +} + +static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct msm_vidc_inst *inst; + int rc = 0; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); + return -EINVAL; + } + inst = q->drv_priv; + dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n", + q->type, inst); + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming) + rc = start_streaming(inst); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming) + rc = start_streaming(inst); + break; + default: + dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type); + rc = -EINVAL; + goto stream_start_failed; + } + if (rc) { + dprintk(VIDC_ERR, + "Streamon failed on: %d capability for inst: %pK\n", + q->type, inst); + goto stream_start_failed; + } + + rc = msm_comm_qbuf(inst, NULL); + if (rc) { + dprintk(VIDC_ERR, + "Failed to commit buffers queued before STREAM_ON to hardware: %d\n", + rc); + goto stream_start_failed; + } + +stream_start_failed: + return rc; +} + +static void msm_venc_stop_streaming(struct vb2_queue *q) +{ + struct msm_vidc_inst *inst; + int rc = 0; + + if (!q || !q->drv_priv) { + dprintk(VIDC_ERR, "%s - Invalid input, q = %pK\n", __func__, q); + return; + } + + inst = q->drv_priv; + dprintk(VIDC_DBG, "Streamoff called on: %d capability\n", q->type); + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + break; + default: + dprintk(VIDC_ERR, "Q-type is not supported: %d\n", q->type); + rc = -EINVAL; + break; + } + + msm_comm_scale_clocks_and_bus(inst); + + if (rc) + dprintk(VIDC_ERR, + "Failed to move inst: %pK, cap = %d to state: %d\n", + inst, q->type, MSM_VIDC_CLOSE_DONE); +} + +static void msm_venc_buf_queue(struct vb2_buffer *vb) +{ + int rc = msm_comm_qbuf(vb2_get_drv_priv(vb->vb2_queue), vb); + + if (rc) + dprintk(VIDC_ERR, "Failed to queue buffer: %d\n", rc); +} + +static const struct vb2_ops msm_venc_vb2q_ops = { + .queue_setup = msm_venc_queue_setup, + .start_streaming = msm_venc_start_streaming, + .buf_queue = msm_venc_buf_queue, + .stop_streaming = msm_venc_stop_streaming, +}; + +const struct vb2_ops *msm_venc_get_vb2q_ops(void) +{ + return &msm_venc_vb2q_ops; +} + +static struct v4l2_ctrl *get_ctrl_from_cluster(int id, + struct v4l2_ctrl **cluster, int ncontrols) +{ + int c; + + for (c = 0; c < ncontrols; ++c) + if (cluster[c]->id == id) + return cluster[c]; + return NULL; +} + +/* Helper function to translate V4L2_* to HAL_* */ +static inline int venc_v4l2_to_hal(int id, int value) +{ + switch (id) { + /* MPEG4 */ + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0: + return HAL_MPEG4_LEVEL_0; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B: + return HAL_MPEG4_LEVEL_0b; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_1: + return HAL_MPEG4_LEVEL_1; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_2: + return HAL_MPEG4_LEVEL_2; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_3: + return HAL_MPEG4_LEVEL_3; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_4: + return HAL_MPEG4_LEVEL_4; + case V4L2_MPEG_VIDEO_MPEG4_LEVEL_5: + return HAL_MPEG4_LEVEL_5; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE: + return HAL_MPEG4_PROFILE_SIMPLE; + case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE: + return HAL_MPEG4_PROFILE_ADVANCEDSIMPLE; + default: + goto unknown_value; + } + /* H264 */ + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return HAL_H264_PROFILE_BASELINE; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + return HAL_H264_PROFILE_CONSTRAINED_BASE; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return HAL_H264_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + return HAL_H264_PROFILE_EXTENDED; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return HAL_H264_PROFILE_HIGH; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10: + return HAL_H264_PROFILE_HIGH10; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422: + return HAL_H264_PROFILE_HIGH422; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE: + return HAL_H264_PROFILE_HIGH444; + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH: + return HAL_H264_PROFILE_CONSTRAINED_HIGH; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return HAL_H264_LEVEL_1; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return HAL_H264_LEVEL_1b; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return HAL_H264_LEVEL_11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return HAL_H264_LEVEL_12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return HAL_H264_LEVEL_13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return HAL_H264_LEVEL_2; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return HAL_H264_LEVEL_21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return HAL_H264_LEVEL_22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return HAL_H264_LEVEL_3; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return HAL_H264_LEVEL_31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return HAL_H264_LEVEL_32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return HAL_H264_LEVEL_4; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return HAL_H264_LEVEL_41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return HAL_H264_LEVEL_42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return HAL_H264_LEVEL_5; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return HAL_H264_LEVEL_51; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_2: + return HAL_H264_LEVEL_52; + default: + goto unknown_value; + } + /* H263 */ + case V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE: + return HAL_H263_PROFILE_BASELINE; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_H320CODING: + return HAL_H263_PROFILE_H320CODING; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BACKWARDCOMPATIBLE: + return HAL_H263_PROFILE_BACKWARDCOMPATIBLE; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV2: + return HAL_H263_PROFILE_ISWV2; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV3: + return HAL_H263_PROFILE_ISWV3; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHCOMPRESSION: + return HAL_H263_PROFILE_HIGHCOMPRESSION; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERNET: + return HAL_H263_PROFILE_INTERNET; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERLACE: + return HAL_H263_PROFILE_INTERLACE; + case V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY: + return HAL_H263_PROFILE_HIGHLATENCY; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC: + return HAL_H264_ENTROPY_CAVLC; + case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: + return HAL_H264_ENTROPY_CABAC; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0: + return HAL_H264_CABAC_MODEL_0; + case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1: + return HAL_H264_CABAC_MODEL_1; + case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2: + return HAL_H264_CABAC_MODEL_2; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0: + return HAL_H263_LEVEL_10; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_2_0: + return HAL_H263_LEVEL_20; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_3_0: + return HAL_H263_LEVEL_30; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_4_0: + return HAL_H263_LEVEL_40; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_4_5: + return HAL_H263_LEVEL_45; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_5_0: + return HAL_H263_LEVEL_50; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_6_0: + return HAL_H263_LEVEL_60; + case V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0: + return HAL_H263_LEVEL_70; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0: + return HAL_VPX_PROFILE_VERSION_0; + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1: + return HAL_VPX_PROFILE_VERSION_1; + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2: + return HAL_VPX_PROFILE_VERSION_2; + case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3: + return HAL_VPX_PROFILE_VERSION_3; + case V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED: + return HAL_VPX_PROFILE_UNUSED; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN: + return HAL_HEVC_PROFILE_MAIN; + case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10: + return HAL_HEVC_PROFILE_MAIN10; + case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC: + return HAL_HEVC_PROFILE_MAIN_STILL_PIC; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + switch (value) { + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1: + return HAL_HEVC_MAIN_TIER_LEVEL_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2: + return HAL_HEVC_MAIN_TIER_LEVEL_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1: + return HAL_HEVC_MAIN_TIER_LEVEL_2_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3: + return HAL_HEVC_MAIN_TIER_LEVEL_3; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1: + return HAL_HEVC_MAIN_TIER_LEVEL_3_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4: + return HAL_HEVC_MAIN_TIER_LEVEL_4; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1: + return HAL_HEVC_MAIN_TIER_LEVEL_4_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5: + return HAL_HEVC_MAIN_TIER_LEVEL_5; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1: + return HAL_HEVC_MAIN_TIER_LEVEL_5_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_2: + return HAL_HEVC_MAIN_TIER_LEVEL_5_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6: + return HAL_HEVC_MAIN_TIER_LEVEL_6; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_1: + return HAL_HEVC_MAIN_TIER_LEVEL_6_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_2: + return HAL_HEVC_MAIN_TIER_LEVEL_6_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1: + return HAL_HEVC_HIGH_TIER_LEVEL_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2: + return HAL_HEVC_HIGH_TIER_LEVEL_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1: + return HAL_HEVC_HIGH_TIER_LEVEL_2_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3: + return HAL_HEVC_HIGH_TIER_LEVEL_3; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1: + return HAL_HEVC_HIGH_TIER_LEVEL_3_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4: + return HAL_HEVC_HIGH_TIER_LEVEL_4; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1: + return HAL_HEVC_HIGH_TIER_LEVEL_4_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5: + return HAL_HEVC_HIGH_TIER_LEVEL_5; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1: + return HAL_HEVC_HIGH_TIER_LEVEL_5_1; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2: + return HAL_HEVC_HIGH_TIER_LEVEL_5_2; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6: + return HAL_HEVC_HIGH_TIER_LEVEL_6; + case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_1: + return HAL_HEVC_HIGH_TIER_LEVEL_6_1; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE: + return HAL_ROTATE_NONE; + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90: + return HAL_ROTATE_90; + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180: + return HAL_ROTATE_180; + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270: + return HAL_ROTATE_270; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + switch (value) { + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED: + return HAL_H264_DB_MODE_DISABLE; + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: + return HAL_H264_DB_MODE_ALL_BOUNDARY; + case L_MODE: + return HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_MBI_STATISTICS_MODE: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT: + return HAL_STATISTICS_MODE_DEFAULT; + case V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_1: + return HAL_STATISTICS_MODE_1; + case V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_2: + return HAL_STATISTICS_MODE_2; + case V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_3: + return HAL_STATISTICS_MODE_3; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT: + return HAL_IFRAMESIZE_TYPE_DEFAULT; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM: + return HAL_IFRAMESIZE_TYPE_MEDIUM; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE: + return HAL_IFRAMESIZE_TYPE_HUGE; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED: + return HAL_IFRAMESIZE_TYPE_UNLIMITED; + default: + goto unknown_value; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE: + switch (value) { + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE: + return HAL_INTRA_REFRESH_NONE; + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC: + return HAL_INTRA_REFRESH_CYCLIC; + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOMMODE: + return HAL_INTRA_REFRESH_RANDOM; + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_ADAPTIVE: + return HAL_INTRA_REFRESH_ADAPTIVE; + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE: + return HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE; + default: + goto unknown_value; + } + } + +unknown_value: + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); + return -EINVAL; +} + +/* Small helper macro for quickly getting a control and err checking */ +#define TRY_GET_CTRL(__ctrl_id) ({ \ + struct v4l2_ctrl *__temp; \ + __temp = get_ctrl_from_cluster( \ + __ctrl_id, \ + ctrl->cluster, ctrl->ncontrols); \ + if (!__temp) { \ + dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \ + #__ctrl_id, __ctrl_id); \ + /* Clusters are hardcoded, if we can't find */ \ + /* something then things are massively screwed up */ \ + WARN_ON(VIDC_DBG_WARN_ENABLE); \ + } \ + __temp; \ + }) + +static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst, + struct v4l2_ctrl *ctrl) +{ + int rc = 0, min, max; + struct v4l2_ctrl *temp_ctrl = NULL; + int qp_value = ctrl->val; + +#define VALIDATE_BOUNDARIES(__min, __max, __val) ({\ + int __rc = __val >= __min && \ + __val <= __max; \ + if (!__rc) \ + dprintk(VIDC_ERR, "QP beyond range: min(%d) max(%d) val(%d)", \ + __min, __max, __val); \ + __rc; \ +}) + + switch (inst->fmts[CAPTURE_PORT].fourcc) { + case V4L2_PIX_FMT_VP8: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control"); + return -EINVAL; + } + max = temp_ctrl->maximum; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control"); + return -EINVAL; + } + min = temp_ctrl->minimum; + if (!VALIDATE_BOUNDARIES(min, max, qp_value)) + rc = -EINVAL; + break; + case V4L2_PIX_FMT_H263: + case V4L2_PIX_FMT_MPEG4: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control"); + return -EINVAL; + } + max = temp_ctrl->maximum; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control"); + return -EINVAL; + } + min = temp_ctrl->minimum; + if (!VALIDATE_BOUNDARIES(min, max, qp_value)) + rc = -EINVAL; + break; + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_HEVC: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control"); + return -EINVAL; + } + max = temp_ctrl->maximum; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP); + if (!temp_ctrl) { + dprintk(VIDC_ERR, + "failed to get control"); + return -EINVAL; + } + min = temp_ctrl->minimum; + if (!VALIDATE_BOUNDARIES(min, max, qp_value)) + rc = -EINVAL; + break; + default: + dprintk(VIDC_ERR, "%s Invalid Codec\n", __func__); + return -EINVAL; + } + return rc; +#undef VALIDATE_BOUNDARIES +} + + +static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0; + struct hal_request_iframe request_iframe; + struct hal_bitrate bitrate; + struct hal_profile_level profile_level; + struct hal_h264_entropy_control h264_entropy_control; + struct hal_quantization quantization; + struct hal_intra_period intra_period; + struct hal_idr_period idr_period; + struct hal_operations operations; + struct hal_intra_refresh intra_refresh; + struct hal_multi_slice_control multi_slice_control; + struct hal_h264_db_control h264_db_control; + struct hal_enable enable; + struct hal_h264_vui_timing_info vui_timing_info; + struct hal_quantization_range qp_range; + struct hal_h264_vui_bitstream_restrc vui_bitstream_restrict; + struct hal_preserve_text_quality preserve_text_quality; + u32 property_id = 0, property_val = 0; + void *pdata = NULL; + struct v4l2_ctrl *temp_ctrl = NULL; + struct hfi_device *hdev; + struct hal_extradata_enable extra; + struct hal_mpeg4_time_resolution time_res; + struct hal_ltr_use use_ltr; + struct hal_ltr_mark mark_ltr; + struct hal_hybrid_hierp hyb_hierp; + u32 hier_p_layers = 0, hier_b_layers = 0, mbi_statistics_mode = 0; + enum hal_perf_mode venc_mode; + int max_hierp_layers; + int baselayerid = 0; + int frameqp = 0; + int pic_order_cnt = 0; + struct hal_video_signal_info signal_info = {0}; + enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + /* + * Unlock the control prior to setting to the hardware. Otherwise + * lower level code that attempts to do a get_ctrl() will end up + * deadlocking. + */ + v4l2_ctrl_unlock(ctrl); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD: + if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_H264 && + inst->fmts[CAPTURE_PORT].fourcc != + V4L2_PIX_FMT_H264_NO_SC && + inst->fmts[CAPTURE_PORT].fourcc != + V4L2_PIX_FMT_HEVC) { + dprintk(VIDC_ERR, + "Control %#x only valid for H264 and HEVC\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + + property_id = HAL_CONFIG_VENC_IDR_PERIOD; + idr_period.idr_period = ctrl->val; + pdata = &idr_period; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES: + case V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES: + { + int num_p, num_b; + u32 max_num_b_frames; + + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES); + num_b = temp_ctrl->val; + + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES); + num_p = temp_ctrl->val; + + if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES) + num_p = ctrl->val; + else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES) + num_b = ctrl->val; + + max_num_b_frames = num_b ? MAX_NUM_B_FRAMES : 0; + property_id = HAL_PARAM_VENC_MAX_NUM_B_FRAMES; + pdata = &max_num_b_frames; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + if (rc) { + dprintk(VIDC_ERR, + "Failed : Setprop MAX_NUM_B_FRAMES %d\n", + rc); + break; + } + + property_id = HAL_CONFIG_VENC_INTRA_PERIOD; + intra_period.pframes = num_p; + intra_period.bframes = num_b; + + /* + *Incase firmware does not have B-Frame support, + *offload the b-frame count to p-frame to make up + *for the requested Intraperiod + */ + if (!inst->capability.bframe.max) { + intra_period.pframes = num_p + num_b; + intra_period.bframes = 0; + dprintk(VIDC_DBG, + "No bframe support, changing pframe from %d to %d\n", + num_p, intra_period.pframes); + } + pdata = &intra_period; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME: + property_id = HAL_CONFIG_VENC_REQUEST_IFRAME; + request_iframe.enable = true; + pdata = &request_iframe; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL: + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + { + int final_mode = 0; + struct v4l2_ctrl update_ctrl = {.id = 0}; + + /* V4L2_CID_MPEG_VIDEO_BITRATE_MODE and _RATE_CONTROL + * manipulate the same thing. If one control's state + * changes, try to mirror the state in the other control's + * value + */ + if (ctrl->id == V4L2_CID_MPEG_VIDEO_BITRATE_MODE) { + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { + final_mode = HAL_RATE_CONTROL_VBR_CFR; + update_ctrl.val = + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR; + } else {/* ...if (ctrl->val == _BITRATE_MODE_CBR) */ + final_mode = HAL_RATE_CONTROL_CBR_CFR; + update_ctrl.val = + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR; + } + + update_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL; + + } else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL) { + switch (ctrl->val) { + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR: + update_ctrl.val = + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR: + update_ctrl.val = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + break; + } + + final_mode = ctrl->val; + update_ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE; + } + + if (update_ctrl.id) { + temp_ctrl = TRY_GET_CTRL(update_ctrl.id); + temp_ctrl->val = update_ctrl.val; + } + + property_id = HAL_PARAM_VENC_RATE_CONTROL; + property_val = final_mode; + pdata = &property_val; + + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE: + { + property_id = HAL_CONFIG_VENC_TARGET_BITRATE; + bitrate.bit_rate = ctrl->val; + bitrate.layer_id = 0; + pdata = &bitrate; + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + { + struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_BITRATE); + + if (ctrl->val < avg_bitrate->val) { + dprintk(VIDC_ERR, + "Peak bitrate (%d) is lower than average bitrate (%d)\n", + ctrl->val, avg_bitrate->val); + rc = -EINVAL; + break; + } else if (ctrl->val < avg_bitrate->val * 2) { + dprintk(VIDC_WARN, + "Peak bitrate (%d) ideally should be twice the average bitrate (%d)\n", + ctrl->val, avg_bitrate->val); + } + + property_id = HAL_CONFIG_VENC_MAX_BITRATE; + bitrate.bit_rate = ctrl->val; + bitrate.layer_id = 0; + pdata = &bitrate; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + temp_ctrl = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL); + + property_id = HAL_PARAM_VENC_H264_ENTROPY_CONTROL; + h264_entropy_control.entropy_mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, ctrl->val); + h264_entropy_control.cabac_model = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL, + temp_ctrl->val); + pdata = &h264_entropy_control; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE); + + property_id = HAL_PARAM_VENC_H264_ENTROPY_CONTROL; + h264_entropy_control.cabac_model = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, ctrl->val); + h264_entropy_control.entropy_mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL, + temp_ctrl->val); + pdata = &h264_entropy_control; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + dprintk(VIDC_DBG, "\nprofile: %d\n", + profile_level.profile); + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + dprintk(VIDC_DBG, "\nLevel: %d\n", + profile_level.level); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE, + ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL, + ctrl->val); + profile_level.level = HAL_VPX_PROFILE_UNUSED; + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE: + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.profile = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.level = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE); + + property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT; + profile_level.level = venc_v4l2_to_hal(ctrl->id, + ctrl->val); + profile_level.profile = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE, + temp_ctrl->val); + pdata = &profile_level; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION: + { + struct v4l2_ctrl *deinterlace = NULL; + + if (!(inst->capability.pixelprocess_capabilities & + HAL_VIDEO_ENCODER_ROTATION_CAPABILITY)) { + dprintk(VIDC_ERR, "Rotation not supported: %#x\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + deinterlace = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE); + if (ctrl->val && deinterlace && deinterlace->val != + V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED) { + dprintk(VIDC_ERR, + "Rotation not supported with deinterlacing\n"); + rc = -EINVAL; + break; + } + property_id = HAL_CONFIG_VPE_OPERATIONS; + operations.rotate = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_ROTATION, + ctrl->val); + operations.flip = HAL_FLIP_NONE; + pdata = &operations; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: { + struct v4l2_ctrl *qpp, *qpb; + + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) + break; + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpi = ctrl->val; + quantization.qpp = qpp->val; + quantization.qpb = qpb->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpb; + + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) + break; + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpp = ctrl->val; + quantization.qpi = qpi->val; + quantization.qpb = qpb->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpp; + + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) + break; + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP); + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpb = ctrl->val; + quantization.qpi = qpi->val; + quantization.qpp = qpp->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: { + struct v4l2_ctrl *qpp, *qpb; + + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpi = ctrl->val; + quantization.qpp = qpp->val; + quantization.qpb = qpb->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpb; + + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP); + qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpp = ctrl->val; + quantization.qpi = qpi->val; + quantization.qpb = qpb->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: { + struct v4l2_ctrl *qpi, *qpp; + + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP); + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpb = ctrl->val; + quantization.qpi = qpi->val; + quantization.qpp = qpp->val; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: { + struct v4l2_ctrl *qpp; + + qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpi = ctrl->val; + quantization.qpp = qpp->val; + /* Bframes are not supported for VPX */ + quantization.qpb = 0; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: { + struct v4l2_ctrl *qpi; + + qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP); + + property_id = HAL_PARAM_VENC_SESSION_QP; + quantization.qpp = ctrl->val; + quantization.qpi = qpi->val; + /* Bframes are not supported for VPX */ + quantization.qpb = 0; + quantization.layer_id = 0; + + pdata = &quantization; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP); + if (ctrl->val >= qp_max->val) { + dprintk(VIDC_ERR, + "Bad range: Min QP (%d) > Max QP(%d)\n", + ctrl->val, qp_max->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP); + if (ctrl->val <= qp_min->val) { + dprintk(VIDC_ERR, + "Bad range: Max QP (%d) < Min QP(%d)\n", + ctrl->val, qp_min->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP); + if (ctrl->val >= qp_max->val) { + dprintk(VIDC_ERR, + "Bad range: Min QP (%d) > Max QP(%d)\n", + ctrl->val, qp_max->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP); + if (ctrl->val <= qp_min->val) { + dprintk(VIDC_ERR, + "Bad range: Max QP (%d) < Min QP(%d)\n", + ctrl->val, qp_min->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP); + if (ctrl->val >= qp_max->val) { + dprintk(VIDC_ERR, + "Bad range: Min QP (%d) > Max QP(%d)\n", + ctrl->val, qp_max->val); + rc = -ERANGE; + break; + } + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP); + if (ctrl->val <= qp_min->val) { + dprintk(VIDC_ERR, + "Bad range: Max QP (%d) < Min QP(%d)\n", + ctrl->val, qp_min->val); + rc = -ERANGE; + break; + } + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP); + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP); + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED: { + struct v4l2_ctrl *qp_max; + + qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED); + if (ctrl->val >= qp_max->val) { + dprintk(VIDC_ERR, + "Bad range: Min QP PACKED (0x%x) > Max QP PACKED (0x%x)\n", + ctrl->val, qp_max->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED; + qp_range.layer_id = 0; + qp_range.max_qp = qp_max->val; + qp_range.min_qp = ctrl->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED: { + struct v4l2_ctrl *qp_min; + + qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED); + if (ctrl->val <= qp_min->val) { + dprintk(VIDC_ERR, + "Bad range: Max QP PACKED (%d) < Min QP PACKED (%d)\n", + ctrl->val, qp_min->val); + rc = -ERANGE; + break; + } + + property_id = HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED; + qp_range.layer_id = 0; + qp_range.max_qp = ctrl->val; + qp_range.min_qp = qp_min->val; + + pdata = &qp_range; + break; + } + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: { + int temp = 0; + + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: + temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB; + break; + case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: + temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES; + break; + case V4L2_MPEG_VIDEO_MULTI_SLICE_GOB: + temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB; + break; + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: + default: + temp = 0; + break; + } + + if (temp) + temp_ctrl = TRY_GET_CTRL(temp); + + property_id = HAL_PARAM_VENC_MULTI_SLICE_CONTROL; + multi_slice_control.multi_slice = ctrl->val; + multi_slice_control.slice_size = temp ? temp_ctrl->val : 0; + + pdata = &multi_slice_control; + break; + } + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB: + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + + property_id = HAL_PARAM_VENC_MULTI_SLICE_CONTROL; + multi_slice_control.multi_slice = temp_ctrl->val; + multi_slice_control.slice_size = ctrl->val; + pdata = &multi_slice_control; + break; + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE: { + bool codec_avc = + inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264 || + inst->fmts[CAPTURE_PORT].fourcc == + V4L2_PIX_FMT_H264_NO_SC; + + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE); + if (codec_avc && temp_ctrl->val == + V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + property_id = HAL_PARAM_VENC_SLICE_DELIVERY_MODE; + enable.enable = true; + } else { + dprintk(VIDC_WARN, + "Failed : slice delivery mode is not supported\n"); + enable.enable = false; + } + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE: { + struct v4l2_ctrl *air_mbs, *air_ref, *cir_mbs; + bool is_cont_intra_supported = false; + + air_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS); + air_ref = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF); + cir_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS); + + is_cont_intra_supported = + (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264) || + (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_HEVC); + + if (is_cont_intra_supported) { + if (ctrl->val != HAL_INTRA_REFRESH_NONE) + enable.enable = true; + else + enable.enable = false; + + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, + HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, &enable); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set constrained intra\n"); + rc = -EINVAL; + break; + } + } + + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + intra_refresh.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE, + ctrl->val); + + intra_refresh.air_mbs = air_mbs->val; + intra_refresh.air_ref = air_ref->val; + intra_refresh.cir_mbs = cir_mbs->val; + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS: { + struct v4l2_ctrl *ir_mode, *air_ref, *cir_mbs; + + ir_mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE); + air_ref = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF); + cir_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS); + + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + + intra_refresh.air_mbs = ctrl->val; + intra_refresh.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE, + ir_mode->val); + intra_refresh.air_ref = air_ref->val; + intra_refresh.cir_mbs = cir_mbs->val; + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF: { + struct v4l2_ctrl *ir_mode, *air_mbs, *cir_mbs; + + ir_mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE); + air_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS); + cir_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS); + + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + + intra_refresh.air_ref = ctrl->val; + intra_refresh.air_mbs = air_mbs->val; + intra_refresh.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE, + ir_mode->val); + intra_refresh.cir_mbs = cir_mbs->val; + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS: { + struct v4l2_ctrl *ir_mode, *air_mbs, *air_ref; + + ir_mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE); + air_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS); + air_ref = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF); + + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + + intra_refresh.cir_mbs = ctrl->val; + intra_refresh.air_mbs = air_mbs->val; + intra_refresh.air_ref = air_ref->val; + intra_refresh.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE, + ir_mode->val); + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: { + struct v4l2_ctrl *air_mbs, *air_ref; + + air_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS); + air_ref = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF); + + property_id = HAL_PARAM_VENC_INTRA_REFRESH; + + intra_refresh.cir_mbs = ctrl->val; + intra_refresh.air_mbs = air_mbs->val; + intra_refresh.air_ref = air_ref->val; + intra_refresh.mode = HAL_INTRA_REFRESH_CYCLIC; + + pdata = &intra_refresh; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: + { + struct v4l2_ctrl *alpha, *beta; + + alpha = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA); + beta = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA); + + property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL; + h264_db_control.slice_alpha_offset = alpha->val; + h264_db_control.slice_beta_offset = beta->val; + h264_db_control.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + ctrl->val); + pdata = &h264_db_control; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: + { + struct v4l2_ctrl *mode, *beta; + + mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE); + beta = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA); + + property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL; + h264_db_control.slice_alpha_offset = ctrl->val; + h264_db_control.slice_beta_offset = beta->val; + h264_db_control.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + mode->val); + pdata = &h264_db_control; + break; + } + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: + { + struct v4l2_ctrl *mode, *alpha; + + mode = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE); + alpha = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA); + property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL; + h264_db_control.slice_alpha_offset = alpha->val; + h264_db_control.slice_beta_offset = ctrl->val; + h264_db_control.mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, + mode->val); + pdata = &h264_db_control; + break; + } + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + property_id = HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER; + + switch (ctrl->val) { + case V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE: + enable.enable = false; + break; + case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME: + enable.enable = true; + break; + case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME: + default: + rc = -ENOTSUPP; + break; + } + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE: + inst->flags |= VIDC_SECURE; + dprintk(VIDC_INFO, "Setting secure mode to: %d\n", + !!(inst->flags & VIDC_SECURE)); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA: + property_id = HAL_PARAM_INDEX_EXTRADATA; + extra.index = msm_comm_get_hal_extradata_index(ctrl->val); + extra.enable = 1; + pdata = &extra; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO: + { + struct v4l2_ctrl *rc_mode; + bool cfr = false; + + property_id = HAL_PARAM_VENC_H264_VUI_TIMING_INFO; + rc_mode = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL); + + switch (rc_mode->val) { + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR: + cfr = true; + break; + default: + cfr = false; + break; + } + + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED: + vui_timing_info.enable = 0; + break; + case V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED: + vui_timing_info.enable = 1; + vui_timing_info.fixed_frame_rate = cfr; + vui_timing_info.time_scale = NSEC_PER_SEC; + } + + pdata = &vui_timing_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER: + property_id = HAL_PARAM_VENC_GENERATE_AUDNAL; + + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED: + enable.enable = false; + break; + case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED: + enable.enable = true; + break; + default: + rc = -ENOTSUPP; + break; + } + + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL: + switch (ctrl->val) { + case V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL: + if (inst->flags & VIDC_TURBO) { + inst->flags &= ~VIDC_TURBO; + msm_dcvs_init_load(inst); + } + break; + case V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO: + inst->flags |= VIDC_TURBO; + break; + default: + dprintk(VIDC_ERR, "Perf mode %x not supported\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + msm_comm_scale_clocks_and_bus(inst); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT: + property_id = HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC; + vui_bitstream_restrict.enable = ctrl->val; + pdata = &vui_bitstream_restrict; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY: + property_id = HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY; + preserve_text_quality.enable = ctrl->val; + pdata = &preserve_text_quality; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MPEG4_TIME_RESOLUTION: + property_id = HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION; + time_res.time_increment_resolution = ctrl->val; + pdata = &time_res; + break; + + case V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE: + { + struct v4l2_ctrl *rotation = NULL; + + if (!(inst->capability.pixelprocess_capabilities & + HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY)) { + dprintk(VIDC_ERR, "Deinterlace not supported: %#x\n", + ctrl->id); + rc = -ENOTSUPP; + break; + } + rotation = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_ROTATION); + if (ctrl->val && rotation && rotation->val != + V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE) { + dprintk(VIDC_ERR, + "Deinterlacing not supported with rotation"); + rc = -EINVAL; + break; + } + property_id = HAL_CONFIG_VPE_DEINTERLACE; + switch (ctrl->val) { + case V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED: + enable.enable = true; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED: + default: + enable.enable = false; + break; + } + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_SEQ_HEADER: + atomic_inc(&inst->seq_hdr_reqs); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME: + property_id = HAL_CONFIG_VENC_USELTRFRAME; + use_ltr.ref_ltr = ctrl->val; + use_ltr.use_constraint = true; + use_ltr.frames = 0; + pdata = &use_ltr; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME: + if (ctrl->val < inst->capability.ltr_count.min || + ctrl->val >= inst->capability.ltr_count.max) { + dprintk(VIDC_ERR, + "Error setting markltr %d range: [%d,%d)\n", + ctrl->val, inst->capability.ltr_count.min, + inst->capability.ltr_count.max); + rc = -ENOTSUPP; + break; + } + property_id = HAL_CONFIG_VENC_MARKLTRFRAME; + mark_ltr.mark_frame = ctrl->val; + pdata = &mark_ltr; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS: + property_id = HAL_CONFIG_VENC_HIER_P_NUM_FRAMES; + hier_p_layers = ctrl->val; + if (hier_p_layers > inst->capability.hier_p.max) { + dprintk(VIDC_ERR, + "Error setting hier p num layers %d max supported is %d\n", + hier_p_layers, inst->capability.hier_p.max); + rc = -ENOTSUPP; + break; + } + pdata = &hier_p_layers; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE: + property_id = HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP; + enable.enable = (ctrl->val == + V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_IGNORE); + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE: + property_id = HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE; + enable.enable = ctrl->val; + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC: + property_id = HAL_PARAM_VENC_H264_NAL_SVC_EXT; + enable.enable = ctrl->val; + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PERF_MODE: + property_id = HAL_CONFIG_VENC_PERF_MODE; + + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_PERF_POWER_SAVE: + inst->flags |= VIDC_LOW_POWER; + venc_mode = HAL_PERF_MODE_POWER_SAVE; + break; + case V4L2_MPEG_VIDC_VIDEO_PERF_MAX_QUALITY: + inst->flags &= ~VIDC_LOW_POWER; + venc_mode = HAL_PERF_MODE_POWER_MAX_QUALITY; + break; + default: + dprintk(VIDC_ERR, "Power save mode %x not supported\n", + ctrl->val); + rc = -ENOTSUPP; + property_id = 0; + break; + } + pdata = &venc_mode; + + msm_dcvs_enc_set_power_save_mode(inst, + ctrl->val == V4L2_MPEG_VIDC_VIDEO_PERF_POWER_SAVE); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS: + if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) { + dprintk(VIDC_ERR, "Hier B supported for HEVC only\n"); + rc = -ENOTSUPP; + break; + } + property_id = HAL_PARAM_VENC_HIER_B_MAX_ENH_LAYERS; + hier_b_layers = ctrl->val; + pdata = &hier_b_layers; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE: + property_id = HAL_PARAM_VENC_HIER_P_HYBRID_MODE; + hyb_hierp.layers = ctrl->val; + pdata = &hyb_hierp; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MBI_STATISTICS_MODE: + property_id = HAL_PARAM_VENC_MBI_STATISTICS_MODE; + mbi_statistics_mode = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_MBI_STATISTICS_MODE, + ctrl->val); + pdata = &mbi_statistics_mode; + break; + case V4L2_CID_VIDC_QBUF_MODE: + property_id = HAL_PARAM_SYNC_BASED_INTERRUPT; + enable.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED; + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS: + property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS; + max_hierp_layers = ctrl->val; + if (max_hierp_layers > inst->capability.hier_p.max) { + dprintk(VIDC_ERR, + "Error max HP layers(%d)>max supported(%d)\n", + max_hierp_layers, inst->capability.hier_p.max); + rc = -ENOTSUPP; + break; + } + pdata = &max_hierp_layers; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID: + property_id = HAL_CONFIG_VENC_BASELAYER_PRIORITYID; + baselayerid = ctrl->val; + pdata = &baselayerid; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_CONFIG_QP: + /* Sanity check for the QP boundaries as we are using + * same control to set dynamic QP for all the codecs + */ + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid QP Config QP Range\n"); + break; + } + property_id = HAL_CONFIG_VENC_FRAME_QP; + frameqp = ctrl->val; + pdata = &frameqp; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP: + { + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial I QP\n"); + break; + } + /* + * Defer sending property from here, set_ext_ctrl + * will send it based on the rc value. + */ + property_id = 0; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP: + { + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial B QP\n"); + break; + } + /* + * Defer sending property from here, set_ext_ctrl + * will send it based on the rc value. + */ + property_id = 0; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP: + { + rc = msm_venc_validate_qp_value(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Invalid Initial P QP\n"); + break; + } + /* + * Defer sending property from here, set_ext_ctrl + * will send it based on the rc value. + */ + property_id = 0; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI: + property_id = HAL_PARAM_VENC_VQZIP_SEI; + enable.enable = ctrl->val; + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY: + property_id = HAL_CONFIG_REALTIME; + /* firmware has inverted values for realtime and + * non-realtime priority + */ + enable.enable = !(ctrl->val); + pdata = &enable; + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE: + inst->flags &= ~VIDC_REALTIME; + break; + case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE: + inst->flags |= VIDC_REALTIME; + break; + default: + dprintk(VIDC_WARN, + "inst(%pK) invalid priority ctrl value %#x\n", + inst, ctrl->val); + break; + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE: + dprintk(VIDC_DBG, + "inst(%pK) operating rate changed from %d to %d\n", + inst, inst->prop.operating_rate >> 16, + ctrl->val >> 16); + inst->prop.operating_rate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE: + { + property_id = HAL_PARAM_VENC_BITRATE_TYPE; + enable.enable = ctrl->val; + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_H264_PIC_ORDER_CNT: + { + property_id = HAL_PARAM_VENC_H264_PIC_ORDER_CNT; + pic_order_cnt = ctrl->val; + pdata = &pic_order_cnt; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE: + { + signal_info.color_space = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE); + signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS); + signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS); + signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE: + { + signal_info.full_range = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS); + signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS); + signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS: + { + signal_info.transfer_chars = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE); + signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS); + signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS: + { + signal_info.matrix_coeffs = ctrl->val; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE); + signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = + TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS); + signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0; + temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE); + signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0; + property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO; + pdata = &signal_info; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC: + if (ctrl->val == V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE) { + rc = msm_venc_set_csc(inst); + if (rc) + dprintk(VIDC_ERR, "fail to set csc: %d\n", rc); + } + break; + case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE: + { + property_id = HAL_PARAM_VENC_LOW_LATENCY; + if (ctrl->val == + V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE) + enable.enable = true; + else + enable.enable = false; + pdata = &enable; + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8: + property_id = HAL_PARAM_VENC_H264_TRANSFORM_8x8; + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE: + enable.enable = true; + break; + case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE: + enable.enable = false; + break; + default: + dprintk(VIDC_ERR, + "Invalid H264 8x8 transform control value %d\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + pdata = &enable; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE: + property_id = HAL_PARAM_VENC_IFRAMESIZE_TYPE; + iframesize_type = venc_v4l2_to_hal( + V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE, + ctrl->val); + pdata = &iframesize_type; + break; + default: + dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id); + rc = -ENOTSUPP; + break; + } + + v4l2_ctrl_lock(ctrl); + + if (!rc && property_id) { + dprintk(VIDC_DBG, "Control: HAL property=%x,ctrl_value=%d\n", + property_id, + ctrl->val); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + } + + return rc; +} +#undef TRY_GET_CTRL + +static int try_set_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0, i; + struct v4l2_ext_control *control; + struct hfi_device *hdev; + struct hal_ltr_mode ltr_mode; + struct hal_vc1e_perf_cfg_type search_range = { {0} }; + u32 property_id = 0; + void *pdata = NULL; + struct msm_vidc_capability *cap = NULL; + struct hal_initial_quantization quant; + struct hal_aspect_ratio sar; + struct hal_bitrate bitrate; + struct hal_frame_size blur_res; + struct v4l2_control temp_ctrl; + + if (!inst || !inst->core || !inst->core->device || !ctrl) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + cap = &inst->capability; + + control = ctrl->controls; + for (i = 0; i < ctrl->count; i++) { + switch (control[i].id) { + case V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE: + if (control[i].value != + V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE) { + rc = msm_venc_toggle_hier_p(inst, false); + if (rc) + break; + } + ltr_mode.mode = control[i].value; + ltr_mode.trust_mode = 1; + property_id = HAL_PARAM_VENC_LTRMODE; + pdata = <r_mode; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT: + ltr_mode.count = control[i].value; + if (ltr_mode.count > cap->ltr_count.max) { + dprintk(VIDC_ERR, + "Invalid LTR count %d. Supported max: %d\n", + ltr_mode.count, + cap->ltr_count.max); + /* + * FIXME: Return an error (-EINVALID) + * here once VP8 supports LTR count + * capability + */ + ltr_mode.count = 1; + } + ltr_mode.trust_mode = 1; + property_id = HAL_PARAM_VENC_LTRMODE; + pdata = <r_mode; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP: + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + quant.init_qp_enable = control[i].value; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP: + quant.qpi = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP: + quant.qpp = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP: + quant.qpb = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP: + /* Sanity check for the QP boundaries as we are using + * same control to set Initial QP for all the codecs + */ + temp_ctrl.id = + V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP; + temp_ctrl.value = control[i].value; + + rc = msm_comm_s_ctrl(inst, &temp_ctrl); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting Initial I Frame QP : %d\n", + __func__, rc); + break; + } + quant.qpi = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP: + temp_ctrl.id = + V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP; + temp_ctrl.value = control[i].value; + rc = msm_comm_s_ctrl(inst, &temp_ctrl); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting Initial P Frame QP : %d\n", + __func__, rc); + break; + } + quant.qpp = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP: + temp_ctrl.id = + V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP; + temp_ctrl.value = control[i].value; + rc = msm_comm_s_ctrl(inst, &temp_ctrl); + if (rc) { + dprintk(VIDC_ERR, + "%s Failed setting Initial B Frame QP : %d\n", + __func__, rc); + break; + } + quant.qpb = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE: + search_range.i_frame.x_subsampled = control[i].value; + property_id = HAL_PARAM_VENC_SEARCH_RANGE; + pdata = &search_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE: + search_range.i_frame.y_subsampled = control[i].value; + property_id = HAL_PARAM_VENC_SEARCH_RANGE; + pdata = &search_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE: + search_range.p_frame.x_subsampled = control[i].value; + property_id = HAL_PARAM_VENC_SEARCH_RANGE; + pdata = &search_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE: + search_range.p_frame.y_subsampled = control[i].value; + property_id = HAL_PARAM_VENC_SEARCH_RANGE; + pdata = &search_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE: + search_range.b_frame.x_subsampled = control[i].value; + property_id = HAL_PARAM_VENC_SEARCH_RANGE; + pdata = &search_range; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE: + search_range.b_frame.y_subsampled = control[i].value; + property_id = HAL_PARAM_VENC_SEARCH_RANGE; + pdata = &search_range; + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH: + sar.aspect_width = control[i].value; + property_id = HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO; + pdata = &sar; + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT: + sar.aspect_height = control[i].value; + property_id = HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO; + pdata = &sar; + break; + case V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE: + { + if (control[i].value) { + bitrate.layer_id = i; + bitrate.bit_rate = control[i].value; + property_id = HAL_CONFIG_VENC_TARGET_BITRATE; + pdata = &bitrate; + dprintk(VIDC_DBG, "bitrate for layer(%d)=%d\n", + i, bitrate.bit_rate); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, + pdata); + if (rc) { + dprintk(VIDC_DBG, "prop %x failed\n", + property_id); + return rc; + } + if (i == MAX_HYBRID_HIER_P_LAYERS - 1) { + dprintk(VIDC_DBG, "HAL property=%x\n", + property_id); + property_id = 0; + rc = 0; + } + } + break; + } + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH: + property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION; + blur_res.width = control[i].value; + blur_res.buffer_type = HAL_BUFFER_INPUT; + property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION; + pdata = &blur_res; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT: + blur_res.height = control[i].value; + blur_res.buffer_type = HAL_BUFFER_INPUT; + property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION; + pdata = &blur_res; + break; + default: + dprintk(VIDC_ERR, "Invalid id set: %d\n", + control[i].id); + rc = -ENOTSUPP; + break; + } + if (rc) + break; + } + + if (!rc && property_id) { + dprintk(VIDC_DBG, "Control: HAL property=%x\n", property_id); + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + } + return rc; +} + +static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + + int rc = 0, c = 0; + + struct msm_vidc_inst *inst = container_of(ctrl->handler, + struct msm_vidc_inst, ctrl_handler); + + if (!inst) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to start done state\n", inst); + goto failed_open_done; + } + + for (c = 0; c < ctrl->ncontrols; ++c) { + if (ctrl->cluster[c]->is_new) { + struct v4l2_ctrl *temp = ctrl->cluster[c]; + + rc = try_set_ctrl(inst, temp); + if (rc) { + dprintk(VIDC_ERR, "Failed setting %s (%x)\n", + v4l2_ctrl_get_name(temp->id), + temp->id); + break; + } + } + } +failed_open_done: + if (rc) + dprintk(VIDC_ERR, "Failed setting control: %x (%s)", + ctrl->id, v4l2_ctrl_get_name(ctrl->id)); + return rc; +} + +static int msm_venc_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + return 0; +} + +static const struct v4l2_ctrl_ops msm_venc_ctrl_ops = { + + .s_ctrl = msm_venc_op_s_ctrl, + .g_volatile_ctrl = msm_venc_op_g_volatile_ctrl, +}; + +const struct v4l2_ctrl_ops *msm_venc_get_ctrl_ops(void) +{ + return &msm_venc_ctrl_ops; +} + +int msm_venc_inst_init(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid input = %pK\n", inst); + return -EINVAL; + } + inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT; + inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH; + inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT; + inst->prop.width[OUTPUT_PORT] = DEFAULT_WIDTH; + inst->capability.height.min = MIN_SUPPORTED_HEIGHT; + inst->capability.height.max = DEFAULT_HEIGHT; + inst->capability.width.min = MIN_SUPPORTED_WIDTH; + inst->capability.width.max = DEFAULT_WIDTH; + inst->capability.alloc_mode_in = HAL_BUFFER_MODE_STATIC; + inst->capability.alloc_mode_out = HAL_BUFFER_MODE_STATIC; + inst->capability.secure_output2_threshold.min = 0; + inst->capability.secure_output2_threshold.max = 0; + inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC; + inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC; + inst->prop.fps = DEFAULT_FPS; + inst->prop.operating_rate = 0; + inst->capability.pixelprocess_capabilities = 0; + memcpy(&inst->fmts[CAPTURE_PORT], &venc_formats[4], + sizeof(struct msm_vidc_format)); + memcpy(&inst->fmts[OUTPUT_PORT], &venc_formats[0], + sizeof(struct msm_vidc_format)); + return rc; +} + +int msm_venc_s_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0; + + rc = try_set_ext_ctrl(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Error setting extended control\n"); + return rc; + } + return rc; +} + +int msm_venc_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap) +{ + if (!inst || !cap) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, cap = %pK\n", inst, cap); + return -EINVAL; + } + strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->card, MSM_VENC_DVC_NAME, sizeof(cap->card)); + cap->bus_info[0] = 0; + //cap->version = MSM_VIDC_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_STREAMING; + memset(cap->reserved, 0, sizeof(cap->reserved)); + return 0; +} + +int msm_venc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f) +{ + const struct msm_vidc_format *fmt = NULL; + int rc = 0; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, f = %pK\n", inst, f); + return -EINVAL; + } + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(venc_formats, + ARRAY_SIZE(venc_formats), f->index, CAPTURE_PORT); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fmt = msm_comm_get_pixel_fmt_index(venc_formats, + ARRAY_SIZE(venc_formats), f->index, OUTPUT_PORT); + f->flags = V4L2_FMT_FLAG_COMPRESSED; + } + + memset(f->reserved, 0, sizeof(f->reserved)); + if (fmt) { + strlcpy(f->description, fmt->description, + sizeof(f->description)); + f->pixelformat = fmt->fourcc; + } else { + dprintk(VIDC_DBG, "No more formats found\n"); + rc = -EINVAL; + } + return rc; +} + +static int msm_venc_set_csc(struct msm_vidc_inst *inst) +{ + int rc = 0; + int count = 0; + struct hal_vpe_color_space_conversion vpe_csc; + + while (count < HAL_MAX_MATRIX_COEFFS) { + if (count < HAL_MAX_BIAS_COEFFS) + vpe_csc.csc_bias[count] = + vpe_csc_601_to_709_bias_coeff[count]; + if (count < HAL_MAX_LIMIT_COEFFS) + vpe_csc.csc_limit[count] = + vpe_csc_601_to_709_limit_coeff[count]; + vpe_csc.csc_matrix[count] = + vpe_csc_601_to_709_matrix_coeff[count]; + count = count + 1; + } + rc = msm_comm_try_set_prop(inst, + HAL_PARAM_VPE_COLOR_SPACE_CONVERSION, &vpe_csc); + if (rc) + dprintk(VIDC_ERR, "Setting VPE coefficients failed\n"); + + return rc; +} + +int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + struct msm_vidc_format *fmt = NULL; + int rc = 0; + int i; + struct hfi_device *hdev; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, format = %pK\n", inst, f); + return -EINVAL; + } + + if (!inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, + ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat, + CAPTURE_PORT); + if (!fmt || fmt->type != CAPTURE_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on CAPTURE port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto exit; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + msm_venc_update_plane_count(inst, CAPTURE_PORT); + fmt->num_planes = inst->fmts[CAPTURE_PORT].num_planes; + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + goto exit; + } + + inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width; + inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height; + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto exit; + } + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct hal_frame_size frame_sz; + + inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; + inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; + + rc = msm_vidc_check_session_supported(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: session not supported\n", __func__); + goto exit; + } + + frame_sz.buffer_type = HAL_BUFFER_INPUT; + frame_sz.width = inst->prop.width[OUTPUT_PORT]; + frame_sz.height = inst->prop.height[OUTPUT_PORT]; + dprintk(VIDC_DBG, "width = %d, height = %d\n", + frame_sz.width, frame_sz.height); + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set framesize for Output port\n"); + goto exit; + } + + fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats, + ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat, + OUTPUT_PORT); + if (!fmt || fmt->type != OUTPUT_PORT) { + dprintk(VIDC_ERR, + "Format: %d not supported on OUTPUT port\n", + f->fmt.pix_mp.pixelformat); + rc = -EINVAL; + goto exit; + } + memcpy(&inst->fmts[fmt->type], fmt, + sizeof(struct msm_vidc_format)); + + msm_venc_update_plane_count(inst, OUTPUT_PORT); + fmt->num_planes = inst->fmts[OUTPUT_PORT].num_planes; + + msm_comm_set_color_format(inst, HAL_BUFFER_INPUT, fmt->fourcc); + } else { + dprintk(VIDC_ERR, "%s - Unsupported buf type: %d\n", + __func__, f->type); + rc = -EINVAL; + goto exit; + } + + f->fmt.pix_mp.num_planes = fmt->num_planes; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + struct hal_frame_size frame_sz = {0}; + struct hal_buffer_requirements *bufreq = NULL; + + frame_sz.width = inst->prop.width[CAPTURE_PORT]; + frame_sz.height = inst->prop.height[CAPTURE_PORT]; + frame_sz.buffer_type = HAL_BUFFER_OUTPUT; + rc = call_hfi_op(hdev, session_set_property, (void *) + inst->session, HAL_PARAM_FRAME_SIZE, + &frame_sz); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set OUTPUT framesize\n"); + goto exit; + } + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_WARN, + "%s : Getting buffer reqs failed: %d\n", + __func__, rc); + goto exit; + } + bufreq = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + f->fmt.pix_mp.plane_fmt[0].sizeimage = + bufreq ? bufreq->buffer_size : 0; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct hal_buffer_requirements *bufreq = NULL; + int extra_idx = 0; + + for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) { + f->fmt.pix_mp.plane_fmt[i].sizeimage = + inst->fmts[fmt->type].get_frame_size(i, + f->fmt.pix_mp.height, f->fmt.pix_mp.width); + } + extra_idx = EXTRADATA_IDX(inst->fmts[fmt->type].num_planes); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_INPUT); + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + bufreq ? bufreq->buffer_size : 0; + } + } +exit: + return rc; +} + +int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) +{ + const struct msm_vidc_format *fmt = NULL; + int rc = 0; + int i; + u32 height, width, num_planes; + unsigned int extra_idx = 0; + struct hal_buffer_requirements *bufreq = NULL; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, format = %pK\n", inst, f); + return -EINVAL; + } + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_WARN, "Getting buffer requirements failed: %d\n", + rc); + return rc; + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt = &inst->fmts[CAPTURE_PORT]; + height = inst->prop.height[CAPTURE_PORT]; + width = inst->prop.width[CAPTURE_PORT]; + msm_venc_update_plane_count(inst, CAPTURE_PORT); + num_planes = inst->fmts[CAPTURE_PORT].num_planes; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fmt = &inst->fmts[OUTPUT_PORT]; + height = inst->prop.height[OUTPUT_PORT]; + width = inst->prop.width[OUTPUT_PORT]; + msm_venc_update_plane_count(inst, OUTPUT_PORT); + num_planes = inst->fmts[OUTPUT_PORT].num_planes; + } else { + dprintk(VIDC_ERR, "Invalid type: %x\n", f->type); + return -ENOTSUPP; + } + + f->fmt.pix_mp.pixelformat = fmt->fourcc; + f->fmt.pix_mp.height = height; + f->fmt.pix_mp.width = width; + f->fmt.pix_mp.num_planes = num_planes; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + for (i = 0; i < num_planes; ++i) { + f->fmt.pix_mp.plane_fmt[i].sizeimage = + fmt->get_frame_size(i, height, width); + } + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_OUTPUT); + + f->fmt.pix_mp.plane_fmt[0].sizeimage = + bufreq ? bufreq->buffer_size : 0; + } + extra_idx = EXTRADATA_IDX(num_planes); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_OUTPUT); + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_INPUT); + + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + bufreq ? bufreq->buffer_size : 0; + } + + for (i = 0; i < num_planes; ++i) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + inst->bufq[OUTPUT_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + inst->bufq[CAPTURE_PORT].plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + } + return rc; +} + +int msm_venc_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b) +{ + struct buf_queue *q = NULL; + int rc = 0; + + if (!inst || !b) { + dprintk(VIDC_ERR, + "Invalid input, inst = %pK, buffer = %pK\n", inst, b); + return -EINVAL; + } + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", b->type); + return -EINVAL; + } + + mutex_lock(&q->lock); + rc = vb2_reqbufs(&q->vb2_bufq, b); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_DBG, "Failed to get reqbufs, %d\n", rc); + return rc; +} + +int msm_venc_prepare_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b) +{ + int rc = 0; + int i; + struct vidc_buffer_addr_info buffer_info = {0}; + struct hfi_device *hdev; + int extra_idx = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core %pK in bad state, ignoring prepare buf\n", + inst->core); + goto exit; + } + + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (b->length != inst->fmts[CAPTURE_PORT].num_planes) { + dprintk(VIDC_ERR, + "Planes mismatch: needed: %d, allocated: %d\n", + inst->fmts[CAPTURE_PORT].num_planes, + b->length); + rc = -EINVAL; + break; + } + + for (i = 0; i < min_t(int, b->length, VIDEO_MAX_PLANES); i++) { + dprintk(VIDC_DBG, "device_addr = %#lx, size = %d\n", + b->m.planes[i].m.userptr, + b->m.planes[i].length); + } + buffer_info.buffer_size = b->m.planes[0].length; + buffer_info.buffer_type = HAL_BUFFER_OUTPUT; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = + b->m.planes[0].m.userptr; + + extra_idx = EXTRADATA_IDX(b->length); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) { + buffer_info.extradata_addr = + b->m.planes[extra_idx].m.userptr; + dprintk(VIDC_DBG, "extradata: %#lx\n", + b->m.planes[extra_idx].m.userptr); + buffer_info.extradata_size = + b->m.planes[extra_idx].length; + } + + 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"); + break; + default: + dprintk(VIDC_ERR, + "Buffer type not recognized: %d\n", b->type); + break; + } +exit: + return rc; +} + +int msm_venc_release_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b) +{ + int i, rc = 0, extra_idx = 0; + struct vidc_buffer_addr_info buffer_info = {0}; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to release res done state\n", + inst); + goto exit; + } + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: { + if (b->length != + inst->fmts[CAPTURE_PORT].num_planes) { + dprintk(VIDC_ERR, + "Planes mismatch: needed: %d, to release: %d\n", + inst->fmts[CAPTURE_PORT].num_planes, + b->length); + rc = -EINVAL; + break; + } + for (i = 0; i < b->length; i++) { + dprintk(VIDC_DBG, + "Release device_addr = %#lx, size = %d, %d\n", + b->m.planes[i].m.userptr, + b->m.planes[i].length, inst->state); + } + buffer_info.buffer_size = b->m.planes[0].length; + buffer_info.buffer_type = HAL_BUFFER_OUTPUT; + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = + b->m.planes[0].m.userptr; + extra_idx = EXTRADATA_IDX(b->length); + if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) + buffer_info.extradata_addr = + b->m.planes[extra_idx].m.userptr; + buffer_info.response_required = false; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (rc) + dprintk(VIDC_ERR, + "vidc_hal_session_release_buffers failed\n"); + } + break; + default: + dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type); + break; + } +exit: + return rc; +} + +int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + struct buf_queue *q = NULL; + int rc = 0; + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", b->type); + return -EINVAL; + } + mutex_lock(&q->lock); + rc = vb2_qbuf(&q->vb2_bufq, b); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_ERR, "Failed to qbuf, %d\n", rc); + return rc; +} + +int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + struct buf_queue *q = NULL; + int rc = 0; + + q = msm_comm_get_vb2q(inst, b->type); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", b->type); + return -EINVAL; + } + mutex_lock(&q->lock); + rc = vb2_dqbuf(&q->vb2_bufq, b, true); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc); + return rc; +} + +int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i) +{ + int rc = 0; + struct buf_queue *q; + + q = msm_comm_get_vb2q(inst, i); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", i); + return -EINVAL; + } + dprintk(VIDC_DBG, "Calling streamon\n"); + mutex_lock(&q->lock); + rc = vb2_streamon(&q->vb2_bufq, i); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_ERR, "streamon failed on port: %d\n", i); + return rc; +} + +int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i) +{ + int rc = 0; + struct buf_queue *q; + + q = msm_comm_get_vb2q(inst, i); + if (!q) { + dprintk(VIDC_ERR, + "Failed to find buffer queue for type = %d\n", i); + return -EINVAL; + } + dprintk(VIDC_DBG, "Calling streamoff on port: %d\n", i); + mutex_lock(&q->lock); + rc = vb2_streamoff(&q->vb2_bufq, i); + mutex_unlock(&q->lock); + if (rc) + dprintk(VIDC_ERR, "streamoff failed on port: %d\n", i); + return rc; +} + +int msm_venc_ctrl_init(struct msm_vidc_inst *inst) +{ + return msm_comm_ctrl_init(inst, msm_venc_ctrls, + ARRAY_SIZE(msm_venc_ctrls), &msm_venc_ctrl_ops); +} diff --git a/drivers/media/platform/msm/vidc_3x/msm_venc.h b/drivers/media/platform/msm/vidc_3x/msm_venc.h new file mode 100644 index 000000000000..857e5a0b59c6 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_venc.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2015, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MSM_VENC_H_ +#define _MSM_VENC_H_ + +#include +#include "msm_vidc_internal.h" + +int msm_venc_inst_init(struct msm_vidc_inst *inst); +int msm_venc_ctrl_init(struct msm_vidc_inst *inst); +int msm_venc_querycap(void *instance, struct v4l2_capability *cap); +int msm_venc_enum_fmt(void *instance, struct v4l2_fmtdesc *f); +int msm_venc_s_fmt(void *instance, struct v4l2_format *f); +int msm_venc_g_fmt(void *instance, struct v4l2_format *f); +int msm_venc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); +int msm_venc_reqbufs(void *instance, struct v4l2_requestbuffers *b); +int msm_venc_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_venc_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); +int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i); +int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i); +int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc); +int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a); +struct vb2_ops *msm_venc_get_vb2q_ops(void); + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc.c b/drivers/media/platform/msm/vidc_3x/msm_vidc.c new file mode 100644 index 000000000000..eb5cda963c54 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc.c @@ -0,0 +1,1498 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include "msm_vidc_internal.h" +#include "msm_vidc_debug.h" +#include "msm_vdec.h" +#include "msm_venc.h" +#include "msm_vidc_common.h" +#include +#include "vidc_hfi_api.h" +#include "msm_vidc_dcvs.h" + +#define MAX_EVENTS 30 + +static int get_poll_flags(void *instance) +{ + struct msm_vidc_inst *inst = instance; + struct vb2_queue *outq = &inst->bufq[OUTPUT_PORT].vb2_bufq; + struct vb2_queue *capq = &inst->bufq[CAPTURE_PORT].vb2_bufq; + struct vb2_buffer *out_vb = NULL; + struct vb2_buffer *cap_vb = NULL; + unsigned long flags; + int rc = 0; + + if (v4l2_event_pending(&inst->event_handler)) + rc |= POLLPRI; + + spin_lock_irqsave(&capq->done_lock, flags); + if (!list_empty(&capq->done_list)) + cap_vb = list_first_entry(&capq->done_list, struct vb2_buffer, + done_entry); + if (cap_vb && (cap_vb->state == VB2_BUF_STATE_DONE + || cap_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLIN | POLLRDNORM; + spin_unlock_irqrestore(&capq->done_lock, flags); + + spin_lock_irqsave(&outq->done_lock, flags); + if (!list_empty(&outq->done_list)) + out_vb = list_first_entry(&outq->done_list, struct vb2_buffer, + done_entry); + if (out_vb && (out_vb->state == VB2_BUF_STATE_DONE + || out_vb->state == VB2_BUF_STATE_ERROR)) + rc |= POLLOUT | POLLWRNORM; + spin_unlock_irqrestore(&outq->done_lock, flags); + + return rc; +} + +int msm_vidc_poll(void *instance, struct file *filp, + struct poll_table_struct *wait) +{ + struct msm_vidc_inst *inst = instance; + struct vb2_queue *outq = NULL; + struct vb2_queue *capq = NULL; + + if (!inst) + return -EINVAL; + + outq = &inst->bufq[OUTPUT_PORT].vb2_bufq; + capq = &inst->bufq[CAPTURE_PORT].vb2_bufq; + + poll_wait(filp, &inst->event_handler.wait, wait); + poll_wait(filp, &capq->done_wq, wait); + poll_wait(filp, &outq->done_wq, wait); + return get_poll_flags(inst); +} +EXPORT_SYMBOL(msm_vidc_poll); + +int msm_vidc_querycap(void *instance, struct v4l2_capability *cap) +{ + int rc = -EINVAL; + struct msm_vidc_inst *inst = instance; + + if (!inst || !cap) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + rc = msm_vdec_querycap(instance, cap); + else if (inst->session_type == MSM_VIDC_ENCODER) + rc = msm_venc_querycap(instance, cap); + else + goto exit; + if (!rc) { + cap->device_caps = cap->capabilities; + cap->capabilities |= V4L2_CAP_DEVICE_CAPS; + } +exit: + return rc; +} +EXPORT_SYMBOL(msm_vidc_querycap); + +int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_enum_fmt(instance, f); + else if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_enum_fmt(instance, f); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_enum_fmt); + +int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_s_fmt(instance, f); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_s_fmt(instance, f); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_s_fmt); + +int msm_vidc_g_fmt(void *instance, struct v4l2_format *f) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_g_fmt(instance, f); + else if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_g_fmt(instance, f); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_g_fmt); + +int msm_vidc_s_ctrl(void *instance, struct v4l2_control *control) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + return msm_comm_s_ctrl(instance, control); +} +EXPORT_SYMBOL(msm_vidc_s_ctrl); + +int msm_vidc_g_ctrl(void *instance, struct v4l2_control *control) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + return msm_comm_g_ctrl(instance, control); +} +EXPORT_SYMBOL(msm_vidc_g_ctrl); + +int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_s_ext_ctrl(instance, control); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_s_ext_ctrl(instance, control); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_s_ext_ctrl); + +int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !b) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_reqbufs(instance, b); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_reqbufs(instance, b); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_reqbufs); + +struct buffer_info *get_registered_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b, int idx, int *plane) +{ + struct buffer_info *temp; + struct buffer_info *ret = NULL; + int i; + int fd = b->m.planes[idx].reserved[0]; + u32 buff_off = b->m.planes[idx].reserved[1]; + u32 size = b->m.planes[idx].length; + phys_addr_t device_addr = b->m.planes[idx].m.userptr; + + if (fd < 0 || !plane) { + dprintk(VIDC_ERR, "Invalid input\n"); + goto err_invalid_input; + } + + WARN(!mutex_is_locked(&inst->registeredbufs.lock), + "Registered buf lock is not acquired for %s", __func__); + + *plane = 0; + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) { + bool dma_matches = msm_smem_compare_buffers(fd, + temp->smem[i].dma_buf); + bool device_addr_matches = device_addr == + temp->device_addr[i]; + bool contains_within = CONTAINS(temp->buff_off[i], + temp->size[i], buff_off) || + CONTAINS(buff_off, size, temp->buff_off[i]); + bool overlaps = OVERLAPS(buff_off, size, + temp->buff_off[i], temp->size[i]); + + if (!temp->inactive && + (dma_matches || device_addr_matches) && + (contains_within || overlaps)) { + dprintk(VIDC_DBG, + "This memory region is already mapped\n"); + ret = temp; + *plane = i; + break; + } + } + if (ret) + break; + } + +err_invalid_input: + return ret; +} + +static struct msm_smem *get_same_fd_buffer(struct msm_vidc_inst *inst, int fd) +{ + struct buffer_info *temp; + struct msm_smem *same_fd_handle = NULL; + int i; + + if (!fd) + return NULL; + + if (!inst || fd < 0) { + dprintk(VIDC_ERR, "%s: Invalid input\n", __func__); + goto err_invalid_input; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) { + bool dma_matches = msm_smem_compare_buffers(fd, + temp->smem[i].dma_buf); + if (dma_matches && temp->mapped[i]) { + temp->same_fd_ref[i]++; + dprintk(VIDC_INFO, + "Found same fd buffer\n"); + same_fd_handle = &temp->smem[i]; + break; + } + } + if (same_fd_handle) + break; + } + mutex_unlock(&inst->registeredbufs.lock); + +err_invalid_input: + return same_fd_handle; +} + +struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list, + phys_addr_t device_addr) +{ + struct buffer_info *temp = NULL; + bool found = false; + int i; + + if (!buf_list || !device_addr) { + dprintk(VIDC_ERR, + "Invalid input- device_addr: %pa buf_list: %pK\n", + &device_addr, buf_list); + goto err_invalid_input; + } + + mutex_lock(&buf_list->lock); + list_for_each_entry(temp, &buf_list->list, list) { + for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) { + if (!temp->inactive && + temp->device_addr[i] == device_addr) { + dprintk(VIDC_INFO, + "Found same fd buffer\n"); + found = true; + break; + } + } + + if (found) + break; + } + mutex_unlock(&buf_list->lock); + +err_invalid_input: + return temp; +} + +static inline void populate_buf_info(struct buffer_info *binfo, + struct v4l2_buffer *b, u32 i) +{ + if (i >= VIDEO_MAX_PLANES) { + dprintk(VIDC_ERR, "%s: Invalid input\n", __func__); + return; + } + binfo->type = b->type; + binfo->fd[i] = b->m.planes[i].reserved[0]; + binfo->buff_off[i] = b->m.planes[i].reserved[1]; + binfo->size[i] = b->m.planes[i].length; + binfo->uvaddr[i] = b->m.planes[i].m.userptr; + binfo->num_planes = b->length; + binfo->memory = b->memory; + binfo->v4l2_index = b->index; + binfo->timestamp.tv_sec = b->timestamp.tv_sec; + binfo->timestamp.tv_usec = b->timestamp.tv_usec; + dprintk(VIDC_DBG, "%s: fd[%d] = %d b->index = %d", + __func__, i, binfo->fd[0], b->index); +} + +static inline void repopulate_v4l2_buffer(struct v4l2_buffer *b, + struct buffer_info *binfo) +{ + int i = 0; + + b->type = binfo->type; + b->length = binfo->num_planes; + b->memory = binfo->memory; + b->index = binfo->v4l2_index; + b->timestamp.tv_sec = binfo->timestamp.tv_sec; + b->timestamp.tv_usec = binfo->timestamp.tv_usec; + binfo->dequeued = false; + for (i = 0; i < binfo->num_planes; ++i) { + b->m.planes[i].reserved[0] = binfo->fd[i]; + b->m.planes[i].reserved[1] = binfo->buff_off[i]; + b->m.planes[i].length = binfo->size[i]; + b->m.planes[i].m.userptr = binfo->device_addr[i]; + dprintk(VIDC_DBG, "%s %d %d %d %pa\n", __func__, binfo->fd[i], + binfo->buff_off[i], binfo->size[i], + &binfo->device_addr[i]); + } +} + +static inline enum hal_buffer get_hal_buffer_type( + struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return HAL_BUFFER_INPUT; + else if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return HAL_BUFFER_OUTPUT; + else + return -EINVAL; +} + +static inline bool is_dynamic_output_buffer_mode(struct v4l2_buffer *b, + struct msm_vidc_inst *inst) +{ + return b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC; +} + + +static inline bool is_encoder_input_buffer(struct v4l2_buffer *b, + struct msm_vidc_inst *inst) +{ + return b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + inst->session_type == MSM_VIDC_ENCODER; +} + +static inline void save_v4l2_buffer(struct v4l2_buffer *b, + struct buffer_info *binfo) +{ + int i = 0; + + for (i = 0; i < b->length; ++i) { + if (EXTRADATA_IDX(b->length) && + (i == EXTRADATA_IDX(b->length)) && + !b->m.planes[i].length) { + continue; + } + populate_buf_info(binfo, b, i); + } +} + +int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + struct buffer_info *binfo = NULL; + struct buffer_info *temp = NULL, *iterator = NULL; + int plane = 0; + int i = 0, rc = 0; + struct msm_smem *same_fd_handle = NULL; + + if (!b || !inst) { + dprintk(VIDC_ERR, "%s: invalid input\n", __func__); + return -EINVAL; + } + + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto exit; + } + if (b->length > VIDEO_MAX_PLANES) { + dprintk(VIDC_ERR, "Num planes exceeds max: %d, %d\n", + b->length, VIDEO_MAX_PLANES); + rc = -EINVAL; + goto exit; + } + + dprintk(VIDC_DBG, "[MAP] Create binfo = %pK fd = %d type = %d\n", + binfo, b->m.planes[0].reserved[0], b->type); + + for (i = 0; i < b->length; ++i) { + rc = 0; + if (EXTRADATA_IDX(b->length) && + (i == EXTRADATA_IDX(b->length)) && + !b->m.planes[i].length) { + continue; + } + mutex_lock(&inst->registeredbufs.lock); + temp = get_registered_buf(inst, b, i, &plane); + if (temp && !is_dynamic_output_buffer_mode(b, inst)) { + dprintk(VIDC_DBG, + "This memory region has already been prepared\n"); + rc = 0; + mutex_unlock(&inst->registeredbufs.lock); + goto exit; + } + + if (temp && is_dynamic_output_buffer_mode(b, inst) && !i) { + /* + * Buffer is already present in registered list + * increment ref_count, populate new values of v4l2 + * buffer in existing buffer_info struct. + * + * We will use the saved buffer info and queue it when + * we receive RELEASE_BUFFER_REFERENCE EVENT from f/w. + */ + dprintk(VIDC_DBG, "[MAP] Buffer already prepared\n"); + temp->inactive = false; + list_for_each_entry(iterator, + &inst->registeredbufs.list, list) { + if (iterator == temp) { + rc = buf_ref_get(inst, temp); + save_v4l2_buffer(b, temp); + break; + } + } + } + mutex_unlock(&inst->registeredbufs.lock); + /* + * rc == 1, + * buffer is mapped, fw has released all reference, so skip + * mapping and queue it immediately. + * + * rc == 2, + * buffer is mapped and fw is holding a reference, hold it in + * the driver and queue it later when fw has released + */ + if (rc == 1) { + rc = 0; + goto exit; + } else if (rc == 2) { + rc = -EEXIST; + goto exit; + } + + same_fd_handle = get_same_fd_buffer( + inst, b->m.planes[i].reserved[0]); + + populate_buf_info(binfo, b, i); + if (same_fd_handle) { + binfo->device_addr[i] = + same_fd_handle->device_addr + binfo->buff_off[i]; + b->m.planes[i].m.userptr = binfo->device_addr[i]; + binfo->mapped[i] = false; + binfo->smem[i] = *same_fd_handle; + } else { + binfo->smem[i].buffer_type = get_hal_buffer_type( + inst, b); + binfo->smem[i].fd = binfo->fd[i]; + binfo->smem[i].offset = binfo->buff_off[i]; + binfo->smem[i].size = binfo->size[i]; + rc = msm_smem_map_dma_buf(inst, &binfo->smem[i]); + if (rc) { + dprintk(VIDC_ERR, "%s: map failed.\n", + __func__); + goto exit; + } + /* increase refcount as we get both fbd and rbr */ + rc = msm_smem_map_dma_buf(inst, &binfo->smem[i]); + if (rc) { + dprintk(VIDC_ERR, "%s: map failed..\n", + __func__); + goto exit; + } + binfo->mapped[i] = true; + binfo->device_addr[i] = binfo->smem[i].device_addr + + binfo->buff_off[i]; + b->m.planes[i].m.userptr = binfo->device_addr[i]; + } + + /* We maintain one ref count for all planes*/ + if (!i && is_dynamic_output_buffer_mode(b, inst)) { + rc = buf_ref_get(inst, binfo); + if (rc < 0) + goto exit; + } + dprintk(VIDC_DBG, + "%s: [MAP] binfo = %pK, handle[%d] = %pK, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n", + __func__, binfo, i, binfo->smem[i], + &binfo->device_addr[i], binfo->fd[i], + binfo->buff_off[i], binfo->mapped[i]); + } + + mutex_lock(&inst->registeredbufs.lock); + list_add_tail(&binfo->list, &inst->registeredbufs.list); + mutex_unlock(&inst->registeredbufs.lock); + return 0; + +exit: + kfree(binfo); + return rc; +} +int unmap_and_deregister_buf(struct msm_vidc_inst *inst, + struct buffer_info *binfo) +{ + int i = 0; + struct buffer_info *temp = NULL; + bool found = false, keep_node = false; + + if (!inst || !binfo) { + dprintk(VIDC_ERR, "%s invalid param: %pK %pK\n", + __func__, inst, binfo); + return -EINVAL; + } + + WARN(!mutex_is_locked(&inst->registeredbufs.lock), + "Registered buf lock is not acquired for %s", __func__); + + /* + * Make sure the buffer to be unmapped and deleted + * from the registered list is present in the list. + */ + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (temp == binfo) { + found = true; + break; + } + } + + /* + * Free the buffer info only if + * - buffer info has not been deleted from registered list + * - vidc client has called dqbuf on the buffer + * - no references are held on the buffer + */ + if (!found || !temp || !temp->pending_deletion || !temp->dequeued) + goto exit; + + for (i = 0; i < temp->num_planes; i++) { + dprintk(VIDC_DBG, + "%s: [UNMAP] binfo = %pK, handle[%d] = %pK, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n", + __func__, temp, i, temp->smem[i], + &temp->device_addr[i], temp->fd[i], + temp->buff_off[i], temp->mapped[i]); + /* + * Unmap the handle only if the buffer has been mapped and no + * other buffer has a reference to this buffer. + * In case of buffers with same fd, we will map the buffer only + * once and subsequent buffers will refer to the mapped buffer's + * device address. + * For buffers which share the same fd, do not unmap and keep + * the buffer info in registered list. + */ + if (temp->mapped[i] && !temp->same_fd_ref[i]) { + if (msm_smem_unmap_dma_buf(inst, &temp->smem[i])) + dprintk(VIDC_DBG, "unmap failed..\n"); + + msm_comm_smem_free(inst, + &temp->smem[i]); + } + + if (temp->same_fd_ref[i]) + keep_node = true; + else { + temp->fd[i] = 0; + //temp->smem[i] = 0; + temp->device_addr[i] = 0; + temp->uvaddr[i] = 0; + } + } + if (!keep_node) { + dprintk(VIDC_DBG, "[UNMAP] AND-FREED binfo: %pK\n", temp); + list_del(&temp->list); + kfree(temp); + } else { + temp->inactive = true; + dprintk(VIDC_DBG, "[UNMAP] NOT-FREED binfo: %pK\n", temp); + } +exit: + return 0; +} + + +int qbuf_dynamic_buf(struct msm_vidc_inst *inst, + struct buffer_info *binfo) +{ + struct v4l2_buffer b = {0}; + struct v4l2_plane plane[VIDEO_MAX_PLANES] = { {0} }; + + if (!binfo) { + dprintk(VIDC_ERR, "%s invalid param: %pK\n", __func__, binfo); + return -EINVAL; + } + dprintk(VIDC_DBG, "%s fd[0] = %d\n", __func__, binfo->fd[0]); + + b.m.planes = plane; + repopulate_v4l2_buffer(&b, binfo); + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_qbuf(inst, &b); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_qbuf(inst, &b); + + return -EINVAL; +} + +int qbuf_cache_operations(struct msm_vidc_inst *inst, + struct buffer_info *binfo) +{ + unsigned long offset, size; + enum smem_cache_ops cache_op; + bool skip; + int i = 0, rc = 0; + + skip = true; + + for (i = 0; i < binfo->num_planes; i++) { + if (inst->session_type == MSM_VIDC_DECODER) { + if (binfo->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!i) { /* bitstream */ + skip = false; + offset = binfo->buff_off[i]; + size = binfo->size[i]; + cache_op = SMEM_CACHE_CLEAN_INVALIDATE; + } + } else if (binfo->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* yuv */ + skip = false; + offset = 0; + size = binfo->size[i]; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + if (binfo->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!i) { /* yuv */ + skip = false; + offset = binfo->buff_off[i]; + size = binfo->size[i]; + cache_op = SMEM_CACHE_CLEAN_INVALIDATE; + } + } else if (binfo->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* bitstream */ + skip = false; + offset = 0; + size = binfo->size[i]; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } + if (!skip) { + rc = msm_smem_cache_operations(binfo->smem[i].dma_buf, + cache_op, offset, size); + if (rc) { + dprintk(VIDC_ERR, + "Failed to clean caches: %d\n", rc); + return rc; + } + } + } + return 0; +} +int dqbuf_cache_operations(struct msm_vidc_inst *inst, + struct v4l2_buffer *b, + struct buffer_info *buffer_info) +{ + int i = 0, rc = 0; + bool skip = true; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst); + return -EINVAL; + } + + if (!b || !buffer_info) { + dprintk(VIDC_ERR, "%s: invalid buffer: %pK\n", + __func__, inst); + return -EINVAL; + } + + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return 0; + + for (i = 0; i < b->length; i++) { + unsigned long offset, size; + enum smem_cache_ops cache_op; + + skip = true; + if (inst->session_type == MSM_VIDC_DECODER) { + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* bitstream and extradata */ + /* we do not need cache operations */ + } else if (b->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* yuv */ + skip = false; + offset = b->m.planes[i].data_offset; + size = b->m.planes[i].bytesused; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } else if (inst->session_type == MSM_VIDC_ENCODER) { + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* yuv and extradata */ + /* we do not need cache operations */ + } else if (b->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + if (!i) { /* bitstream */ + skip = false; + /* + * Include vp8e header bytes as well + * by making offset equal to zero + */ + offset = 0; + size = b->m.planes[i].bytesused+ + b->m.planes[i].data_offset; + cache_op = SMEM_CACHE_INVALIDATE; + } + } + } + if (!skip && size > 0) { + rc = msm_smem_cache_operations( + buffer_info->smem[i].dma_buf, + cache_op, offset, size); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to clean caches: %d\n", + __func__, rc); + return -EINVAL; + } + } + } + return 0; +} + +static bool valid_v4l2_buffer(struct v4l2_buffer *b, + struct msm_vidc_inst *inst) +{ + enum vidc_ports port = + !V4L2_TYPE_IS_MULTIPLANAR(b->type) ? MAX_PORT_NUM : + b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? CAPTURE_PORT : + b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? OUTPUT_PORT : + MAX_PORT_NUM; + + return port != MAX_PORT_NUM && + inst->fmts[port].num_planes == b->length; +} + +int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst)) + return -EINVAL; + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID) + return -EINVAL; + + if (is_dynamic_output_buffer_mode(b, inst)) + return 0; + + if (map_and_register_buf(inst, b)) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_prepare_buf(instance, b); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_prepare_buf(instance, b); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_prepare_buf); + +int msm_vidc_release_buffers(void *instance, int buffer_type) +{ + struct msm_vidc_inst *inst = instance; + struct buffer_info *bi, *dummy; + struct v4l2_buffer buffer_info; + struct v4l2_plane plane[VIDEO_MAX_PLANES]; + int i, rc = 0; + + if (!inst) + return -EINVAL; + + if (!inst->in_reconfig && + inst->state > MSM_VIDC_LOAD_RESOURCES && + inst->state < MSM_VIDC_RELEASE_RESOURCES_DONE) { + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %pK to release res done\n", + inst); + } + } + + /* + * In dynamic buffer mode, driver needs to release resources, + * but not call release buffers on firmware, as the buffers + * were never registered with firmware. + */ + if (buffer_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + inst->buffer_mode_set[CAPTURE_PORT] == + HAL_BUFFER_MODE_DYNAMIC) { + goto free_and_unmap; + } + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(bi, &inst->registeredbufs.list, list) { + bool release_buf = false; + + if (bi->type == buffer_type) { + buffer_info.type = bi->type; + for (i = 0; i < min(bi->num_planes, VIDEO_MAX_PLANES); + i++) { + plane[i].reserved[0] = bi->fd[i]; + plane[i].reserved[1] = bi->buff_off[i]; + plane[i].length = bi->size[i]; + plane[i].m.userptr = bi->device_addr[i]; + buffer_info.m.planes = plane; + dprintk(VIDC_DBG, + "Releasing buffer: %d, %d, %d\n", + buffer_info.m.planes[i].reserved[0], + buffer_info.m.planes[i].reserved[1], + buffer_info.m.planes[i].length); + } + buffer_info.length = bi->num_planes; + release_buf = true; + } + + if (!release_buf) + continue; + if (inst->session_type == MSM_VIDC_DECODER) + rc = msm_vdec_release_buf(instance, + &buffer_info); + if (inst->session_type == MSM_VIDC_ENCODER) + rc = msm_venc_release_buf(instance, + &buffer_info); + if (rc) + dprintk(VIDC_ERR, + "Failed Release buffer: %d, %d, %d\n", + buffer_info.m.planes[0].reserved[0], + buffer_info.m.planes[0].reserved[1], + buffer_info.m.planes[0].length); + } + mutex_unlock(&inst->registeredbufs.lock); + +free_and_unmap: + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(bi, dummy, &inst->registeredbufs.list, list) { + if (bi->type == buffer_type) { + list_del(&bi->list); + for (i = 0; i < bi->num_planes; i++) { + if (bi->mapped[i]) { + dprintk(VIDC_DBG, + "%s: [UNMAP] binfo = %pK, handle[%d] = %pK, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n", + __func__, bi, i, bi->smem[i], + &bi->device_addr[i], bi->fd[i], + bi->buff_off[i], bi->mapped[i]); + msm_comm_smem_free(inst, + &bi->smem[i]); + } + } + kfree(bi); + } + } + mutex_unlock(&inst->registeredbufs.lock); + return rc; +} +EXPORT_SYMBOL(msm_vidc_release_buffers); + +int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + struct buffer_info *binfo; + int plane = 0; + int rc = 0; + int i; + + if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst)) + return -EINVAL; + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID) + return -EINVAL; + + rc = map_and_register_buf(inst, b); + if (rc == -EEXIST) { + if (atomic_read(&inst->in_flush) && + is_dynamic_output_buffer_mode(b, inst)) { + dprintk(VIDC_ERR, + "Flush in progress, do not hold any buffers in driver\n"); + msm_comm_flush_dynamic_buffers(inst); + } + return 0; + } + if (rc) + return rc; + + for (i = 0; i < b->length; ++i) { + if (EXTRADATA_IDX(b->length) && + (i == EXTRADATA_IDX(b->length)) && + !b->m.planes[i].length) { + b->m.planes[i].m.userptr = 0; + continue; + } + mutex_lock(&inst->registeredbufs.lock); + binfo = get_registered_buf(inst, b, i, &plane); + mutex_unlock(&inst->registeredbufs.lock); + if (!binfo) { + dprintk(VIDC_ERR, + "This buffer is not registered: %d, %d, %d\n", + b->m.planes[i].reserved[0], + b->m.planes[i].reserved[1], + b->m.planes[i].length); + goto err_invalid_buff; + } + b->m.planes[i].m.userptr = binfo->device_addr[i]; + dprintk(VIDC_DBG, "Queueing device address = %pa\n", + &binfo->device_addr[i]); + } + + qbuf_cache_operations(inst, binfo); + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_qbuf(instance, b); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_qbuf(instance, b); + +err_invalid_buff: + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_qbuf); + +int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + struct buffer_info *buffer_info = NULL; + int i = 0, rc = 0; + + if (!inst || !b || !valid_v4l2_buffer(b, inst)) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + rc = msm_vdec_dqbuf(instance, b); + if (inst->session_type == MSM_VIDC_ENCODER) + rc = msm_venc_dqbuf(instance, b); + + if (rc) + return rc; + + for (i = b->length - 1; i >= 0 ; i--) { + if (EXTRADATA_IDX(b->length) && + i == EXTRADATA_IDX(b->length)) { + continue; + } + buffer_info = device_to_uvaddr(&inst->registeredbufs, + b->m.planes[i].m.userptr); + + if (!buffer_info) { + dprintk(VIDC_ERR, + "%s no buffer info registered for buffer addr: %#lx\n", + __func__, b->m.planes[i].m.userptr); + return -EINVAL; + } + + b->m.planes[i].m.userptr = buffer_info->uvaddr[i]; + b->m.planes[i].reserved[0] = buffer_info->fd[i]; + b->m.planes[i].reserved[1] = buffer_info->buff_off[i]; + + b->m.planes[i].reserved[2] = buffer_info->crop_data.nLeft; + b->m.planes[i].reserved[3] = buffer_info->crop_data.nTop; + b->m.planes[i].reserved[4] = buffer_info->crop_data.nWidth; + b->m.planes[i].reserved[5] = buffer_info->crop_data.nHeight; + b->m.planes[i].reserved[6] = + buffer_info->crop_data.width_height[0]; + b->m.planes[i].reserved[7] = + buffer_info->crop_data.width_height[1]; + if (!(inst->flags & VIDC_SECURE) && !b->m.planes[i].m.userptr) { + dprintk(VIDC_ERR, + "%s: Failed to find user virtual address, %#lx, %d, %d\n", + __func__, b->m.planes[i].m.userptr, b->type, i); + return -EINVAL; + } + } + + if (!buffer_info) { + dprintk(VIDC_ERR, + "%s: error - no buffer info found in registered list\n", + __func__); + return -EINVAL; + } + + rc = dqbuf_cache_operations(inst, b, buffer_info); + if (rc) + return rc; + + if (is_dynamic_output_buffer_mode(b, inst)) { + buffer_info->dequeued = true; + + dprintk(VIDC_DBG, "[DEQUEUED]: fd[0] = %d\n", + buffer_info->fd[0]); + mutex_lock(&inst->registeredbufs.lock); + rc = unmap_and_deregister_buf(inst, buffer_info); + mutex_unlock(&inst->registeredbufs.lock); + } + + return rc; +} +EXPORT_SYMBOL(msm_vidc_dqbuf); + +int msm_vidc_streamon(void *instance, enum v4l2_buf_type i) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_streamon(instance, i); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_streamon(instance, i); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_streamon); + +int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_streamoff(instance, i); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_streamoff(instance, i); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_streamoff); + +int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize) +{ + struct msm_vidc_inst *inst = instance; + struct msm_vidc_capability *capability = NULL; + + if (!inst || !fsize) { + dprintk(VIDC_ERR, "%s: invalid parameter: %pK %pK\n", + __func__, inst, fsize); + return -EINVAL; + } + if (!inst->core) + return -EINVAL; + + capability = &inst->capability; + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = capability->width.min; + fsize->stepwise.max_width = capability->width.max; + fsize->stepwise.step_width = capability->width.step_size; + fsize->stepwise.min_height = capability->height.min; + fsize->stepwise.max_height = capability->height.max; + fsize->stepwise.step_height = capability->height.step_size; + return 0; +} +EXPORT_SYMBOL(msm_vidc_enum_framesizes); + +static void *vidc_get_userptr(struct device *dev, unsigned long vaddr, + unsigned long size, enum dma_data_direction dma_dir) +{ + return (void *)0xdeadbeef; +} + +static void vidc_put_userptr(void *buf_priv) +{ + +} +static const struct vb2_mem_ops msm_vidc_vb2_mem_ops = { + .get_userptr = vidc_get_userptr, + .put_userptr = vidc_put_userptr, +}; + +static inline int vb2_bufq_init(struct msm_vidc_inst *inst, + enum v4l2_buf_type type, enum session_type sess) +{ + struct vb2_queue *q = NULL; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + q = &inst->bufq[CAPTURE_PORT].vb2_bufq; + } else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + q = &inst->bufq[OUTPUT_PORT].vb2_bufq; + } else { + dprintk(VIDC_ERR, "buf_type = %d not recognised\n", type); + return -EINVAL; + } + + q->type = type; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + + if (sess == MSM_VIDC_DECODER) + q->ops = msm_vdec_get_vb2q_ops(); + else if (sess == MSM_VIDC_ENCODER) + q->ops = msm_venc_get_vb2q_ops(); + q->mem_ops = &msm_vidc_vb2_mem_ops; + q->drv_priv = inst; + q->allow_zero_bytesused = 1; + return vb2_queue_init(q); +} + +static int setup_event_queue(void *inst, + struct video_device *pvdev) +{ + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + v4l2_fh_init(&vidc_inst->event_handler, pvdev); + v4l2_fh_add(&vidc_inst->event_handler); + + return 0; +} + +int msm_vidc_subscribe_event(void *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + if (!inst || !sub) + return -EINVAL; + + rc = v4l2_event_subscribe(&vidc_inst->event_handler, + sub, MAX_EVENTS, NULL); + return rc; +} +EXPORT_SYMBOL(msm_vidc_subscribe_event); + +int msm_vidc_unsubscribe_event(void *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + if (!inst || !sub) + return -EINVAL; + + rc = v4l2_event_unsubscribe(&vidc_inst->event_handler, sub); + return rc; +} +EXPORT_SYMBOL(msm_vidc_unsubscribe_event); + +int msm_vidc_dqevent(void *inst, struct v4l2_event *event) +{ + int rc = 0; + struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst; + + if (!inst || !event) + return -EINVAL; + + rc = v4l2_event_dequeue(&vidc_inst->event_handler, event, false); + return rc; +} +EXPORT_SYMBOL(msm_vidc_dqevent); + +static bool msm_vidc_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++; + /* This flag is not updated yet for the current instance */ + if (inst->flags & VIDC_SECURE) + secure_instance_count++; + } + mutex_unlock(&core->lock); + + /* Instance count includes current instance as well. */ + + if ((instance_count > core->resources.max_inst_count) || + (secure_instance_count > core->resources.max_secure_inst_count)) + overload = true; + return overload; +} + +void *msm_vidc_open(int core_id, int session_type) +{ + struct msm_vidc_inst *inst = NULL; + struct msm_vidc_core *core = NULL; + int rc = 0; + int i = 0; + + if (core_id >= MSM_VIDC_CORES_MAX || + session_type >= MSM_VIDC_MAX_DEVICES) { + dprintk(VIDC_ERR, "Invalid input, core_id = %d, session = %d\n", + core_id, session_type); + goto err_invalid_core; + } + core = get_vidc_core(core_id); + if (!core) { + dprintk(VIDC_ERR, + "Failed to find core for core_id = %d\n", core_id); + goto err_invalid_core; + } + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) { + dprintk(VIDC_ERR, "Failed to allocate memory\n"); + rc = -ENOMEM; + goto err_invalid_core; + } + + pr_info(VIDC_DBG_TAG "Opening video instance: %pK, %d\n", + VIDC_MSG_PRIO2STRING(VIDC_INFO), inst, session_type); + mutex_init(&inst->sync_lock); + mutex_init(&inst->bufq[CAPTURE_PORT].lock); + mutex_init(&inst->bufq[OUTPUT_PORT].lock); + mutex_init(&inst->lock); + + INIT_MSM_VIDC_LIST(&inst->pendingq); + INIT_MSM_VIDC_LIST(&inst->scratchbufs); + INIT_MSM_VIDC_LIST(&inst->persistbufs); + INIT_MSM_VIDC_LIST(&inst->pending_getpropq); + INIT_MSM_VIDC_LIST(&inst->outputbufs); + INIT_MSM_VIDC_LIST(&inst->registeredbufs); + INIT_MSM_VIDC_LIST(&inst->eosbufs); + + kref_init(&inst->kref); + + inst->session_type = session_type; + inst->state = MSM_VIDC_CORE_UNINIT_DONE; + inst->core = core; + inst->bit_depth = MSM_VIDC_BIT_DEPTH_8; + inst->instant_bitrate = 0; + inst->pic_struct = MSM_VIDC_PIC_STRUCT_PROGRESSIVE; + inst->colour_space = MSM_VIDC_BT601_6_525; + + for (i = SESSION_MSG_INDEX(SESSION_MSG_START); + i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) { + init_completion(&inst->completions[i]); + } + + if (session_type == MSM_VIDC_DECODER) { + msm_vdec_inst_init(inst); + rc = msm_vdec_ctrl_init(inst); + } else if (session_type == MSM_VIDC_ENCODER) { + msm_venc_inst_init(inst); + rc = msm_venc_ctrl_init(inst); + } + + if (rc) + goto fail_bufq_capture; + + msm_dcvs_init(inst); + rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + session_type); + if (rc) { + dprintk(VIDC_ERR, + "Failed to initialize vb2 queue on capture port\n"); + goto fail_bufq_capture; + } + rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + session_type); + if (rc) { + dprintk(VIDC_ERR, + "Failed to initialize vb2 queue on capture port\n"); + goto fail_bufq_output; + } + + setup_event_queue(inst, &core->vdev[session_type].vdev); + + mutex_lock(&core->lock); + list_add_tail(&inst->list, &core->instances); + mutex_unlock(&core->lock); + + rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move video instance to init state\n"); + goto fail_init; + } + + if (msm_vidc_check_for_inst_overload(core)) { + dprintk(VIDC_ERR, + "Instance count reached Max limit, rejecting session"); + goto fail_init; + } + + inst->debugfs_root = + msm_vidc_debugfs_init_inst(inst, core->debugfs_root); + + return inst; +fail_init: + mutex_lock(&core->lock); + v4l2_fh_del(&inst->event_handler); + v4l2_fh_exit(&inst->event_handler); + list_del(&inst->list); + mutex_unlock(&core->lock); + vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq); + +fail_bufq_output: + vb2_queue_release(&inst->bufq[CAPTURE_PORT].vb2_bufq); +fail_bufq_capture: + msm_comm_ctrl_deinit(inst); + DEINIT_MSM_VIDC_LIST(&inst->eosbufs); + kfree(inst); + inst = NULL; +err_invalid_core: + return inst; +} +EXPORT_SYMBOL(msm_vidc_open); + +static void cleanup_instance(struct msm_vidc_inst *inst) +{ + struct vb2_buf_entry *entry, *dummy; + + if (inst) { + + mutex_lock(&inst->pendingq.lock); + list_for_each_entry_safe(entry, dummy, &inst->pendingq.list, + list) { + list_del(&entry->list); + kfree(entry); + } + mutex_unlock(&inst->pendingq.lock); + + if (msm_comm_release_scratch_buffers(inst, false)) { + dprintk(VIDC_ERR, + "Failed to release scratch buffers\n"); + } + + if (msm_comm_release_persist_buffers(inst)) { + dprintk(VIDC_ERR, + "Failed to release persist buffers\n"); + } + + msm_comm_release_eos_buffers(inst); + + if (msm_comm_release_output_buffers(inst)) { + dprintk(VIDC_ERR, + "Failed to release output buffers\n"); + } + + if (inst->extradata_handle) + msm_comm_smem_free(inst, inst->extradata_handle); + + mutex_lock(&inst->pending_getpropq.lock); + if (!list_empty(&inst->pending_getpropq.list)) { + dprintk(VIDC_ERR, + "pending_getpropq not empty\n"); + WARN_ON(VIDC_DBG_WARN_ENABLE); + } + mutex_unlock(&inst->pending_getpropq.lock); + } +} + +int msm_vidc_destroy(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + int i = 0; + + if (!inst || !inst->core) + return -EINVAL; + + core = inst->core; + + mutex_lock(&core->lock); + /* inst->list lives in core->instances */ + list_del(&inst->list); + mutex_unlock(&core->lock); + + msm_comm_ctrl_deinit(inst); + + v4l2_fh_del(&inst->event_handler); + v4l2_fh_exit(&inst->event_handler); + + for (i = 0; i < MAX_PORT_NUM; i++) + vb2_queue_release(&inst->bufq[i].vb2_bufq); + + DEINIT_MSM_VIDC_LIST(&inst->eosbufs); + + mutex_destroy(&inst->sync_lock); + mutex_destroy(&inst->bufq[CAPTURE_PORT].lock); + mutex_destroy(&inst->bufq[OUTPUT_PORT].lock); + mutex_destroy(&inst->lock); + + msm_vidc_debugfs_deinit_inst(inst); + pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n", + VIDC_MSG_PRIO2STRING(VIDC_INFO), inst); + kfree(inst); + return 0; +} +static void close_helper(struct kref *kref) +{ + struct msm_vidc_inst *inst = container_of(kref, + struct msm_vidc_inst, kref); + msm_vidc_destroy(inst); +} +int msm_vidc_close(void *instance) +{ + struct msm_vidc_inst *inst = instance; + struct buffer_info *bi, *dummy; + int rc = 0; + + if (!inst || !inst->core) + return -EINVAL; + + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry_safe(bi, dummy, &inst->registeredbufs.list, list) { + if (bi->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + int i = 0; + + list_del(&bi->list); + + for (i = 0; i < min(bi->num_planes, VIDEO_MAX_PLANES); + i++) { + if (bi->mapped[i]) + msm_comm_smem_free(inst, &bi->smem[i]); + } + + kfree(bi); + } + } + mutex_unlock(&inst->registeredbufs.lock); + + cleanup_instance(inst); + if (inst->state != MSM_VIDC_CORE_INVALID && + inst->core->state != VIDC_CORE_INVALID) + rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT); + else + rc = msm_comm_force_cleanup(inst); + if (rc) + dprintk(VIDC_ERR, + "Failed to move video instance to uninit state\n"); + + msm_comm_session_clean(inst); + + kref_put(&inst->kref, close_helper); + return 0; +} +EXPORT_SYMBOL(msm_vidc_close); + +int msm_vidc_suspend(int core_id) +{ + return msm_comm_suspend(core_id); +} +EXPORT_SYMBOL(msm_vidc_suspend); + diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c new file mode 100644 index 000000000000..8127ed971cf4 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.c @@ -0,0 +1,5325 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_dcvs.h" + +#define IS_ALREADY_IN_STATE(__p, __d) ({\ + int __rc = (__p >= __d);\ + __rc; \ +}) + +#define SUM_ARRAY(__arr, __start, __end) ({\ + int __index;\ + typeof((__arr)[0]) __sum = 0;\ + for (__index = (__start); __index <= (__end); __index++) {\ + if (__index >= 0 && __index < ARRAY_SIZE(__arr))\ + __sum += __arr[__index];\ + } \ + __sum;\ +}) + +#define V4L2_EVENT_SEQ_CHANGED_SUFFICIENT \ + V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT +#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 MAX_SUPPORTED_INSTANCES 16 + +const char *const mpeg_video_vidc_extradata[] = { + "Extradata none", + "Extradata MB Quantization", + "Extradata Interlace Video", + "Extradata enc DTS", + "Extradata VC1 Framedisp", + "Extradata timestamp", + "Extradata S3D Frame Packing", + "Extradata Frame Rate", + "Extradata Panscan Window", + "Extradata Recovery point SEI", + "Extradata Multislice info", + "Extradata number of concealed MB", + "Extradata metadata filler", + "Extradata input crop", + "Extradata digital zoom", + "Extradata aspect ratio", + "Extradata mpeg2 seqdisp", + "Extradata stream userdata", + "Extradata frame QP", + "Extradata frame bits info", + "Extradata LTR", + "Extradata macroblock metadata", + "Extradata VQZip SEI", + "Extradata HDR10+ Metadata", + "Extradata YUV Stats", + "Extradata ROI QP", + "Extradata output crop", + "Extradata display colour SEI", + "Extradata light level SEI", + "Extradata PQ Info", + "Extradata display VUI", + "Extradata vpx color space", + "Extradata UBWC CR stats info", + "Extradata enc frame QP", + "Extradata VC1 Seqdisp", + "Extradata YUV Stats" +}; + +struct getprop_buf { + struct list_head list; + void *data; +}; + +static void msm_comm_generate_session_error(struct msm_vidc_inst *inst); +static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst); +static void handle_session_error(enum hal_command_response cmd, void *data); + +bool msm_comm_turbo_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_TURBO); +} + +static inline bool is_thumbnail_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_THUMBNAIL); +} + +static inline bool is_low_power_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_LOW_POWER); +} + +int msm_comm_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) +{ + return v4l2_g_ctrl(&inst->ctrl_handler, ctrl); +} + +int msm_comm_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) +{ + return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl); +} + +int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id) +{ + int rc = 0; + struct v4l2_control ctrl = { + .id = id, + }; + + rc = msm_comm_g_ctrl(inst, &ctrl); + return rc ?: ctrl.value; +} + +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) + return NULL; + + for (c = 0; c < num_ctrls; c++) + cluster[c] = inst->ctrls[c]; + + return cluster; +} + +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 < 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, + drv_ctrls[idx].maximum, + drv_ctrls[idx].menu_skip_mask, + 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; + } + + /* Construct a super cluster of all controls */ + inst->cluster = get_super_cluster(inst, num_ctrls); + if (!inst->cluster) { + dprintk(VIDC_WARN, + "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; +} + +static inline bool is_realtime_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_REALTIME); +} + +enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst) +{ + switch (msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE)) { + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY: + return HAL_VIDEO_DECODER_SECONDARY; + case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY: + default: + return HAL_VIDEO_DECODER_PRIMARY; + } +} + +static int msm_comm_get_mbs_per_frame(struct msm_vidc_inst *inst) +{ + int output_port_mbs, capture_port_mbs; + + output_port_mbs = inst->in_reconfig ? + NUM_MBS_PER_FRAME(inst->reconfig_width, + inst->reconfig_height) : + NUM_MBS_PER_FRAME(inst->prop.width[OUTPUT_PORT], + inst->prop.height[OUTPUT_PORT]); + capture_port_mbs = NUM_MBS_PER_FRAME(inst->prop.width[CAPTURE_PORT], + inst->prop.height[CAPTURE_PORT]); + + return max(output_port_mbs, capture_port_mbs); +} + +static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst) +{ + u32 fps; + int mb_per_frame; + u32 oper_rate; + + mb_per_frame = msm_comm_get_mbs_per_frame(inst); + oper_rate = inst->prop.operating_rate; + + if (oper_rate) { + fps = (oper_rate >> 16) ? oper_rate >> 16 : 1; + /* + * Check if operating rate is less than fps. + * If Yes, then use fps to scale the clocks + */ + fps = fps > inst->prop.fps ? fps : inst->prop.fps; + return (mb_per_frame * fps); + } else + return (mb_per_frame * inst->prop.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 (msm_comm_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->prop.fps) { + dprintk(VIDC_INFO, "instance:%pK fps = 0\n", inst); + load = 0; + } else { + load = msm_comm_get_mbs_per_sec(inst) / inst->prop.fps; + } + } + +exit: + mutex_unlock(&inst->lock); + 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; + default: + dprintk(VIDC_ERR, "Wrong domain\n"); + 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_H263: + codec = HAL_VIDEO_CODEC_H263; + 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_MPEG4: + codec = HAL_VIDEO_CODEC_MPEG4; + break; + case V4L2_PIX_FMT_VC1_ANNEX_G: + case V4L2_PIX_FMT_VC1_ANNEX_L: + codec = HAL_VIDEO_CODEC_VC1; + 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_DIVX_311: + codec = HAL_VIDEO_CODEC_DIVX_311; + break; + case V4L2_PIX_FMT_DIVX: + codec = HAL_VIDEO_CODEC_DIVX; + break; + case V4L2_PIX_FMT_HEVC: + codec = HAL_VIDEO_CODEC_HEVC; + break; + case V4L2_PIX_FMT_HEVC_HYBRID: + codec = HAL_VIDEO_CODEC_HEVC_HYBRID; + break; + default: + dprintk(VIDC_ERR, "Wrong codec: %d\n", fourcc); + codec = HAL_UNUSED_CODEC; + break; + } + + return codec; +} + +static enum hal_uncompressed_format 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_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_RGB32: + format = HAL_COLOR_FORMAT_RGBA8888; + break; + case V4L2_PIX_FMT_RGBA8888_UBWC: + format = HAL_COLOR_FORMAT_RGBA8888_UBWC; + break; + default: + format = HAL_UNUSED_COLOR; + break; + } + + return format; +} + +static int msm_comm_vote_bus(struct msm_vidc_core *core) +{ + int rc = 0, vote_data_count = 0, i = 0; + struct hfi_device *hdev; + struct msm_vidc_inst *inst = NULL; + struct vidc_bus_vote_data *vote_data = NULL; + unsigned long core_freq = 0; + + if (!core) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, core); + return -EINVAL; + } + + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle: %pK\n", + __func__, hdev); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) + ++vote_data_count; + + vote_data = kcalloc(vote_data_count, sizeof(*vote_data), + GFP_KERNEL); + if (!vote_data) { + dprintk(VIDC_ERR, "%s: failed to allocate memory\n", __func__); + rc = -ENOMEM; + goto fail_alloc; + } + + core_freq = call_hfi_op(hdev, get_core_clock_rate, + hdev->hfi_device_data, 0); + + list_for_each_entry(inst, &core->instances, list) { + int codec = 0, yuv = 0; + u32 oper_rate; + + codec = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT].fourcc : + inst->fmts[CAPTURE_PORT].fourcc; + + yuv = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[CAPTURE_PORT].fourcc : + inst->fmts[OUTPUT_PORT].fourcc; + + vote_data[i].domain = get_hal_domain(inst->session_type); + vote_data[i].codec = get_hal_codec(codec); + vote_data[i].width = max(inst->prop.width[CAPTURE_PORT], + inst->prop.width[OUTPUT_PORT]); + vote_data[i].height = max(inst->prop.height[CAPTURE_PORT], + inst->prop.height[OUTPUT_PORT]); + + oper_rate = inst->prop.operating_rate; + + if (oper_rate) + vote_data[i].fps = (oper_rate >> 16) ? + oper_rate >> 16 : 1; + else + vote_data[i].fps = inst->prop.fps; + + if (msm_comm_turbo_session(inst)) + vote_data[i].power_mode = VIDC_POWER_TURBO; + else if (is_low_power_session(inst)) + vote_data[i].power_mode = VIDC_POWER_LOW; + else + vote_data[i].power_mode = VIDC_POWER_NORMAL; + if (i == 0) { + vote_data[i].imem_ab_tbl = core->resources.imem_ab_tbl; + vote_data[i].imem_ab_tbl_size = + core->resources.imem_ab_tbl_size; + vote_data[i].core_freq = core_freq; + } + + /* + * TODO: support for OBP-DBP split mode hasn't been yet + * implemented, once it is, this part of code needs to be + * revisited since passing in accurate information to the bus + * governor will drastically reduce bandwidth + */ + vote_data[i].color_formats[0] = get_hal_uncompressed(yuv); + vote_data[i].num_formats = 1; + i++; + } + mutex_unlock(&core->lock); + + rc = call_hfi_op(hdev, vote_bus, hdev->hfi_device_data, vote_data, + vote_data_count); + if (rc) + dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc); + + kfree(vote_data); + return rc; + +fail_alloc: + mutex_unlock(&core->lock); + return rc; +} + +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 *msm_comm_get_pixel_fmt_index( + const struct msm_vidc_format fmt[], int size, int index, int fmt_type) +{ + 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 (fmt[i].type != fmt_type) + continue; + if (k == index) + break; + k++; + } + if (i == size) { + dprintk(VIDC_INFO, "Format not found\n"); + return NULL; + } + return &fmt[i]; +} +struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc( + struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type) +{ + 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_INFO, "Format 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 == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return &inst->bufq[CAPTURE_PORT]; + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return &inst->bufq[OUTPUT_PORT]; + return NULL; +} + +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; + struct vidc_hal_sys_init_done *sys_init_msg; + u32 index; + + if (!IS_HAL_SYS_CMD(cmd)) { + dprintk(VIDC_ERR, "%s - invalid cmd\n", __func__); + return; + } + + index = SYS_MSG_INDEX(cmd); + + 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; + } + sys_init_msg = &response->data.sys_init_done; + if (!sys_init_msg) { + dprintk(VIDC_ERR, "sys_init_done message not proper\n"); + return; + } + + core->enc_codec_supported = sys_init_msg->enc_codec_supported; + core->dec_codec_supported = sys_init_msg->dec_codec_supported; + + /* This should come from sys_init_done */ + core->resources.max_inst_count = + sys_init_msg->max_sessions_supported ? : + MAX_SUPPORTED_INSTANCES; + + core->resources.max_secure_inst_count = + core->resources.max_secure_inst_count ? : + core->resources.max_inst_count; + + if (core->id == MSM_VIDC_CORE_VENUS && + (core->dec_codec_supported & HAL_VIDEO_CODEC_H264)) + core->dec_codec_supported |= + HAL_VIDEO_CODEC_MVC; + + core->codec_count = sys_init_msg->codec_count; + memcpy(core->capabilities, sys_init_msg->capabilities, + sys_init_msg->codec_count * sizeof(struct msm_vidc_capability)); + + dprintk(VIDC_DBG, + "%s: supported_codecs[%d]: enc = %#x, dec = %#x\n", + __func__, core->codec_count, core->enc_codec_supported, + core->dec_codec_supported); + + complete(&(core->completions[index])); + +} +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_WARN, "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_DBG, "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_DBG, "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)]); +} + +static 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_DBG, + "Inst: %pK is in bad state can't change state to %d\n", + inst, state); + goto exit; + } + dprintk(VIDC_DBG, "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; + + if (!IS_HAL_SESSION_CMD(cmd)) { + dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd); + return -EINVAL; + } + rc = wait_for_completion_timeout( + &inst->completions[SESSION_MSG_INDEX(cmd)], + msecs_to_jiffies(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); + WARN_ON(msm_vidc_debug_timeout); + 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_INFO, "inst: %pK is already in state: %d\n", + inst, inst->state); + goto err_same_state; + } + dprintk(VIDC_DBG, "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_DBG, + "%-24s: %-8d %-8d %-8d\n", + type, cap->min, cap->max, cap->step_size); +} + +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 vidc_hal_session_init_done *session_init_done = NULL; + struct msm_vidc_capability *capability = NULL; + struct hfi_device *hdev; + 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_WARN, "Got a response for an inactive session\n"); + return; + } + + if (response->status) { + dprintk(VIDC_ERR, + "Session init response from FW : %#x\n", + response->status); + 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); + return; + } + + core = inst->core; + hdev = inst->core->device; + codec = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT].fourcc : + inst->fmts[CAPTURE_PORT].fourcc; + + /* check if capabilities are available for this session */ + for (i = 0; i < VIDC_MAX_SESSIONS; 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_DBG, + "%s: capabilities available for codec 0x%x, domain %#x\n", + __func__, capability->codec, capability->domain); + memcpy(&inst->capability, capability, + sizeof(struct msm_vidc_capability)); + } else { + session_init_done = (struct vidc_hal_session_init_done *) + &response->data.session_init_done; + if (!session_init_done) { + dprintk(VIDC_ERR, + "%s: Failed to get valid response for session init\n", + __func__); + return; + } + capability = &session_init_done->capability; + dprintk(VIDC_DBG, + "%s: got capabilities for codec 0x%x, domain 0x%x\n", + __func__, capability->codec, + capability->domain); + memcpy(&inst->capability, capability, + sizeof(struct msm_vidc_capability)); + } + inst->capability.pixelprocess_capabilities = + call_hfi_op(hdev, get_core_capabilities, hdev->hfi_device_data); + + dprintk(VIDC_DBG, + "Capability type : min max step size\n"); + print_cap("width", &inst->capability.width); + print_cap("height", &inst->capability.height); + print_cap("mbs_per_frame", &inst->capability.mbs_per_frame); + print_cap("frame_rate", &inst->capability.frame_rate); + print_cap("scale_x", &inst->capability.scale_x); + print_cap("scale_y", &inst->capability.scale_y); + print_cap("hier_p", &inst->capability.hier_p); + print_cap("ltr_count", &inst->capability.ltr_count); + print_cap("mbs_per_sec_low_power", + &inst->capability.mbs_per_sec_power_save); + + signal_session_msg_receipt(cmd, inst); + put_inst(inst); +} + +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; + + if (!event_notify) { + dprintk(VIDC_WARN, "Got an empty event from hfi\n"); + goto err_bad_event; + } + + inst = get_inst(get_vidc_core(event_notify->device_id), + event_notify->session_id); + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "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: + event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + + rc = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER); + + if (!IS_ERR_VALUE((unsigned long)rc) && rc == true) { + event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT; + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + struct hal_frame_size frame_sz; + + frame_sz.buffer_type = HAL_BUFFER_OUTPUT2; + frame_sz.width = event_notify->width; + frame_sz.height = event_notify->height; + dprintk(VIDC_DBG, + "Update OPB dimensions to firmware if buffer requirements are sufficient\n"); + rc = msm_comm_try_set_prop(inst, + HAL_PARAM_FRAME_SIZE, &frame_sz); + } + + dprintk(VIDC_DBG, + "send session_continue after sufficient event\n"); + rc = call_hfi_op(hdev, session_continue, + (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "%s - failed to send session_continue\n", + __func__); + 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 v4l2_event buf_event = {0}; + struct buffer_info *binfo = NULL, *temp = NULL; + u32 *ptr = NULL; + + dprintk(VIDC_DBG, "%s - inst: %pK buffer: %pa extra: %pa\n", + __func__, inst, &event_notify->packet_buffer, + &event_notify->extra_data_buffer); + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_DBG, + "Event release buf ref received in invalid state - discard\n"); + goto err_bad_event; + } + + /* + * Get the buffer_info entry for the + * device address. + */ + binfo = device_to_uvaddr(&inst->registeredbufs, + event_notify->packet_buffer); + if (!binfo) { + dprintk(VIDC_ERR, + "%s buffer not found in registered list\n", + __func__); + goto err_bad_event; + } + + /* Fill event data to be sent to client*/ + buf_event.type = V4L2_EVENT_RELEASE_BUFFER_REFERENCE; + ptr = (u32 *)buf_event.u.data; + ptr[0] = binfo->fd[0]; + ptr[1] = binfo->buff_off[0]; + + dprintk(VIDC_DBG, + "RELEASE REFERENCE EVENT FROM F/W - fd = %d offset = %d\n", + ptr[0], ptr[1]); + + /* Decrement buffer reference count*/ + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, + list) { + if (temp == binfo) { + buf_ref_put(inst, binfo); + break; + } + } + + /* + * Release buffer and remove from list + * if reference goes to zero. + */ + if (unmap_and_deregister_buf(inst, binfo)) + dprintk(VIDC_ERR, + "%s: buffer unmap failed\n", __func__); + mutex_unlock(&inst->registeredbufs.lock); + + /*send event to client*/ + v4l2_event_queue_fh(&inst->event_handler, &buf_event); + 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] = flag to indicate bit depth or/and pic struct changed + * ptr[3] = bit depth + * ptr[4] = pic struct (progressive or interlaced) + * ptr[5] = colour space + */ + + ptr = (u32 *)seq_changed_event.u.data; + ptr[2] = 0x0; + ptr[3] = inst->bit_depth; + ptr[4] = inst->pic_struct; + ptr[5] = inst->colour_space; + + if (inst->bit_depth != event_notify->bit_depth) { + inst->bit_depth = event_notify->bit_depth; + ptr[2] |= V4L2_EVENT_BITDEPTH_FLAG; + ptr[3] = inst->bit_depth; + event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + dprintk(VIDC_DBG, + "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to bit-depth change\n"); + } + + if (inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_NV12 && + event_notify->pic_struct != MSM_VIDC_PIC_STRUCT_UNKNOWN && + inst->pic_struct != event_notify->pic_struct) { + inst->pic_struct = event_notify->pic_struct; + event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + ptr[2] |= V4L2_EVENT_PICSTRUCT_FLAG; + ptr[4] = inst->pic_struct; + dprintk(VIDC_DBG, + "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to pic-struct change\n"); + } + + if (inst->bit_depth == MSM_VIDC_BIT_DEPTH_10 + && inst->colour_space != + event_notify->colour_space) { + inst->colour_space = event_notify->colour_space; + event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + ptr[2] |= V4L2_EVENT_COLOUR_SPACE_FLAG; + ptr[5] = inst->colour_space; + dprintk(VIDC_DBG, + "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to colour space change\n"); + } + + if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) { + dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT\n"); + inst->reconfig_height = event_notify->height; + inst->reconfig_width = event_notify->width; + inst->in_reconfig = true; + } else { + dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n"); + dprintk(VIDC_DBG, + "event_notify->height = %d event_notify->width = %d\n", + event_notify->height, + event_notify->width); + inst->prop.height[OUTPUT_PORT] = event_notify->height; + inst->prop.width[OUTPUT_PORT] = event_notify->width; + } + + inst->seqchanged_count++; + + if (inst->session_type == MSM_VIDC_DECODER) + msm_dcvs_init_load(inst); + + rc = msm_vidc_check_session_supported(inst); + if (!rc) { + seq_changed_event.type = event; + if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) { + u32 *ptr = NULL; + + ptr = (u32 *)seq_changed_event.u.data; + ptr[0] = event_notify->height; + ptr[1] = event_notify->width; + } + 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_WARN, "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(&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_WARN, "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_WARN, "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_WARN, "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_WARN, "Got a response for an inactive session\n"); + return; + } + + signal_session_msg_receipt(cmd, inst); + put_inst(inst); +} + +void validate_output_buffers(struct msm_vidc_inst *inst) +{ + struct internal_buf *binfo; + u32 buffers_owned_by_driver = 0; + struct hal_buffer_requirements *output_buf; + + output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + HAL_BUFFER_OUTPUT); + return; + } + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + if (binfo->buffer_ownership != DRIVER) { + dprintk(VIDC_DBG, + "This buffer is with FW %x\n", + binfo->smem.device_addr); + continue; + } + buffers_owned_by_driver++; + } + mutex_unlock(&inst->outputbufs.lock); + + if (buffers_owned_by_driver != output_buf->buffer_count_actual) + dprintk(VIDC_WARN, + "OUTPUT Buffer count mismatch %d of %d\n", + buffers_owned_by_driver, + output_buf->buffer_count_actual); + +} + +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst) +{ + struct internal_buf *binfo; + struct hfi_device *hdev; + struct vidc_frame_data frame_data = {0}; + struct hal_buffer_requirements *output_buf, *extra_buf; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + HAL_BUFFER_OUTPUT); + return 0; + } + dprintk(VIDC_DBG, + "output: num = %d, size = %d\n", + output_buf->buffer_count_actual, + output_buf->buffer_size); + + extra_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); + + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + if (binfo->buffer_ownership != DRIVER) + continue; + frame_data.alloc_len = output_buf->buffer_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 = binfo->smem.device_addr + + output_buf->buffer_size; + frame_data.buffer_type = HAL_BUFFER_OUTPUT; + frame_data.extradata_size = extra_buf ? + extra_buf->buffer_size : 0; + rc = call_hfi_op(hdev, session_ftb, + (void *) inst->session, &frame_data); + binfo->buffer_ownership = FIRMWARE; + } + mutex_unlock(&inst->outputbufs.lock); + + return 0; +} + +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_WARN, "Got a response for an inactive session\n"); + return; + } + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + validate_output_buffers(inst); + if (!inst->in_reconfig) { + rc = msm_comm_queue_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue output buffers: %d\n", + rc); + } + } + } + atomic_dec(&inst->in_flush); + 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_QCOM_CMD_FLUSH_OUTPUT; + break; + case HAL_FLUSH_OUTPUT: + ptr[0] = V4L2_QCOM_CMD_FLUSH_CAPTURE; + break; + case HAL_FLUSH_ALL: + ptr[0] |= V4L2_QCOM_CMD_FLUSH_CAPTURE; + ptr[0] |= V4L2_QCOM_CMD_FLUSH_OUTPUT; + break; + default: + dprintk(VIDC_ERR, "Invalid flush type received!"); + goto exit; + } + + dprintk(VIDC_DBG, + "Notify flush complete, flush_type: %x\n", flush_type); + v4l2_event_queue_fh(&inst->event_handler, &flush_event); + +exit: + 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_WARN, "Got a response for an inactive session\n"); + return; + } + + hdev = inst->core->device; + dprintk(VIDC_WARN, "Session error received for session %pK\n", inst); + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + + if (response->status == VIDC_ERR_MAX_CLIENTS) { + dprintk(VIDC_WARN, "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_WARN, "Unsupported bitstream in %pK", inst); + event = V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED; + } else { + dprintk(VIDC_WARN, "Unknown session error (%d) for %pK\n", + response->status, inst); + event = V4L2_EVENT_MSM_VIDC_SYS_ERROR; + } + + 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_WARN, "%s: Core %pK\n", __func__, core); + mutex_lock(&core->lock); + core->state = VIDC_CORE_INVALID; + + list_for_each_entry(inst, &core->instances, list) { + mutex_lock(&inst->lock); + inst->state = MSM_VIDC_CORE_INVALID; + dprintk(VIDC_WARN, + "%s Send sys error for inst %pK\n", __func__, inst); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_SYS_ERROR); + mutex_unlock(&inst->lock); + } + 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; + 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; + } + + dprintk(VIDC_WARN, "SYS_ERROR %d received for core %pK\n", cmd, core); + msm_comm_clean_notify_client(core); + + hdev = core->device; + mutex_lock(&core->lock); + if (core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_DBG, "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); +} + +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; + } + + hdev = inst->core->device; + mutex_lock(&inst->lock); + if (hdev && inst->session) { + dprintk(VIDC_DBG, "cleaning up instance: %pK\n", 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_WARN, "Got a response for an inactive session\n"); + return; + } + + signal_session_msg_receipt(cmd, inst); + show_stats(inst); + put_inst(inst); +} + +static struct vb2_buffer *get_vb_from_device_addr(struct buf_queue *bufq, + unsigned long dev_addr) +{ + struct vb2_buffer *vb = NULL; + struct vb2_queue *q = NULL; + int found = 0; + + if (!bufq) { + dprintk(VIDC_ERR, "Invalid parameter\n"); + return NULL; + } + q = &bufq->vb2_bufq; + mutex_lock(&bufq->lock); + list_for_each_entry(vb, &q->queued_list, queued_entry) { + if (vb->planes[0].m.userptr == dev_addr && + vb->state == VB2_BUF_STATE_ACTIVE) { + found = 1; + dprintk(VIDC_DBG, "Found v4l2_buf index : %d\n", + vb->index); + break; + } + } + mutex_unlock(&bufq->lock); + if (!found) { + dprintk(VIDC_DBG, + "Failed to find buffer in queued list: %#lx, qtype = %d\n", + dev_addr, q->type); + vb = NULL; + } + return vb; +} + +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 vb2_buffer *vb; + struct msm_vidc_inst *inst; + struct vidc_hal_ebd *empty_buf_done; + struct vb2_v4l2_buffer *vbuf = NULL; + + 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_WARN, "Got a response for an inactive session\n"); + return; + } + + vb = get_vb_from_device_addr(&inst->bufq[OUTPUT_PORT], + response->input_done.packet_buffer); + if (vb) { + vbuf = to_vb2_v4l2_buffer(vb); + vb->planes[0].bytesused = response->input_done.filled_len; + vb->planes[0].data_offset = response->input_done.offset; + if (vb->planes[0].data_offset > vb->planes[0].length) + dprintk(VIDC_INFO, "data_offset overflow length\n"); + if (vb->planes[0].bytesused > vb->planes[0].length) + dprintk(VIDC_INFO, "bytesused overflow length\n"); + if (vb->planes[0].m.userptr != + response->clnt_data) + dprintk(VIDC_INFO, "Client data != bufaddr\n"); + 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_DBG, "Received EOS buffer %pK\n", + (void *)empty_buf_done->packet_buffer); + goto exit; + } + + if (empty_buf_done) { + if (empty_buf_done->status == VIDC_ERR_NOT_SUPPORTED) { + dprintk(VIDC_INFO, + "Failed : Unsupported input stream\n"); + vbuf->flags |= + V4L2_QCOM_BUF_INPUT_UNSUPPORTED; + } + if (empty_buf_done->status == VIDC_ERR_BITSTREAM_ERR) { + dprintk(VIDC_INFO, + "Failed : Corrupted input stream\n"); + vbuf->flags |= + V4L2_BUF_FLAG_DATA_CORRUPT; + } + if (empty_buf_done->status == + VIDC_ERR_START_CODE_NOT_FOUND) { + vbuf->flags |= + V4L2_MSM_VIDC_BUF_START_CODE_NOT_FOUND; + dprintk(VIDC_INFO, + "Failed: Start code not found\n"); + } + if (empty_buf_done->flags & HAL_BUFFERFLAG_SYNCFRAME) + vbuf->flags |= + V4L2_QCOM_BUF_FLAG_IDRFRAME | + V4L2_BUF_FLAG_KEYFRAME; + } + dprintk(VIDC_DBG, + "Got ebd from hal: device_addr: %pa, alloc: %d, status: %#x, pic_type: %#x, flags: %#x\n", + &empty_buf_done->packet_buffer, + empty_buf_done->alloc_len, empty_buf_done->status, + empty_buf_done->picture_type, empty_buf_done->flags); + + mutex_lock(&inst->bufq[OUTPUT_PORT].lock); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + mutex_unlock(&inst->bufq[OUTPUT_PORT].lock); + msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD); + } +exit: + put_inst(inst); +} + +int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo) +{ + int cnt = 0; + + if (!inst || !binfo) + return -EINVAL; + + atomic_inc(&binfo->ref_count); + cnt = atomic_read(&binfo->ref_count); + if (cnt > 2) { + dprintk(VIDC_DBG, "%s: invalid ref_cnt: %d\n", __func__, cnt); + cnt = -EINVAL; + } + if (cnt == 2) + inst->buffers_held_in_driver++; + + dprintk(VIDC_DBG, "REF_GET[%d] fd[0] = %d\n", cnt, binfo->fd[0]); + + return cnt; +} + +int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo) +{ + int rc = 0; + int cnt; + bool release_buf = false; + bool qbuf_again = false; + + if (!inst || !binfo) + return -EINVAL; + + atomic_dec(&binfo->ref_count); + cnt = atomic_read(&binfo->ref_count); + dprintk(VIDC_DBG, "REF_PUT[%d] fd[0] = %d\n", cnt, binfo->fd[0]); + if (!cnt) + release_buf = true; + else if (cnt == 1) + qbuf_again = true; + else { + dprintk(VIDC_DBG, "%s: invalid ref_cnt: %d\n", __func__, cnt); + cnt = -EINVAL; + } + + if (cnt < 0) + return cnt; + + if (release_buf) { + /* + * We can not delete binfo here as we need to set the user + * virtual address saved in binfo->uvaddr to the dequeued v4l2 + * buffer. + * + * We will set the pending_deletion flag to true here and delete + * binfo from registered list in dqbuf after setting the uvaddr. + */ + dprintk(VIDC_DBG, "fd[0] = %d -> pending_deletion = true\n", + binfo->fd[0]); + binfo->pending_deletion = true; + } else if (qbuf_again) { + inst->buffers_held_in_driver--; + rc = qbuf_dynamic_buf(inst, binfo); + if (!rc) + return rc; + } + return cnt; +} + +static void handle_dynamic_buffer(struct msm_vidc_inst *inst, + phys_addr_t device_addr, u32 flags) +{ + struct buffer_info *binfo = NULL, *temp = NULL; + + /* + * Update reference count and release OR queue back the buffer, + * only when firmware is not holding a reference. + */ + if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) { + binfo = device_to_uvaddr(&inst->registeredbufs, device_addr); + if (!binfo) { + dprintk(VIDC_ERR, + "%s buffer not found in registered list\n", + __func__); + return; + } + if (flags & HAL_BUFFERFLAG_READONLY) { + dprintk(VIDC_DBG, + "FBD fd[0] = %d -> Reference with f/w, addr: %pa\n", + binfo->fd[0], &device_addr); + } else { + dprintk(VIDC_DBG, + "FBD fd[0] = %d -> FBD_ref_released, addr: %pa\n", + binfo->fd[0], &device_addr); + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, + list) { + if (temp == binfo) { + buf_ref_put(inst, binfo); + break; + } + } + mutex_unlock(&inst->registeredbufs.lock); + } + } +} + +static int handle_multi_stream_buffers(struct msm_vidc_inst *inst, + phys_addr_t 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_inst *inst; + struct vb2_buffer *vb = NULL; + struct vidc_hal_fbd *fill_buf_done; + enum hal_buffer buffer_type; + int extra_idx = 0; + int64_t time_usec = 0; + static int first_enc_frame = 1; + struct vb2_v4l2_buffer *vbuf = NULL; + struct buffer_info *buffer_info = NULL; + + 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_WARN, "Got a response for an inactive session\n"); + return; + } + + fill_buf_done = (struct vidc_hal_fbd *)&response->output_done; + buffer_type = msm_comm_get_hal_output_buffer(inst); + if (fill_buf_done->buffer_type == buffer_type) { + vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT], + fill_buf_done->packet_buffer1); + } 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 err_handle_fbd; + } + + if (vb) { + vbuf = to_vb2_v4l2_buffer(vb); + vb->planes[0].bytesused = fill_buf_done->filled_len1; + vb->planes[0].data_offset = fill_buf_done->offset1; + if (vb->planes[0].data_offset > vb->planes[0].length) + dprintk(VIDC_INFO, + "fbd:Overflow data_offset = %d; length = %d\n", + vb->planes[0].data_offset, + vb->planes[0].length); + if (vb->planes[0].bytesused > vb->planes[0].length) + dprintk(VIDC_INFO, + "fbd:Overflow bytesused = %d; length = %d\n", + vb->planes[0].bytesused, + vb->planes[0].length); + + buffer_info = device_to_uvaddr(&inst->registeredbufs, + fill_buf_done->packet_buffer1); + + if (!buffer_info) { + dprintk(VIDC_ERR, + "%s buffer not found in registered list\n", + __func__); + return; + } + + buffer_info->crop_data.nLeft = fill_buf_done->start_x_coord; + buffer_info->crop_data.nTop = fill_buf_done->start_y_coord; + buffer_info->crop_data.nWidth = fill_buf_done->frame_width; + buffer_info->crop_data.nHeight = fill_buf_done->frame_height; + buffer_info->crop_data.width_height[0] = + inst->prop.width[CAPTURE_PORT]; + buffer_info->crop_data.width_height[1] = + inst->prop.height[CAPTURE_PORT]; + + if (!(fill_buf_done->flags1 & + HAL_BUFFERFLAG_TIMESTAMPINVALID)) { + time_usec = fill_buf_done->timestamp_hi; + time_usec = (time_usec << 32) | + fill_buf_done->timestamp_lo; + } else { + time_usec = 0; + dprintk(VIDC_DBG, + "Set zero timestamp for buffer %pa, filled: %d, (hi:%u, lo:%u)\n", + &fill_buf_done->packet_buffer1, + fill_buf_done->filled_len1, + fill_buf_done->timestamp_hi, + fill_buf_done->timestamp_lo); + } + vb->timestamp = (time_usec * NSEC_PER_USEC); + vbuf->flags = 0; + extra_idx = + EXTRADATA_IDX(inst->fmts[CAPTURE_PORT].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + vb->planes[extra_idx].m.userptr = + (unsigned long)fill_buf_done->extra_data_buffer; + vb->planes[extra_idx].bytesused = + vb->planes[extra_idx].length; + vb->planes[extra_idx].data_offset = 0; + } + + handle_dynamic_buffer(inst, fill_buf_done->packet_buffer1, + fill_buf_done->flags1); + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_READONLY) + vbuf->flags |= V4L2_QCOM_BUF_FLAG_READONLY; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS) + vbuf->flags |= V4L2_QCOM_BUF_FLAG_EOS; + /* if (fill_buf_done->flags1 & HAL_BUFFERFLAG_ENDOFFRAME) + * vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_ENDOFFRAME; + */ + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG) + vbuf->flags &= ~V4L2_QCOM_BUF_FLAG_CODECCONFIG; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME) + vbuf->flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOSEQ) + vbuf->flags |= V4L2_QCOM_BUF_FLAG_EOSEQ; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DECODEONLY) + vbuf->flags |= V4L2_QCOM_BUF_FLAG_DECODEONLY; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT) + vbuf->flags |= V4L2_BUF_FLAG_DATA_CORRUPT; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME) + vbuf->flags |= V4L2_QCOM_BUF_DROP_FRAME; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_MBAFF) + vbuf->flags |= V4L2_MSM_BUF_FLAG_MBAFF; + + switch (fill_buf_done->picture_type) { + case HAL_PICTURE_IDR: + vbuf->flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME; + vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME; + break; + case HAL_PICTURE_I: + vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME; + break; + case HAL_PICTURE_P: + vbuf->flags |= V4L2_BUF_FLAG_PFRAME; + break; + case HAL_PICTURE_B: + vbuf->flags |= V4L2_BUF_FLAG_BFRAME; + break; + case HAL_FRAME_NOTCODED: + case HAL_UNUSED_PICT: + /* Do we need to care about these? */ + case HAL_FRAME_YUV: + break; + default: + break; + } + + inst->count.fbd++; + + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + dprintk(VIDC_DBG, + "extradata: userptr = %pK;" + " bytesused = %d; length = %d\n", + (u8 *)vb->planes[extra_idx].m.userptr, + vb->planes[extra_idx].bytesused, + vb->planes[extra_idx].length); + } + if (first_enc_frame == 1) { + boot_stats_init(); + pr_debug("KPI: First Encoded frame received\n"); + first_enc_frame++; + } + dprintk(VIDC_DBG, + "Got fbd from hal: device_addr: %pa, alloc: %d, filled: %d, offset: %d, ts: %lld, flags: %#x, crop: %d %d %d %d, pic_type: %#x, mark_data: %#x\n", + &fill_buf_done->packet_buffer1, fill_buf_done->alloc_len1, + fill_buf_done->filled_len1, fill_buf_done->offset1, time_usec, + fill_buf_done->flags1, fill_buf_done->start_x_coord, + fill_buf_done->start_y_coord, fill_buf_done->frame_width, + fill_buf_done->frame_height, fill_buf_done->picture_type, + fill_buf_done->mark_data); + + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); + msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD); + } + +err_handle_fbd: + put_inst(inst); +} + +static void handle_seq_hdr_done(enum hal_command_response cmd, void *data) +{ + struct msm_vidc_cb_data_done *response = data; + struct msm_vidc_inst *inst; + struct vb2_buffer *vb; + struct vidc_hal_fbd *fill_buf_done; + struct vb2_v4l2_buffer *vbuf; + + 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_WARN, "Got a response for an inactive session\n"); + return; + } + + fill_buf_done = (struct vidc_hal_fbd *)&response->output_done; + vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT], + fill_buf_done->packet_buffer1); + if (!vb) { + dprintk(VIDC_ERR, + "Failed to find video buffer for seq_hdr_done: %pa\n", + &fill_buf_done->packet_buffer1); + goto err_seq_hdr_done; + } + vbuf = to_vb2_v4l2_buffer(vb); + vb->planes[0].bytesused = fill_buf_done->filled_len1; + vb->planes[0].data_offset = fill_buf_done->offset1; + + vbuf->flags = V4L2_QCOM_BUF_FLAG_CODECCONFIG; + vb->timestamp = 0; + + dprintk(VIDC_DBG, "Filled length = %d; offset = %d; flags %x\n", + vb->planes[0].bytesused, + vb->planes[0].data_offset, + vbuf->flags); + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); + +err_seq_hdr_done: + put_inst(inst); +} + +void handle_cmd_response(enum hal_command_response cmd, void *data) +{ + dprintk(VIDC_DBG, "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_SESSION_GET_SEQ_HDR_DONE: + handle_seq_hdr_done(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; + default: + dprintk(VIDC_DBG, "response unhandled: %d\n", cmd); + break; + } +} + +int msm_comm_scale_clocks(struct msm_vidc_core *core) +{ + int num_mbs_per_sec = + msm_comm_get_load(core, MSM_VIDC_ENCODER, LOAD_CALC_NO_QUIRKS) + + msm_comm_get_load(core, MSM_VIDC_DECODER, LOAD_CALC_NO_QUIRKS); + return msm_comm_scale_clocks_load(core, num_mbs_per_sec, + LOAD_CALC_NO_QUIRKS); +} + +int msm_comm_scale_clocks_load(struct msm_vidc_core *core, + int num_mbs_per_sec, enum load_calc_quirks quirks) +{ + int rc = 0; + struct hfi_device *hdev; + struct msm_vidc_inst *inst = NULL; + unsigned long instant_bitrate = 0; + int num_sessions = 0; + struct vidc_clk_scale_data clk_scale_data = { {0} }; + int codec = 0; + + if (!core) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, core); + return -EINVAL; + } + + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle: %pK\n", + __func__, hdev); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + + codec = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT].fourcc : + inst->fmts[CAPTURE_PORT].fourcc; + + if (msm_comm_turbo_session(inst)) + clk_scale_data.power_mode[num_sessions] = + VIDC_POWER_TURBO; + else if (is_low_power_session(inst)) + clk_scale_data.power_mode[num_sessions] = + VIDC_POWER_LOW; + else + clk_scale_data.power_mode[num_sessions] = + VIDC_POWER_NORMAL; + + if (inst->dcvs_mode) + clk_scale_data.load[num_sessions] = inst->dcvs.load; + else + clk_scale_data.load[num_sessions] = + msm_comm_get_inst_load(inst, quirks); + + clk_scale_data.session[num_sessions] = + VIDC_VOTE_DATA_SESSION_VAL( + get_hal_codec(codec), + get_hal_domain(inst->session_type)); + num_sessions++; + + if (inst->instant_bitrate > instant_bitrate) + instant_bitrate = inst->instant_bitrate; + + } + clk_scale_data.num_sessions = num_sessions; + mutex_unlock(&core->lock); + + + rc = call_hfi_op(hdev, scale_clocks, + hdev->hfi_device_data, num_mbs_per_sec, + &clk_scale_data, instant_bitrate); + if (rc) + dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc); + + return rc; +} + +void msm_comm_scale_clocks_and_bus(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 params\n", __func__); + return; + } + core = inst->core; + hdev = core->device; + + if (msm_comm_scale_clocks(core)) { + dprintk(VIDC_WARN, + "Failed to scale clocks. Performance might be impacted\n"); + } + if (msm_comm_vote_bus(core)) { + dprintk(VIDC_WARN, + "Failed to scale DDR bus. Performance might be impacted\n"); + } +} + +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 unsigned long msm_comm_get_clock_rate(struct msm_vidc_core *core) +{ + struct hfi_device *hdev; + unsigned long freq = 0; + + if (!core || !core->device) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return -EINVAL; + } + hdev = core->device; + + freq = call_hfi_op(hdev, get_core_clock_rate, hdev->hfi_device_data, 1); + dprintk(VIDC_DBG, "clock freq %ld\n", freq); + + return freq; +} + +static bool is_core_turbo(struct msm_vidc_core *core, unsigned long freq) +{ + int i = 0; + struct msm_vidc_platform_resources *res = &core->resources; + struct load_freq_table *table = res->load_freq_tbl; + u32 max_freq = 0; + + for (i = 0; i < res->load_freq_tbl_size; i++) { + if (max_freq < table[i].freq) + max_freq = table[i].freq; + } + 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_DBG, + "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 = msm_comm_get_clock_rate(core); + + is_turbo = is_core_turbo(core, freq); + dprintk(VIDC_DBG, + "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; +} + +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); + + rc = call_hfi_op(hdev, session_abort, (void *)inst->session); + if (rc) { + dprintk(VIDC_ERR, + "%s session_abort failed rc: %d\n", __func__, rc); + return rc; + } + rc = wait_for_completion_timeout( + &inst->completions[abort_completion], + msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, + "%s: Wait interrupted or timed out [%pK]: %d\n", + __func__, inst, abort_completion); + WARN_ON(msm_vidc_debug_timeout); + rc = -EBUSY; + } else { + rc = 0; + } + msm_comm_session_clean(inst); + 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_WARN, "%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_WARN, + "%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_WARN, + "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_INFO, "Video core: %d is already in state: %d\n", + core->id, core->state); + goto exit; + } + dprintk(VIDC_DBG, "Waiting for SYS_INIT_DONE\n"); + rc = wait_for_completion_timeout( + &core->completions[SYS_MSG_INDEX(HAL_SYS_INIT_DONE)], + msecs_to_jiffies(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)); + WARN_ON(msm_vidc_debug_timeout); + rc = -EIO; + goto exit; + } else { + core->state = VIDC_CORE_INIT_DONE; + rc = 0; + } + dprintk(VIDC_DBG, "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 = 0; + 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_INFO, "Video core: %d is already in state: %d\n", + core->id, core->state); + goto core_already_inited; + } + if (!core->capabilities) { + core->capabilities = kcalloc(VIDC_MAX_SESSIONS, + sizeof(struct msm_vidc_capability), GFP_KERNEL); + if (!core->capabilities) { + dprintk(VIDC_ERR, + "%s: failed to allocate capabilities\n", + __func__); + rc = -ENOMEM; + goto fail_cap_alloc; + } + } else { + dprintk(VIDC_WARN, + "%s: capabilities memory is expected to be freed\n", + __func__); + } + + 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; + } + core->state = VIDC_CORE_INIT; + core->smmu_fault_handled = false; +core_already_inited: + change_inst_state(inst, MSM_VIDC_CORE_INIT); + mutex_unlock(&core->lock); + return rc; + +fail_core_init: + kfree(core->capabilities); +fail_cap_alloc: + core->capabilities = NULL; + 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_INFO, "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_INVALID ? + 0 : msm_vidc_firmware_unload_delay)); + + dprintk(VIDC_DBG, "firmware unload delayed by %u ms\n", + core->state == VIDC_CORE_INVALID ? + 0 : msm_vidc_firmware_unload_delay); + } + +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(int flipped_state, + struct msm_vidc_inst *inst) +{ + int rc = 0; + int fourcc = 0; + struct hfi_device *hdev; + + 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_INFO, "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + if (inst->session_type == MSM_VIDC_DECODER) { + fourcc = inst->fmts[OUTPUT_PORT].fourcc; + } else if (inst->session_type == MSM_VIDC_ENCODER) { + fourcc = inst->fmts[CAPTURE_PORT].fourcc; + } else { + dprintk(VIDC_ERR, "Invalid session\n"); + return -EINVAL; + } + + 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; + } + 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; + + dprintk(VIDC_ERR, "Running instances:\n"); + dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%4s\n", + "type", "w", "h", "fps", "prop"); + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + 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 (msm_comm_turbo_session(temp)) + strlcat(properties, "T", sizeof(properties)); + + dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4s\n", + temp->session_type, + max(temp->prop.width[CAPTURE_PORT], + temp->prop.width[OUTPUT_PORT]), + max(temp->prop.height[CAPTURE_PORT], + temp->prop.height[OUTPUT_PORT]), + temp->prop.fps, 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; + } + + core = inst->core; + if (core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core is in bad state can't do load res\n"); + return -EINVAL; + } + + if (inst->state == MSM_VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Instance is in invalid state can't do load res\n"); + return -EINVAL; + } + + 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.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); + inst->state = MSM_VIDC_CORE_INVALID; + msm_comm_kill_session(inst); + return -EBUSY; + } + + hdev = core->device; + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) { + dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + + 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 || + inst->core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core is in bad state can't do start\n"); + return -EINVAL; + } + + hdev = inst->core->device; + + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + 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; + } + hdev = inst->core->device; + + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + dprintk(VIDC_DBG, "Send Stop to hal\n"); + rc = call_hfi_op(hdev, session_stop, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, "Failed to send stop\n"); + 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; + } + hdev = inst->core->device; + + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + dprintk(VIDC_DBG, + "Send release res to hal\n"); + 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; + } + hdev = inst->core->device; + if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) { + dprintk(VIDC_INFO, + "inst: %pK is already in state: %d\n", + inst, inst->state); + goto exit; + } + dprintk(VIDC_DBG, + "Send session close to hal\n"); + 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; + } + + mutex_lock(&core->lock); + if (core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "%s - fw is not in proper state, skip suspend\n", + __func__); + rc = -EINVAL; + goto exit; + } + + rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data); + if (rc) + dprintk(VIDC_WARN, "Failed to suspend\n"); + +exit: + mutex_unlock(&core->lock); + 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]; + } + return NULL; +} + +static int set_output_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; + struct hal_buffer_requirements *output_buf, *extradata_buf; + int i; + struct hfi_device *hdev; + struct hal_buffer_size_minimum b; + + hdev = inst->core->device; + + output_buf = get_buff_req_buffer(inst, buffer_type); + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + buffer_type); + return 0; + } + dprintk(VIDC_DBG, + "output: num = %d, size = %d\n", + output_buf->buffer_count_actual, + output_buf->buffer_size); + + buffer_size = output_buf->buffer_size; + b.buffer_type = buffer_type; + b.buffer_size = buffer_size; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM, + &b); + + extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); + if (extradata_buf) { + dprintk(VIDC_DBG, + "extradata: num = %d, size = %d\n", + extradata_buf->buffer_count_actual, + extradata_buf->buffer_size); + buffer_size += extradata_buf->buffer_size; + } else { + dprintk(VIDC_DBG, + "This extradata buffer not required, buffer_type: %x\n", + buffer_type); + } + + if (inst->flags & VIDC_SECURE) + smem_flags |= SMEM_SECURE; + + if (output_buf->buffer_size) { + for (i = 0; i < output_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; + } + 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_DBG, "Output buffer address: %#x\n", + binfo->smem.device_addr); + + if (inst->buffer_mode_set[CAPTURE_PORT] == + HAL_BUFFER_MODE_STATIC) { + struct vidc_buffer_addr_info buffer_info = {0}; + + buffer_info.buffer_size = + output_buf->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 = + binfo->smem.device_addr + + output_buf->buffer_size; + if (extradata_buf) + buffer_info.extradata_size = + extradata_buf->buffer_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; + + rc = msm_comm_smem_cache_operations(inst, + handle, SMEM_CACHE_CLEAN); + if (rc) { + dprintk(VIDC_WARN, + "Failed to clean cache. Undefined behavior\n"); + } + + 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_DBG, "%s %s buffer : %pa\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_DBG, + "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; + 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_DBG, + "This internal buffer not required, buffer_type: %x\n", + buffer_type); + return 0; + } + + dprintk(VIDC_DBG, "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; + struct msm_vidc_core *core; + + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %pK\n", inst); + return -EINVAL; + } + dprintk(VIDC_DBG, + "Trying to move inst: %pK from: %#x to %#x\n", + inst, inst->state, state); + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %pK\n", inst); + return -EINVAL; + } + mutex_lock(&inst->sync_lock); + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core is in bad state can't change the state\n"); + rc = -EINVAL; + goto exit; + } + flipped_state = get_flipped_state(inst->state, state); + dprintk(VIDC_DBG, + "flipped_state = %#x\n", 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 = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE, + HAL_SESSION_INIT_DONE); + 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_DBG, "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_DBG, + "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_DBG, "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); + else + trace_msm_vidc_common_state_change((void *)inst, + inst->state, state); + 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_QCOM_CMD_FLUSH: + if (core->state != VIDC_CORE_INVALID && + inst->state == MSM_VIDC_CORE_INVALID) { + rc = msm_comm_kill_session(inst); + if (rc) + dprintk(VIDC_ERR, + "Fail to clean session: %d\n", + rc); + } + rc = msm_comm_flush(inst, flags); + if (rc) { + dprintk(VIDC_ERR, + "Failed to flush buffers: %d\n", rc); + } + break; + case V4L2_DEC_QCOM_CMD_RECONFIG_HINT: + { + u32 *ptr = NULL; + struct hal_buffer_requirements *output_buf; + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "Getting buffer requirements failed: %d\n", + rc); + break; + } + + output_buf = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + if (output_buf) { + if (dec) { + ptr = (u32 *)dec->raw.data; + ptr[0] = output_buf->buffer_size; + ptr[1] = output_buf->buffer_count_actual; + dprintk(VIDC_DBG, + "Reconfig hint, size is %u, count is %u\n", + ptr[0], ptr[1]); + } else { + dprintk(VIDC_ERR, "Null decoder\n"); + } + } else { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + HAL_BUFFER_OUTPUT); + } + break; + } + case V4L2_DEC_CMD_STOP: + { + struct vidc_frame_data data = {0}; + struct hfi_device *hdev = NULL; + struct eos_buf *binfo = NULL; + u32 smem_flags = SMEM_UNCACHED; + + if (inst->state != MSM_VIDC_START_DONE) { + dprintk(VIDC_DBG, + "Inst = %pK is not ready for EOS\n", inst); + rc = -EINVAL; + break; + } + if (inst->session_type != MSM_VIDC_DECODER) { + dprintk(VIDC_DBG, + "Non-Decoder session. DEC_STOP is not valid\n"); + rc = -EINVAL; + 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); + + 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 = LLONG_MAX; + data.extradata_addr = data.device_addr; + data.extradata_size = 0; + dprintk(VIDC_DBG, "Queueing EOS buffer %pK\n", + (void *)data.device_addr); + hdev = inst->core->device; + + rc = call_hfi_op(hdev, session_etb, inst->session, &data); + break; + } + + default: + dprintk(VIDC_ERR, "Unknown Command %d\n", which_cmd); + rc = -ENOTSUPP; + break; + } + return rc; +} + +static void populate_frame_data(struct vidc_frame_data *data, + const struct vb2_buffer *vb, struct msm_vidc_inst *inst) +{ + u64 time_usec; + int extra_idx; + enum v4l2_buf_type type = vb->type; + enum vidc_ports port = type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? + OUTPUT_PORT : CAPTURE_PORT; + struct vb2_v4l2_buffer *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 = vb->planes[0].m.userptr; + data->timestamp = time_usec; + data->flags = 0; + data->clnt_data = data->device_addr; + + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + bool pic_decoding_mode = msm_comm_g_ctrl_for_id(inst, + V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE); + + data->buffer_type = HAL_BUFFER_INPUT; + data->filled_len = vb->planes[0].bytesused; + data->offset = vb->planes[0].data_offset; + + if (vbuf->flags & V4L2_QCOM_BUF_FLAG_EOS) + data->flags |= HAL_BUFFERFLAG_EOS; + + if (vbuf->flags & V4L2_MSM_BUF_FLAG_YUV_601_709_CLAMP) + data->flags |= HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP; + + if (vbuf->flags & V4L2_QCOM_BUF_FLAG_CODECCONFIG) + data->flags |= HAL_BUFFERFLAG_CODECCONFIG; + + if (vbuf->flags & V4L2_QCOM_BUF_FLAG_DECODEONLY) + data->flags |= HAL_BUFFERFLAG_DECODEONLY; + + if (vbuf->flags & V4L2_QCOM_BUF_TIMESTAMP_INVALID) + data->timestamp = LLONG_MAX; + + /* XXX: This is a dirty hack necessitated by the firmware, + * which refuses to issue FBDs for non I-frames in Picture Type + * Decoding mode, unless we pass in non-zero value in mark_data + * and mark_target. + */ + data->mark_data = data->mark_target = + pic_decoding_mode ? 0xdeadbeef : 0; + + } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + data->buffer_type = msm_comm_get_hal_output_buffer(inst); + } + + extra_idx = EXTRADATA_IDX(inst->fmts[port].num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES && + vb->planes[extra_idx].m.userptr) { + data->extradata_addr = vb->planes[extra_idx].m.userptr; + data->extradata_size = vb->planes[extra_idx].length; + data->flags |= HAL_BUFFERFLAG_EXTRADATA; + } +} + +static unsigned int count_single_batch(struct msm_vidc_list *list, + enum v4l2_buf_type type) +{ + struct vb2_buf_entry *buf; + int count = 0; + struct vb2_v4l2_buffer *vbuf = NULL; + + mutex_lock(&list->lock); + list_for_each_entry(buf, &list->list, list) { + if (buf->vb->type != type) + continue; + + ++count; + + vbuf = to_vb2_v4l2_buffer(buf->vb); + if (!(vbuf->flags & V4L2_MSM_BUF_FLAG_DEFER)) + goto found_batch; + } + /* don't have a full batch */ + count = 0; + +found_batch: + mutex_unlock(&list->lock); + return count; +} + +static unsigned int count_buffers(struct msm_vidc_list *list, + enum v4l2_buf_type type) +{ + struct vb2_buf_entry *buf; + int count = 0; + + mutex_lock(&list->lock); + list_for_each_entry(buf, &list->list, list) { + if (buf->vb->type != type) + continue; + + ++count; + } + mutex_unlock(&list->lock); + + return count; +} + +static void log_frame(struct msm_vidc_inst *inst, struct vidc_frame_data *data, + enum v4l2_buf_type type) +{ + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + dprintk(VIDC_DBG, + "Sending etb (%pa) to hal: filled: %d, ts: %lld, flags = %#x\n", + &data->device_addr, data->filled_len, + data->timestamp, data->flags); + msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_ETB); + + if (msm_vidc_bitrate_clock_scaling && + inst->session_type == MSM_VIDC_DECODER && + !inst->dcvs_mode) + inst->instant_bitrate = + data->filled_len * 8 * inst->prop.fps; + else + inst->instant_bitrate = 0; + } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + dprintk(VIDC_DBG, + "Sending ftb (%pa) to hal: size: %d, ts: %lld, flags = %#x\n", + &data->device_addr, data->alloc_len, + data->timestamp, data->flags); + msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FTB); + } + + msm_dcvs_check_and_scale_clocks(inst, + type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + if (msm_vidc_bitrate_clock_scaling && !inst->dcvs_mode && + type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + inst->session_type == MSM_VIDC_DECODER) + if (msm_comm_scale_clocks(inst->core)) + dprintk(VIDC_WARN, + "Failed to scale clocks. Performance might be impacted\n"); +} + +static int request_seq_header(struct msm_vidc_inst *inst, + struct vidc_frame_data *data) +{ + struct vidc_seq_hdr seq_hdr = { + .seq_hdr = data->device_addr, + .seq_hdr_len = data->alloc_len, + }; + + dprintk(VIDC_DBG, "Requesting sequence header in %pa\n", + &seq_hdr.seq_hdr); + return call_hfi_op(inst->core->device, session_get_seq_hdr, + inst->session, &seq_hdr); +} + +/* + * Attempts to queue `vb` to hardware. If, for various reasons, the buffer + * cannot be queued to hardware, the buffer will be staged for commit in the + * pending queue. Once the hardware reaches a good state (or if `vb` is NULL, + * the subsequent *_qbuf will commit the previously staged buffers to hardware. + */ +int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb) +{ + int rc = 0; + int capture_count, output_count; + struct msm_vidc_core *core; + struct hfi_device *hdev; + struct { + struct vidc_frame_data *data; + int count; + } etbs, ftbs; + bool defer = false, batch_mode; + struct vb2_buf_entry *temp, *next; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (!inst) { + dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + core = inst->core; + hdev = core->device; + + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID || + core->state == VIDC_CORE_UNINIT) { + dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n"); + return -EINVAL; + } + + /* Stick the buffer into the pendinq, we'll pop it out later on + * if we want to commit it to hardware + */ + if (vb) { + temp = kzalloc(sizeof(*temp), GFP_KERNEL); + if (!temp) { + dprintk(VIDC_ERR, "Out of memory\n"); + goto err_no_mem; + } + + temp->vb = vb; + mutex_lock(&inst->pendingq.lock); + list_add_tail(&temp->list, &inst->pendingq.list); + mutex_unlock(&inst->pendingq.lock); + } + + batch_mode = msm_comm_g_ctrl_for_id(inst, V4L2_CID_VIDC_QBUF_MODE) + == V4L2_VIDC_QBUF_BATCHED; + capture_count = (batch_mode ? &count_single_batch : &count_buffers) + (&inst->pendingq, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + output_count = (batch_mode ? &count_single_batch : &count_buffers) + (&inst->pendingq, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + /* + * Somewhat complicated logic to prevent queuing the buffer to hardware. + * Don't queue if: + * 1) Hardware isn't ready (that's simple) + */ + defer = defer ?: inst->state != MSM_VIDC_START_DONE; + + /* + * 2) The client explicitly tells us not to because it wants this + * buffer to be batched with future frames. The batch size (on both + * capabilities) is completely determined by the client. + */ + defer = defer ?: vbuf && vbuf->flags & V4L2_MSM_BUF_FLAG_DEFER; + + /* 3) If we're in batch mode, we must have full batches of both types */ + defer = defer ?: batch_mode && (!output_count || !capture_count); + + if (defer) { + dprintk(VIDC_DBG, "Deferring queue of %pK\n", vb); + return 0; + } + + dprintk(VIDC_DBG, "%sing %d etbs and %d ftbs\n", + batch_mode ? "Batch" : "Process", + output_count, capture_count); + + etbs.data = kcalloc(output_count, sizeof(*etbs.data), GFP_KERNEL); + ftbs.data = kcalloc(capture_count, sizeof(*ftbs.data), GFP_KERNEL); + /* Note that it's perfectly normal for (e|f)tbs.data to be NULL if + * we're not in batch mode (i.e. (output|capture)_count == 0) + */ + if ((!etbs.data && output_count) || + (!ftbs.data && capture_count)) { + dprintk(VIDC_ERR, "Failed to alloc memory for batching\n"); + kfree(etbs.data); + etbs.data = NULL; + + kfree(ftbs.data); + ftbs.data = NULL; + goto err_no_mem; + } + + etbs.count = ftbs.count = 0; + + /* + * Try to collect all pending buffers into 2 batches of ftb and etb + * Note that these "batches" might be empty if we're no in batching mode + * and the pendingq is empty + */ + mutex_lock(&inst->pendingq.lock); + list_for_each_entry_safe(temp, next, &inst->pendingq.list, list) { + struct vidc_frame_data *frame_data = NULL; + + switch (temp->vb->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ftbs.count < capture_count && ftbs.data) + frame_data = &ftbs.data[ftbs.count++]; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (etbs.count < output_count && etbs.data) + frame_data = &etbs.data[etbs.count++]; + break; + default: + break; + } + + if (!frame_data) + continue; + + populate_frame_data(frame_data, temp->vb, inst); + + list_del(&temp->list); + kfree(temp); + } + mutex_unlock(&inst->pendingq.lock); + + /* Finally commit all our frame(s) to H/W */ + if (batch_mode) { + int ftb_index = 0, c = 0; + + for (c = 0; atomic_read(&inst->seq_hdr_reqs) > 0; ++c) { + rc = request_seq_header(inst, &ftbs.data[c]); + if (rc) { + dprintk(VIDC_ERR, + "Failed requesting sequence header: %d\n", + rc); + goto err_bad_input; + } + + atomic_dec(&inst->seq_hdr_reqs); + } + + ftb_index = c; + rc = call_hfi_op(hdev, session_process_batch, inst->session, + etbs.count, etbs.data, + ftbs.count - ftb_index, &ftbs.data[ftb_index]); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue batch of %d ETBs and %d FTBs\n", + etbs.count, ftbs.count); + goto err_bad_input; + } + + for (c = ftb_index; c < ftbs.count; ++c) { + log_frame(inst, &ftbs.data[c], + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + } + + for (c = 0; c < etbs.count; ++c) { + log_frame(inst, &etbs.data[c], + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + } + } + + if (!batch_mode && etbs.count) { + int c = 0; + + for (c = 0; c < etbs.count; ++c) { + struct vidc_frame_data *frame_data = &etbs.data[c]; + + rc = call_hfi_op(hdev, session_etb, inst->session, + frame_data); + if (rc) { + dprintk(VIDC_ERR, "Failed to issue etb: %d\n", + rc); + goto err_bad_input; + } + + log_frame(inst, frame_data, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + } + } + + if (!batch_mode && ftbs.count) { + int c = 0; + + for (c = 0; atomic_read(&inst->seq_hdr_reqs) > 0; ++c) { + rc = request_seq_header(inst, &ftbs.data[c]); + if (rc) { + dprintk(VIDC_ERR, + "Failed requesting sequence header: %d\n", + rc); + goto err_bad_input; + } + + atomic_dec(&inst->seq_hdr_reqs); + } + + for (; c < ftbs.count; ++c) { + struct vidc_frame_data *frame_data = &ftbs.data[c]; + + rc = call_hfi_op(hdev, session_ftb, + inst->session, frame_data); + if (rc) { + dprintk(VIDC_ERR, "Failed to issue ftb: %d\n", + rc); + goto err_bad_input; + } + + log_frame(inst, frame_data, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + } + } + +err_bad_input: + if (rc) + dprintk(VIDC_ERR, "Failed to queue buffer\n"); + + kfree(etbs.data); + kfree(ftbs.data); +err_no_mem: + return rc; +} + +int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst) +{ + int rc = 0, i = 0; + union hal_get_property hprop; + + rc = msm_comm_try_get_prop(inst, HAL_PARAM_GET_BUFFER_REQUIREMENTS, + &hprop); + if (rc) { + dprintk(VIDC_ERR, "Failed getting buffer requirements: %d", rc); + return rc; + } + + dprintk(VIDC_DBG, "Buffer requirements:\n"); + dprintk(VIDC_DBG, "%15s %8s %8s\n", "buffer type", "count", "size"); + for (i = 0; i < HAL_BUFFER_MAX; i++) { + struct hal_buffer_requirements req = hprop.buf_req.buffer[i]; + + inst->buff_req.buffer[i] = req; + dprintk(VIDC_DBG, "%15s %8d %8d\n", + get_buffer_name(req.buffer_type), + req.buffer_count_actual, req.buffer_size); + } + + dprintk(VIDC_PROF, "Input buffers: %d, Output buffers: %d\n", + inst->buff_req.buffer[0].buffer_count_actual, + inst->buff_req.buffer[1].buffer_count_actual); + return rc; +} + +int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype, + 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); + + switch (ptype) { + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + case HAL_CONFIG_VDEC_ENTROPY: + rc = call_hfi_op(hdev, session_get_property, inst->session, + ptype); + break; + case HAL_PARAM_GET_BUFFER_REQUIREMENTS: + rc = call_hfi_op(hdev, session_get_buf_req, inst->session); + break; + default: + rc = -EAGAIN; + break; + } + + 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(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)); + inst->state = MSM_VIDC_CORE_INVALID; + msm_comm_kill_session(inst); + WARN_ON(msm_vidc_debug_timeout); + 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_output_buffers(struct msm_vidc_inst *inst) +{ + 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_DBG, "%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 (!handle) { + dprintk(VIDC_ERR, "%s - invalid handle\n", __func__); + goto exit; + } + + 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[CAPTURE_PORT] == + HAL_BUFFER_MODE_STATIC && + inst->state != MSM_VIDC_CORE_INVALID && + core->state != VIDC_CORE_INVALID) { + buffer_info.response_required = false; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (rc) { + dprintk(VIDC_WARN, + "Rel output buf fail:%pa, %d\n", + &buffer_info.align_device_addr, + buffer_info.buffer_size); + } + } + + list_del(&buf->list); + msm_comm_smem_free(inst, &buf->smem); + kfree(buf); + } + +exit: + 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_DBG, + "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; + if (inst->state != MSM_VIDC_CORE_INVALID && + core->state != VIDC_CORE_INVALID) { + buffer_info.response_required = true; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (rc) { + dprintk(VIDC_WARN, + "Rel scrtch buf fail:%pa, %d\n", + &buffer_info.align_device_addr, + buffer_info.buffer_size); + } + mutex_unlock(&inst->scratchbufs.lock); + rc = wait_for_sess_signal_receipt(inst, + HAL_SESSION_RELEASE_BUFFER_DONE); + if (rc) { + change_inst_state(inst, + MSM_VIDC_CORE_INVALID); + msm_comm_kill_session(inst); + } + mutex_lock(&inst->scratchbufs.lock); + } + + /*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_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; + if (inst->state != MSM_VIDC_CORE_INVALID && + core->state != VIDC_CORE_INVALID) { + buffer_info.response_required = true; + rc = call_hfi_op(hdev, session_release_buffers, + (void *)inst->session, &buffer_info); + if (rc) { + dprintk(VIDC_WARN, + "Rel prst buf fail:%pa, %d\n", + &buffer_info.align_device_addr, + buffer_info.buffer_size); + } + mutex_unlock(&inst->persistbufs.lock); + rc = wait_for_sess_signal_receipt(inst, + HAL_SESSION_RELEASE_BUFFER_DONE); + if (rc) { + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + msm_comm_kill_session(inst); + } + mutex_lock(&inst->persistbufs.lock); + } + list_del(&buf->list); + msm_comm_smem_free(inst, handle); + kfree(buf); + } + mutex_unlock(&inst->persistbufs.lock); + return rc; +} + +int msm_comm_try_set_prop(struct msm_vidc_inst *inst, + enum hal_property ptype, void *pdata) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid input: %pK\n", inst); + return -EINVAL; + } + + if (!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) { + dprintk(VIDC_ERR, "Not in proper state to set property\n"); + rc = -EAGAIN; + goto exit; + } + rc = call_hfi_op(hdev, session_set_property, (void *)inst->session, + ptype, pdata); + if (rc) + dprintk(VIDC_ERR, "Failed to set hal property for framesize\n"); +exit: + mutex_unlock(&inst->sync_lock); + return rc; +} + +int msm_comm_set_output_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_output_buffers(inst)) + dprintk(VIDC_WARN, "Failed to release output buffers\n"); + + rc = set_output_buffers(inst, HAL_BUFFER_OUTPUT); + if (rc) + goto error; + return rc; +error: + msm_comm_release_output_buffers(inst); + 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_WARN, "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_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[] = {OUTPUT_PORT, CAPTURE_PORT}; + int c = 0; + + for (c = 0; c < ARRAY_SIZE(ports); ++c) { + enum vidc_ports port = ports[c]; + + dprintk(VIDC_DBG, "Flushing buffers of type %d in bad state\n", + port); + 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); + + vb->planes[0].bytesused = 0; + vb->planes[0].data_offset = 0; + + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } + mutex_unlock(&inst->bufq[port].lock); + } + + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE); +} + +void msm_comm_flush_dynamic_buffers(struct msm_vidc_inst *inst) +{ + struct buffer_info *binfo = NULL; + + if (inst->buffer_mode_set[CAPTURE_PORT] != HAL_BUFFER_MODE_DYNAMIC) + return; + + /* + * dynamic buffer mode:- if flush is called during seek + * driver should not queue any new buffer it has been holding. + * + * Each dynamic o/p buffer can have one of following ref_count: + * ref_count : 0 - f/w has released reference and sent fbd back. + * The buffer has been returned back to client. + * + * ref_count : 1 - f/w is holding reference. f/w may have released + * fbd as read_only OR fbd is pending. f/w will + * release reference before sending flush_done. + * + * ref_count : 2 - f/w is holding reference, f/w has released fbd as + * read_only, which client has queued back to driver. + * driver holds this buffer and will queue back + * only when f/w releases the reference. During + * flush_done, f/w will release the reference but driver + * should not queue back the buffer to f/w. + * Flush all buffers with ref_count 2. + */ + mutex_lock(&inst->registeredbufs.lock); + if (!list_empty(&inst->registeredbufs.list)) { + struct v4l2_event buf_event = {0}; + u32 *ptr = NULL; + + list_for_each_entry(binfo, &inst->registeredbufs.list, list) { + if (binfo->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + atomic_read(&binfo->ref_count) == 2) { + + atomic_dec(&binfo->ref_count); + buf_event.type = + V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER; + ptr = (u32 *)buf_event.u.data; + ptr[0] = binfo->fd[0]; + ptr[1] = binfo->buff_off[0]; + ptr[2] = binfo->uvaddr[0]; + ptr[3] = (u32) binfo->timestamp.tv_sec; + ptr[4] = (u32) binfo->timestamp.tv_usec; + ptr[5] = binfo->v4l2_index; + dprintk(VIDC_DBG, + "released buffer held in driver before issuing flush: %pa fd[0]: %d\n", + &binfo->device_addr[0], binfo->fd[0]); + /*send event to client*/ + v4l2_event_queue_fh(&inst->event_handler, + &buf_event); + } + } + } + mutex_unlock(&inst->registeredbufs.lock); +} + +void msm_comm_flush_pending_dynamic_buffers(struct msm_vidc_inst *inst) +{ + struct buffer_info *binfo = NULL; + + if (!inst) + return; + + if (inst->buffer_mode_set[CAPTURE_PORT] != HAL_BUFFER_MODE_DYNAMIC) + return; + + if (list_empty(&inst->pendingq.list) || + list_empty(&inst->registeredbufs.list)) + return; + + /* + * Dynamic Buffer mode - Since pendingq is not empty + * no output buffers have been sent to firmware yet. + * Hence remove reference to all pendingq o/p buffers + * before flushing them. + */ + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(binfo, &inst->registeredbufs.list, list) { + if (binfo->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + dprintk(VIDC_DBG, + "%s: binfo = %pK device_addr = %pa\n", + __func__, binfo, &binfo->device_addr[0]); + buf_ref_put(inst, binfo); + } + } + mutex_unlock(&inst->registeredbufs.lock); +} + +int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags) +{ + int rc = 0; + bool ip_flush = false; + bool op_flush = false; + struct vb2_buf_entry *temp, *next; + struct mutex *lock; + 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; + } + + ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT; + op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE; + + if (ip_flush && !op_flush) { + dprintk(VIDC_INFO, "Input only flush not supported\n"); + return 0; + } + + msm_comm_flush_dynamic_buffers(inst); + + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID || + core->state == VIDC_CORE_UNINIT) { + dprintk(VIDC_ERR, + "Core %pK and inst %pK are in bad state\n", + core, inst); + msm_comm_flush_in_invalid_state(inst); + return 0; + } + + if (inst->in_reconfig && !ip_flush && op_flush) { + mutex_lock(&inst->pendingq.lock); + if (!list_empty(&inst->pendingq.list)) { + /* + * Execution can never reach here since port reconfig + * wont happen unless pendingq is emptied out + * (both pendingq and flush being secured with same + * lock). Printing a message here incase this breaks. + */ + dprintk(VIDC_WARN, + "FLUSH BUG: Pending q not empty! It should be empty\n"); + } + mutex_unlock(&inst->pendingq.lock); + atomic_inc(&inst->in_flush); + dprintk(VIDC_DBG, "Send flush Output to firmware\n"); + rc = call_hfi_op(hdev, session_flush, inst->session, + HAL_FLUSH_OUTPUT); + } else { + msm_comm_flush_pending_dynamic_buffers(inst); + /* + * If flush is called after queueing buffers but before + * streamon driver should flush the pending queue + */ + mutex_lock(&inst->pendingq.lock); + list_for_each_entry_safe(temp, next, + &inst->pendingq.list, list) { + enum v4l2_buf_type type = temp->vb->type; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + lock = &inst->bufq[CAPTURE_PORT].lock; + else + lock = &inst->bufq[OUTPUT_PORT].lock; + + temp->vb->planes[0].bytesused = 0; + + mutex_lock(lock); + vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE); + msm_vidc_debugfs_update(inst, + type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? + MSM_VIDC_DEBUGFS_EVENT_FBD : + MSM_VIDC_DEBUGFS_EVENT_EBD); + list_del(&temp->list); + mutex_unlock(lock); + + kfree(temp); + } + mutex_unlock(&inst->pendingq.lock); + + /*Do not send flush in case of session_error */ + if (!(inst->state == MSM_VIDC_CORE_INVALID && + core->state != VIDC_CORE_INVALID)) + atomic_inc(&inst->in_flush); + dprintk(VIDC_DBG, "Send flush all to firmware\n"); + rc = call_hfi_op(hdev, session_flush, inst->session, + HAL_FLUSH_ALL); + } + + return rc; +} + + +enum hal_extradata_id msm_comm_get_hal_extradata_index( + enum v4l2_mpeg_vidc3x_extradata index) +{ + int ret = 0; + + switch (index) { + case V4L2_MPEG_VIDC_EXTRADATA_NONE: + ret = HAL_EXTRADATA_NONE; + break; + case V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION: + ret = HAL_EXTRADATA_MB_QUANTIZATION; + break; + case V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO: + ret = HAL_EXTRADATA_INTERLACE_VIDEO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP: + ret = HAL_EXTRADATA_VC1_FRAMEDISP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP: + ret = HAL_EXTRADATA_VC1_SEQDISP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP: + ret = HAL_EXTRADATA_TIMESTAMP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING: + ret = HAL_EXTRADATA_S3D_FRAME_PACKING; + break; + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE: + ret = HAL_EXTRADATA_FRAME_RATE; + break; + case V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW: + ret = HAL_EXTRADATA_PANSCAN_WINDOW; + break; + case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI: + ret = HAL_EXTRADATA_RECOVERY_POINT_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO: + ret = HAL_EXTRADATA_MULTISLICE_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB: + ret = HAL_EXTRADATA_NUM_CONCEALED_MB; + break; + case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER: + ret = HAL_EXTRADATA_METADATA_FILLER; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO: + ret = HAL_EXTRADATA_ASPECT_RATIO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP: + ret = HAL_EXTRADATA_INPUT_CROP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM: + ret = HAL_EXTRADATA_DIGITAL_ZOOM; + break; + case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP: + ret = HAL_EXTRADATA_MPEG2_SEQDISP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA: + ret = HAL_EXTRADATA_STREAM_USERDATA; + break; + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP: + ret = HAL_EXTRADATA_DEC_FRAME_QP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP: + ret = HAL_EXTRADATA_ENC_FRAME_QP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO: + ret = HAL_EXTRADATA_FRAME_BITS_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_LTR: + ret = HAL_EXTRADATA_LTR_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI: + ret = HAL_EXTRADATA_METADATA_MBI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI: + ret = HAL_EXTRADATA_VQZIP_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS: + ret = HAL_EXTRADATA_YUV_STATS; + break; + case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP: + ret = HAL_EXTRADATA_ROI_QP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP: + ret = HAL_EXTRADATA_OUTPUT_CROP; + break; + case V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI: + ret = HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI: + ret = HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY: + ret = HAL_EXTRADATA_VUI_DISPLAY_INFO; + break; + case V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE: + ret = HAL_EXTRADATA_VPX_COLORSPACE; + break; + case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO: + ret = HAL_EXTRADATA_PQ_INFO; + break; + default: + dprintk(VIDC_WARN, "Extradata not found: %d\n", index); + break; + } + return ret; +}; + +enum hal_buffer_layout_type msm_comm_get_hal_buffer_layout( + enum v4l2_mpeg_vidc_video_mvc_layout index) +{ + int ret = 0; + + switch (index) { + case V4L2_MPEG_VIDC_VIDEO_MVC_SEQUENTIAL: + ret = HAL_BUFFER_LAYOUT_SEQ; + break; + case V4L2_MPEG_VIDC_VIDEO_MVC_TOP_BOTTOM: + ret = HAL_BUFFER_LAYOUT_TOP_BOTTOM; + break; + default: + break; + } + return ret; +} + +int msm_vidc_trigger_ssr(struct msm_vidc_core *core, + enum hal_ssr_trigger_type type) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!core || !core->device) { + dprintk(VIDC_WARN, "Invalid parameters: %pK\n", core); + return -EINVAL; + } + hdev = core->device; + if (core->state == VIDC_CORE_INIT_DONE) + rc = call_hfi_op(hdev, core_trigger_ssr, + hdev->hfi_device_data, type); + return rc; +} + +static int msm_vidc_load_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 + + inst->capability.mbs_per_frame.max; + 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; + + input_height = inst->prop.height[OUTPUT_PORT]; + input_width = inst->prop.width[OUTPUT_PORT]; + output_height = inst->prop.height[CAPTURE_PORT]; + output_width = inst->prop.width[CAPTURE_PORT]; + + if (!input_height || !input_width || !output_height || !output_width) { + dprintk(VIDC_ERR, + "Invalid : Input height = %d width = %d output height = %d width = %d\n", + input_height, input_width, output_height, + output_width); + return -ENOTSUPP; + } + + if (!inst->capability.scale_x.min || + !inst->capability.scale_x.max || + !inst->capability.scale_y.min || + !inst->capability.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_DBG, "%s: supported WxH = %dx%d\n", + __func__, input_width, input_height); + return 0; + } + + x_min = (1<<16)/inst->capability.scale_x.min; + y_min = (1<<16)/inst->capability.scale_y.min; + x_max = inst->capability.scale_x.max >> 16; + y_max = inst->capability.scale_y.max >> 16; + + if (input_height > output_height) { + if (input_height > x_min * output_height) { + dprintk(VIDC_ERR, + "Unsupported height downscale ratio %d vs %d\n", + input_height/output_height, x_min); + return -ENOTSUPP; + } + } else { + if (output_height > x_max * input_height) { + dprintk(VIDC_ERR, + "Unsupported height upscale ratio %d vs %d\n", + input_height/output_height, x_max); + return -ENOTSUPP; + } + } + if (input_width > output_width) { + if (input_width > y_min * output_width) { + dprintk(VIDC_ERR, + "Unsupported width downscale ratio %d vs %d\n", + input_width/output_width, y_min); + return -ENOTSUPP; + } + } else { + if (output_width > y_max * input_width) { + dprintk(VIDC_ERR, + "Unsupported width upscale ratio %d vs %d\n", + input_width/output_width, y_max); + 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; + int mbs_per_frame = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__); + return -EINVAL; + } + capability = &inst->capability; + hdev = inst->core->device; + core = inst->core; + rc = msm_vidc_load_supported(inst); + if (rc) { + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + msm_comm_kill_session(inst); + dprintk(VIDC_WARN, + "%s: Hardware is overloaded\n", __func__); + return rc; + } + + if (!is_thermal_permissible(core)) { + dprintk(VIDC_WARN, + "Thermal level critical, stop all active sessions!\n"); + return -ENOTSUPP; + } + + if (!rc) { + if (inst->prop.width[CAPTURE_PORT] < capability->width.min || + inst->prop.height[CAPTURE_PORT] < + capability->height.min) { + dprintk(VIDC_ERR, + "Unsupported WxH = (%u)x(%u), min supported is - (%u)x(%u)\n", + inst->prop.width[CAPTURE_PORT], + inst->prop.height[CAPTURE_PORT], + capability->width.min, + capability->height.min); + rc = -ENOTSUPP; + } + if (!rc && inst->prop.width[CAPTURE_PORT] > + capability->width.max) { + dprintk(VIDC_ERR, + "Unsupported width = %u supported max width = %u", + inst->prop.width[CAPTURE_PORT], + capability->width.max); + rc = -ENOTSUPP; + } + + if (!rc && inst->prop.height[CAPTURE_PORT] > + capability->height.max) { + dprintk(VIDC_ERR, + "Unsupported height = %u supported max height = %u", + inst->prop.height[CAPTURE_PORT], + capability->height.max); + rc = -ENOTSUPP; + } + + mbs_per_frame = msm_comm_get_mbs_per_frame(inst); + if (!rc && mbs_per_frame > capability->mbs_per_frame.max) { + dprintk(VIDC_ERR, + "Unsupported mbs per frame = %u, max supported is - %u\n", + mbs_per_frame, + capability->mbs_per_frame.max); + rc = -ENOTSUPP; + } + } + if (rc) { + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + msm_comm_kill_session(inst); + dprintk(VIDC_ERR, + "%s: Resolution unsupported\n", __func__); + } + return rc; +} + +static 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}; + + dprintk(VIDC_WARN, "%s\n", __func__); + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); + return; + } + + response.session_id = inst; + response.status = VIDC_ERR_FAIL; + handle_session_error(cmd, (void *)&response); +} + +static 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; + } + 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) { + /* There's no hfi session to kill */ + return 0; + } + + /* + * 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 == -EBUSY) { + msm_comm_generate_sys_error(inst); + return 0; + } else if (rc) { + return rc; + } + change_inst_state(inst, MSM_VIDC_CLOSE_DONE); + msm_comm_generate_session_error(inst); + } else { + dprintk(VIDC_WARN, + "Inactive session %pK, triggering an internal session error\n", + inst); + msm_comm_generate_session_error(inst); + + } + + 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); +} + +int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst, + struct msm_smem *mem, enum smem_cache_ops cache_ops) +{ + if (!inst || !mem) { + dprintk(VIDC_ERR, + "%s: invalid params: %pK %pK\n", __func__, inst, mem); + return -EINVAL; + } + return msm_smem_cache_operations(mem->dma_buf, mem->offset, + mem->size, cache_ops); +} + +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_DBG, "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 hal_uncompressed_format_select hal_fmt = {0}; + enum hal_uncompressed_format format = HAL_UNUSED_COLOR; + 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 = get_hal_uncompressed(fourcc); + if (format == HAL_UNUSED_COLOR) { + dprintk(VIDC_ERR, "Using unsupported colorformat %#x\n", + fourcc); + rc = -ENOTSUPP; + goto exit; + } + + hal_fmt.buffer_type = buffer_type; + hal_fmt.format = format; + + rc = call_hfi_op(hdev, session_set_property, inst->session, + HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, &hal_fmt); + if (rc) + dprintk(VIDC_ERR, + "Failed to set input color format\n"); + else + dprintk(VIDC_DBG, "Setting uncompressed colorformat to %#x\n", + format); + +exit: + return rc; +} + +int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a) +{ + u32 property_id = 0; + u64 us_per_frame = 0, fps_u64 = 0; + void *pdata; + int rc = 0, fps = 0; + struct hal_frame_rate frame_rate; + struct hfi_device *hdev; + + if (!inst || !inst->core || !inst->core->device || !a) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + property_id = HAL_CONFIG_FRAME_RATE; + + if (a->parm.output.timeperframe.denominator) { + switch (a->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + us_per_frame = a->parm.output.timeperframe.numerator * + (u64)USEC_PER_SEC; + do_div(us_per_frame, + a->parm.output.timeperframe.denominator); + break; + default: + dprintk(VIDC_ERR, + "Scale clocks : Unknown buffer type %d\n", + a->type); + break; + } + } + + if (!us_per_frame) { + dprintk(VIDC_ERR, + "Failed to scale clocks : time between frames is 0\n"); + rc = -EINVAL; + goto exit; + } + + fps_u64 = USEC_PER_SEC; + do_div(fps_u64, us_per_frame); + fps = fps_u64; + + if (fps % 15 == 14 || fps % 24 == 23) + fps = fps + 1; + else if ((fps > 1) && (fps % 24 == 1 || fps % 15 == 1)) + fps = fps - 1; + + if (inst->prop.fps != fps) { + dprintk(VIDC_PROF, "reported fps changed for %pK: %d->%d\n", + inst, inst->prop.fps, fps); + inst->prop.fps = fps; + frame_rate.frame_rate = inst->prop.fps * BIT(16); + frame_rate.buffer_type = HAL_BUFFER_OUTPUT; + pdata = &frame_rate; + if (inst->session_type == MSM_VIDC_ENCODER) { + rc = call_hfi_op(hdev, session_set_property, + inst->session, property_id, pdata); + + if (rc) + dprintk(VIDC_WARN, + "Failed to set frame rate %d\n", rc); + } else { + msm_dcvs_init_load(inst); + } + msm_comm_scale_clocks_and_bus(inst); + } +exit: + return rc; +} diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_common.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.h new file mode 100644 index 000000000000..516bcc2785d0 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_common.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2016, 2018, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_VIDC_COMMON_H_ +#define _MSM_VIDC_COMMON_H_ +#include "msm_vidc_internal.h" +struct vb2_buf_entry { + struct list_head list; + struct vb2_buffer *vb; +}; + +extern const char *const mpeg_video_vidc_extradata[]; + +enum load_calc_quirks { + LOAD_CALC_NO_QUIRKS = 0, + LOAD_CALC_IGNORE_TURBO_LOAD = 1 << 0, + LOAD_CALC_IGNORE_THUMBNAIL_LOAD = 1 << 1, + LOAD_CALC_IGNORE_NON_REALTIME_LOAD = 1 << 2, +}; + +struct msm_vidc_core *get_vidc_core(int core_id); +const struct msm_vidc_format *msm_comm_get_pixel_fmt_index( + const struct msm_vidc_format fmt[], int size, int index, int fmt_type); +struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc( + struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type); +struct buf_queue *msm_comm_get_vb2q( + struct msm_vidc_inst *inst, enum v4l2_buf_type type); +int msm_comm_try_state(struct msm_vidc_inst *inst, int state); +int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst); +int msm_comm_try_set_prop(struct msm_vidc_inst *inst, + enum hal_property ptype, void *pdata); +int msm_comm_try_get_prop(struct msm_vidc_inst *inst, + enum hal_property ptype, union hal_get_property *hprop); +int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst); +int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst); +int msm_comm_set_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb); +void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst); +int msm_comm_scale_clocks(struct msm_vidc_core *core); +int msm_comm_scale_clocks_load(struct msm_vidc_core *core, + int num_mbs_per_sec, enum load_calc_quirks quirks); +void msm_comm_flush_dynamic_buffers(struct msm_vidc_inst *inst); +int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags); +int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst, + bool check_for_reuse); +int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst); +void msm_comm_release_eos_buffers(struct msm_vidc_inst *inst); +int msm_comm_release_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_force_cleanup(struct msm_vidc_inst *inst); +int msm_comm_suspend(int core_id); +enum hal_extradata_id msm_comm_get_hal_extradata_index( + enum v4l2_mpeg_vidc3x_extradata index); +enum hal_buffer_layout_type msm_comm_get_hal_buffer_layout( + enum v4l2_mpeg_vidc_video_mvc_layout index); +struct hal_buffer_requirements *get_buff_req_buffer( + struct msm_vidc_inst *inst, u32 buffer_type); +#define IS_PRIV_CTRL(idx) (\ + (V4L2_CTRL_ID2WHICH(idx) == V4L2_CTRL_CLASS_MPEG) && \ + V4L2_CTRL_DRIVER_PRIV(idx)) +void msm_comm_session_clean(struct msm_vidc_inst *inst); +int msm_comm_kill_session(struct msm_vidc_inst *inst); +enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst); +enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst); +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); +void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem); +int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst, + struct msm_smem *mem, enum smem_cache_ops cache_ops); +enum hal_video_codec get_hal_codec(int fourcc); +enum hal_domain get_hal_domain(int session_type); +int msm_comm_check_core_init(struct msm_vidc_core *core); +int msm_comm_get_inst_load(struct msm_vidc_inst *inst, + enum load_calc_quirks quirks); +int msm_comm_get_load(struct msm_vidc_core *core, + enum session_type type, enum load_calc_quirks quirks); +int msm_comm_set_color_format(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type, int fourcc); +int msm_comm_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl); +int msm_comm_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl); +int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id); +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 msm_comm_ctrl_deinit(struct msm_vidc_inst *inst); +void msm_comm_cleanup_internal_buffers(struct msm_vidc_inst *inst); +int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a); +bool msm_comm_turbo_session(struct msm_vidc_inst *inst); +struct msm_vidc_inst *get_inst(struct msm_vidc_core *core, void *session_id); +void put_inst(struct msm_vidc_inst *inst); +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.c new file mode 100644 index 000000000000..4f16d39f4b03 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.c @@ -0,0 +1,704 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_debug.h" +#include "msm_vidc_dcvs.h" + +#define IS_VALID_DCVS_SESSION(__cur_mbpf, __min_mbpf) \ + ((__cur_mbpf) >= (__min_mbpf)) + +static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst); +static bool msm_dcvs_enc_check(struct msm_vidc_inst *inst); +static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst); +static int msm_dcvs_dec_scale_clocks(struct msm_vidc_inst *inst, bool fbd); + +static inline int msm_dcvs_get_mbs_per_frame(struct msm_vidc_inst *inst) +{ + int height, width; + + if (!inst->in_reconfig) { + height = max(inst->prop.height[CAPTURE_PORT], + inst->prop.height[OUTPUT_PORT]); + width = max(inst->prop.width[CAPTURE_PORT], + inst->prop.width[OUTPUT_PORT]); + } else { + height = inst->reconfig_height; + width = inst->reconfig_width; + } + + return NUM_MBS_PER_FRAME(height, width); +} + +static inline int msm_dcvs_count_active_instances(struct msm_vidc_core *core) +{ + int active_instances = 0; + struct msm_vidc_inst *inst = NULL; + + if (!core) { + dprintk(VIDC_ERR, "%s: Invalid args: %pK\n", __func__, core); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->state >= MSM_VIDC_OPEN_DONE && + inst->state < MSM_VIDC_STOP_DONE) + active_instances++; + } + mutex_unlock(&core->lock); + return active_instances; +} + +static bool msm_dcvs_check_codec_supported(int fourcc, + unsigned long codecs_supported, enum session_type type) +{ + int codec_bit, session_type_bit; + bool codec_type, session_type; + unsigned long session; + + session = VIDC_VOTE_DATA_SESSION_VAL(get_hal_codec(fourcc), + get_hal_domain(type)); + + if (!codecs_supported || !session) + return false; + + /* ffs returns a 1 indexed, test_bit takes a 0 indexed...index */ + codec_bit = ffs(session) - 1; + session_type_bit = codec_bit + 1; + + codec_type = + test_bit(codec_bit, &codecs_supported) == + test_bit(codec_bit, &session); + session_type = + test_bit(session_type_bit, &codecs_supported) == + test_bit(session_type_bit, &session); + + return codec_type && session_type; +} + +static void msm_dcvs_update_dcvs_params(int idx, struct msm_vidc_inst *inst) +{ + struct dcvs_stats *dcvs = NULL; + struct msm_vidc_platform_resources *res = NULL; + struct dcvs_table *table = NULL; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst); + return; + } + + dcvs = &inst->dcvs; + res = &inst->core->resources; + table = res->dcvs_tbl; + + dcvs->load_low = table[idx].load_low; + dcvs->load_high = table[idx].load_high; + dcvs->supported_codecs = table[idx].supported_codecs; +} + +static void msm_dcvs_enc_check_and_scale_clocks(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (inst->session_type == MSM_VIDC_ENCODER && msm_vidc_enc_dcvs_mode) { + inst->dcvs_mode = msm_dcvs_check_supported(inst); + dprintk(VIDC_DBG, "%s: session DCVS %s supported\n", + __func__, inst->dcvs_mode ? "" : "not"); + + if (inst->dcvs_mode) { + rc = msm_dcvs_enc_scale_clocks(inst); + if (rc) { + dprintk(VIDC_DBG, + "ENC_DCVS: error while scaling clocks\n"); + } + } + } +} + +static void msm_dcvs_dec_check_and_scale_clocks(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (inst->session_type != MSM_VIDC_DECODER || !msm_vidc_dec_dcvs_mode) + return; + + if (msm_dcvs_check_supported(inst)) { + inst->dcvs_mode = true; + dprintk(VIDC_DBG, + "%s: session DCVS supported, decode_dcvs_mode = %d\n", + __func__, inst->dcvs_mode); + } else { + inst->dcvs_mode = false; + dprintk(VIDC_DBG, + "%s: session DCVS not supported, decode_dcvs_mode = %d\n", + __func__, inst->dcvs_mode); + } + + if (msm_vidc_dec_dcvs_mode && inst->dcvs_mode) { + msm_dcvs_monitor_buffer(inst); + rc = msm_dcvs_dec_scale_clocks(inst, false); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to scale clocks in DCVS: %d\n", + __func__, rc); + } + } +} + +void msm_dcvs_check_and_scale_clocks(struct msm_vidc_inst *inst, bool is_etb) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst); + return; + } + + if (is_etb) + msm_dcvs_enc_check_and_scale_clocks(inst); + else + msm_dcvs_dec_check_and_scale_clocks(inst); +} + +static inline int get_pending_bufs_fw(struct msm_vidc_inst *inst) +{ + int fw_out_qsize = 0, buffers_in_driver = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return -EINVAL; + } + + if (inst->state >= MSM_VIDC_OPEN_DONE && + inst->state < MSM_VIDC_STOP_DONE) { + fw_out_qsize = inst->count.ftb - inst->count.fbd; + buffers_in_driver = inst->buffers_held_in_driver; + } + + return fw_out_qsize + buffers_in_driver; +} + +static inline void msm_dcvs_print_dcvs_stats(struct dcvs_stats *dcvs) +{ + dprintk(VIDC_DBG, + "DCVS: Load_Low %d, Load High %d\n", + dcvs->load_low, + dcvs->load_high); + + dprintk(VIDC_DBG, + "DCVS: ThrDispBufLow %d, ThrDispBufHigh %d\n", + dcvs->threshold_disp_buf_low, + dcvs->threshold_disp_buf_high); + + dprintk(VIDC_DBG, + "DCVS: min_threshold %d, max_threshold %d\n", + dcvs->min_threshold, dcvs->max_threshold); +} + +void msm_dcvs_init_load(struct msm_vidc_inst *inst) +{ + struct msm_vidc_core *core; + struct hal_buffer_requirements *output_buf_req; + struct dcvs_stats *dcvs; + struct dcvs_table *table; + struct msm_vidc_platform_resources *res = NULL; + int i, num_rows, fourcc; + + dprintk(VIDC_DBG, "Init DCVS Load\n"); + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst); + return; + } + + core = inst->core; + dcvs = &inst->dcvs; + res = &core->resources; + dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS); + + num_rows = res->dcvs_tbl_size; + table = res->dcvs_tbl; + + if (!num_rows || !table) { + dprintk(VIDC_ERR, + "%s: Dcvs table entry not found.\n", __func__); + return; + } + + fourcc = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT].fourcc : + inst->fmts[CAPTURE_PORT].fourcc; + + for (i = 0; i < num_rows; i++) { + bool matches = msm_dcvs_check_codec_supported( + fourcc, + table[i].supported_codecs, + inst->session_type); + if (!matches) + continue; + + if (dcvs->load > table[i].load) { + msm_dcvs_update_dcvs_params(i, inst); + break; + } + } + + if (inst->session_type == MSM_VIDC_ENCODER) + goto print_stats; + + output_buf_req = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + + if (!output_buf_req) { + dprintk(VIDC_ERR, + "%s: No buffer requirement for buffer type %x\n", + __func__, HAL_BUFFER_OUTPUT); + return; + } + + dcvs->transition_turbo = false; + + /* calculating the min and max threshold */ + if (output_buf_req->buffer_count_actual) { + dcvs->min_threshold = output_buf_req->buffer_count_actual - + output_buf_req->buffer_count_min - + msm_dcvs_get_extra_buff_count(inst) + 1; + dcvs->max_threshold = output_buf_req->buffer_count_actual; + if (dcvs->max_threshold <= dcvs->min_threshold) + dcvs->max_threshold = + dcvs->min_threshold + DCVS_BUFFER_SAFEGUARD; + dcvs->threshold_disp_buf_low = dcvs->min_threshold; + dcvs->threshold_disp_buf_high = dcvs->max_threshold; + } + +print_stats: + msm_dcvs_print_dcvs_stats(dcvs); +} + +void msm_dcvs_init(struct msm_vidc_inst *inst) +{ + dprintk(VIDC_DBG, "Init DCVS Struct\n"); + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst); + return; + } + + inst->dcvs = (struct dcvs_stats){ {0} }; + inst->dcvs.threshold_disp_buf_high = DCVS_NOMINAL_THRESHOLD; + inst->dcvs.threshold_disp_buf_low = DCVS_TURBO_THRESHOLD; +} + +void msm_dcvs_monitor_buffer(struct msm_vidc_inst *inst) +{ + int new_ftb, i, prev_buf_count; + int fw_pending_bufs, total_output_buf, buffers_outside_fw; + struct dcvs_stats *dcvs; + struct hal_buffer_requirements *output_buf_req; + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst); + return; + } + dcvs = &inst->dcvs; + + mutex_lock(&inst->lock); + output_buf_req = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + if (!output_buf_req) { + dprintk(VIDC_ERR, "%s : Get output buffer req failed %pK\n", + __func__, inst); + mutex_unlock(&inst->lock); + return; + } + + total_output_buf = output_buf_req->buffer_count_actual; + fw_pending_bufs = get_pending_bufs_fw(inst); + mutex_unlock(&inst->lock); + + buffers_outside_fw = total_output_buf - fw_pending_bufs; + dcvs->num_ftb[dcvs->ftb_index] = buffers_outside_fw; + dcvs->ftb_index = (dcvs->ftb_index + 1) % DCVS_FTB_WINDOW; + + if (dcvs->ftb_counter < DCVS_FTB_WINDOW) + dcvs->ftb_counter++; + + dprintk(VIDC_PROF, + "DCVS: ftb_counter %d\n", dcvs->ftb_counter); + + if (dcvs->ftb_counter == DCVS_FTB_WINDOW) { + new_ftb = 0; + for (i = 0; i < dcvs->ftb_counter; i++) { + if (dcvs->num_ftb[i] > new_ftb) + new_ftb = dcvs->num_ftb[i]; + } + + dcvs->threshold_disp_buf_high = new_ftb; + if (dcvs->threshold_disp_buf_high <= + dcvs->threshold_disp_buf_low + + DCVS_BUFFER_SAFEGUARD) { + dcvs->threshold_disp_buf_high = + dcvs->threshold_disp_buf_low + + DCVS_BUFFER_SAFEGUARD + + (DCVS_BUFFER_SAFEGUARD == 0); + } + + dcvs->threshold_disp_buf_high = + clamp(dcvs->threshold_disp_buf_high, + dcvs->min_threshold, + dcvs->max_threshold); + } + + if (dcvs->ftb_counter == DCVS_FTB_WINDOW && + dcvs->load == dcvs->load_low) { + prev_buf_count = + dcvs->num_ftb[((dcvs->ftb_index - 2 + + DCVS_FTB_WINDOW) % DCVS_FTB_WINDOW)]; + if (prev_buf_count == dcvs->threshold_disp_buf_low && + buffers_outside_fw <= dcvs->threshold_disp_buf_low) { + dcvs->transition_turbo = true; + } else if (buffers_outside_fw > dcvs->threshold_disp_buf_low && + (buffers_outside_fw - + (prev_buf_count - buffers_outside_fw)) + < dcvs->threshold_disp_buf_low){ + dcvs->transition_turbo = true; + } + } + + dprintk(VIDC_PROF, + "DCVS: total_output_buf %d buffers_outside_fw %d load %d transition_turbo %d\n", + total_output_buf, buffers_outside_fw, dcvs->load_low, + dcvs->transition_turbo); +} + +static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst) +{ + int rc = 0, fw_pending_bufs = 0, total_input_buf = 0; + struct msm_vidc_core *core; + struct dcvs_stats *dcvs; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return -EINVAL; + } + + core = inst->core; + dcvs = &inst->dcvs; + + mutex_lock(&inst->lock); + total_input_buf = inst->buff_req.buffer[0].buffer_count_actual; + fw_pending_bufs = (inst->count.etb - inst->count.ebd); + mutex_unlock(&inst->lock); + + dprintk(VIDC_PROF, + "DCVS: total_input_buf %d, fw_pending_bufs %d\n", + total_input_buf, fw_pending_bufs); + + if (dcvs->etb_counter < total_input_buf) { + dcvs->etb_counter++; + if (dcvs->etb_counter != total_input_buf) + return rc; + } + + dprintk(VIDC_PROF, + "DCVS: total_input_buf %d, fw_pending_bufs %d etb_counter %d dcvs->load %d\n", + total_input_buf, fw_pending_bufs, + dcvs->etb_counter, dcvs->load); + + if (fw_pending_bufs <= DCVS_ENC_LOW_THR && + dcvs->load > dcvs->load_low) { + dcvs->load = dcvs->load_low; + dcvs->prev_freq_lowered = true; + } else { + dcvs->prev_freq_lowered = false; + } + + if (fw_pending_bufs >= DCVS_ENC_HIGH_THR && + dcvs->load <= dcvs->load_low) { + dcvs->load = dcvs->load_high; + dcvs->prev_freq_increased = true; + } else { + dcvs->prev_freq_increased = false; + } + + if (dcvs->prev_freq_lowered || dcvs->prev_freq_increased) { + dprintk(VIDC_PROF, + "DCVS: (Scaling Clock %s) etb clock set = %d total_input_buf = %d fw_pending_bufs %d\n", + dcvs->prev_freq_lowered ? "Lower" : "Higher", + dcvs->load, total_input_buf, fw_pending_bufs); + + rc = msm_comm_scale_clocks_load(core, dcvs->load, + LOAD_CALC_NO_QUIRKS); + if (rc) { + dprintk(VIDC_PROF, + "Failed to set clock rate in FBD: %d\n", rc); + } + } else { + dprintk(VIDC_PROF, + "DCVS: etb clock load_old = %d total_input_buf = %d fw_pending_bufs %d\n", + dcvs->load, total_input_buf, fw_pending_bufs); + } + + return rc; +} + + +/* + * In DCVS scale_clocks will be done both in qbuf and FBD + * 1 indicates call made from fbd that lowers clock + * 0 indicates call made from qbuf that increases clock + * based on DCVS algorithm + */ + +static int msm_dcvs_dec_scale_clocks(struct msm_vidc_inst *inst, bool fbd) +{ + int rc = 0; + int fw_pending_bufs = 0; + int total_output_buf = 0; + int buffers_outside_fw = 0; + struct msm_vidc_core *core; + struct hal_buffer_requirements *output_buf_req; + struct dcvs_stats *dcvs; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return -EINVAL; + } + core = inst->core; + dcvs = &inst->dcvs; + mutex_lock(&inst->lock); + fw_pending_bufs = get_pending_bufs_fw(inst); + + output_buf_req = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + mutex_unlock(&inst->lock); + if (!output_buf_req) { + dprintk(VIDC_ERR, + "%s: No buffer requirement for buffer type %x\n", + __func__, HAL_BUFFER_OUTPUT); + return -EINVAL; + } + + /* Total number of output buffers */ + total_output_buf = output_buf_req->buffer_count_actual; + + /* Buffers outside FW are with display */ + buffers_outside_fw = total_output_buf - fw_pending_bufs; + + if (buffers_outside_fw >= dcvs->threshold_disp_buf_high && + !dcvs->prev_freq_increased && + dcvs->load > dcvs->load_low) { + dcvs->load = dcvs->load_low; + dcvs->prev_freq_lowered = true; + dcvs->prev_freq_increased = false; + } else if (dcvs->transition_turbo && dcvs->load == dcvs->load_low) { + dcvs->load = dcvs->load_high; + dcvs->prev_freq_increased = true; + dcvs->prev_freq_lowered = false; + dcvs->transition_turbo = false; + } else { + dcvs->prev_freq_increased = false; + dcvs->prev_freq_lowered = false; + } + + if (dcvs->prev_freq_lowered || dcvs->prev_freq_increased) { + dprintk(VIDC_PROF, + "DCVS: clock set = %d tot_output_buf = %d buffers_outside_fw %d threshold_high %d transition_turbo %d\n", + dcvs->load, total_output_buf, buffers_outside_fw, + dcvs->threshold_disp_buf_high, dcvs->transition_turbo); + + rc = msm_comm_scale_clocks_load(core, dcvs->load, + LOAD_CALC_NO_QUIRKS); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set clock rate in FBD: %d\n", rc); + } + } else { + dprintk(VIDC_PROF, + "DCVS: clock old = %d tot_output_buf = %d buffers_outside_fw %d threshold_high %d transition_turbo %d\n", + dcvs->load, total_output_buf, buffers_outside_fw, + dcvs->threshold_disp_buf_high, dcvs->transition_turbo); + } + return rc; +} + +static bool msm_dcvs_enc_check(struct msm_vidc_inst *inst) +{ + int num_mbs_per_frame = 0; + long instance_load = 0; + long dcvs_limit = 0; + bool dcvs_check_passed = false, is_codec_supported = false; + struct msm_vidc_platform_resources *res = NULL; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return dcvs_check_passed; + } + + res = &inst->core->resources; + if (!res->dcvs_limit) { + dprintk(VIDC_ERR, + "%s Dcvs limit table uninitialized\n", __func__); + return false; + } + + is_codec_supported = + msm_dcvs_check_codec_supported( + inst->fmts[CAPTURE_PORT].fourcc, + inst->dcvs.supported_codecs, + inst->session_type); + + num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst); + instance_load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS); + dcvs_limit = + (long)res->dcvs_limit[inst->session_type].min_mbpf * + res->dcvs_limit[inst->session_type].fps; + + if (msm_vidc_enc_dcvs_mode && is_codec_supported && + inst->dcvs.is_power_save_mode && + IS_VALID_DCVS_SESSION(num_mbs_per_frame, + res->dcvs_limit[inst->session_type].min_mbpf) && + IS_VALID_DCVS_SESSION(instance_load, dcvs_limit)) { + dcvs_check_passed = true; + } + return dcvs_check_passed; +} + +static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst) +{ + int num_mbs_per_frame = 0, instance_count = 0; + long instance_load = 0; + long dcvs_limit = 0; + struct msm_vidc_inst *temp = NULL; + struct msm_vidc_core *core; + struct hal_buffer_requirements *output_buf_req; + struct dcvs_stats *dcvs; + bool is_codec_supported = false; + struct msm_vidc_platform_resources *res = NULL; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__); + return -EINVAL; + } + + core = inst->core; + dcvs = &inst->dcvs; + res = &core->resources; + + if (!res->dcvs_limit) { + dprintk(VIDC_INFO, + "%s: dcvs limit table not found\n", __func__); + return false; + } + instance_count = msm_dcvs_count_active_instances(core); + + if (instance_count == 1 && inst->session_type == MSM_VIDC_DECODER && + !msm_comm_turbo_session(inst)) { + num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst); + instance_load = msm_comm_get_inst_load(inst, + LOAD_CALC_NO_QUIRKS); + output_buf_req = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + dcvs_limit = + (long)res->dcvs_limit[inst->session_type].min_mbpf * + res->dcvs_limit[inst->session_type].fps; + is_codec_supported = + msm_dcvs_check_codec_supported( + inst->fmts[OUTPUT_PORT].fourcc, + inst->dcvs.supported_codecs, + inst->session_type); + if (!is_codec_supported || + !IS_VALID_DCVS_SESSION(num_mbs_per_frame, + res->dcvs_limit[inst->session_type].min_mbpf) || + !IS_VALID_DCVS_SESSION(instance_load, dcvs_limit) || + inst->seqchanged_count > 1) + return false; + + if (!output_buf_req) { + dprintk(VIDC_ERR, + "%s: No buffer requirement for buffer type %x\n", + __func__, HAL_BUFFER_OUTPUT); + return false; + } + } else if (instance_count == 1 && + inst->session_type == MSM_VIDC_ENCODER && + !msm_comm_turbo_session(inst)) { + if (!msm_dcvs_enc_check(inst)) + return false; + } else { + /* + * For multiple instance use case with 4K, clocks will be scaled + * as per load in streamon, but the clocks may be scaled + * down as DCVS is running for first playback instance + * Rescaling the core clock for multiple instance use case + */ + if (!dcvs->is_clock_scaled) { + if (!msm_comm_scale_clocks(core)) { + dcvs->is_clock_scaled = true; + dprintk(VIDC_DBG, + "%s: Scaled clocks = %d\n", + __func__, dcvs->is_clock_scaled); + } else { + dprintk(VIDC_DBG, + "%s: Failed to Scale clocks. Perf might be impacted\n", + __func__); + } + } + /* + * For multiple instance use case turn OFF DCVS algorithm + * immediately + */ + if (instance_count > 1) { + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) + temp->dcvs_mode = false; + mutex_unlock(&core->lock); + } + + return false; + } + + return true; +} + +int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst) +{ + int extra_buffer = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return 0; + } + + if (inst->session_type == MSM_VIDC_ENCODER) { + if (msm_dcvs_enc_check(inst)) + extra_buffer = DCVS_ENC_EXTRA_OUTPUT_BUFFERS; + } else if (inst->session_type == MSM_VIDC_DECODER) { + if (msm_dcvs_check_supported(inst)) + extra_buffer = DCVS_DEC_EXTRA_OUTPUT_BUFFERS; + } + return extra_buffer; +} + + +void msm_dcvs_enc_set_power_save_mode(struct msm_vidc_inst *inst, + bool is_power_save_mode) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return; + } + + inst->dcvs.is_power_save_mode = is_power_save_mode; +} diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.h new file mode 100644 index 000000000000..077af2c9e99a --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_dcvs.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014-2015, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_VIDC_DCVS_H_ +#define _MSM_VIDC_DCVS_H_ +#include "msm_vidc_internal.h" + +/* Low threshold for encoder dcvs */ +#define DCVS_ENC_LOW_THR 4 +/* High threshold for encoder dcvs */ +#define DCVS_ENC_HIGH_THR 9 +/* extra o/p buffers in case of encoder dcvs */ +#define DCVS_ENC_EXTRA_OUTPUT_BUFFERS 2 +/* extra o/p buffers in case of decoder dcvs */ +#define DCVS_DEC_EXTRA_OUTPUT_BUFFERS 4 +/* Default threshold to reduce the core frequency */ +#define DCVS_NOMINAL_THRESHOLD 8 +/* Default threshold to increase the core frequency */ +#define DCVS_TURBO_THRESHOLD 4 + +/* Considering one safeguard buffer */ +#define DCVS_BUFFER_SAFEGUARD (DCVS_DEC_EXTRA_OUTPUT_BUFFERS - 1) + +void msm_dcvs_init(struct msm_vidc_inst *inst); +void msm_dcvs_init_load(struct msm_vidc_inst *inst); +void msm_dcvs_monitor_buffer(struct msm_vidc_inst *inst); +void msm_dcvs_check_and_scale_clocks(struct msm_vidc_inst *inst, bool is_etb); +int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst); +void msm_dcvs_enc_set_power_save_mode(struct msm_vidc_inst *inst, + bool is_power_save_mode); +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_debug.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_debug.c new file mode 100644 index 000000000000..25e7c260880d --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_debug.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define CREATE_TRACE_POINTS +#include "msm_vidc_common.h" +#define MAX_SSR_STRING_LEN 10 +#include "msm_vidc_debug.h" +#include "vidc_hfi_api.h" + +int msm_vidc_debug = VIDC_ERR | VIDC_WARN; +int msm_vidc_debug_out = VIDC_OUT_PRINTK; +int msm_vidc_fw_debug = 0x18; +int msm_vidc_fw_debug_mode = 1; +int msm_vidc_fw_low_power_mode = 1; +int msm_vidc_hw_rsp_timeout = 1000; +bool msm_vidc_fw_coverage = true; +bool msm_vidc_dec_dcvs_mode = true; +bool msm_vidc_enc_dcvs_mode = true; +bool msm_vidc_sys_idle_indicator = true; +int msm_vidc_firmware_unload_delay = 15000; +bool msm_vidc_thermal_mitigation_disabled = true; +bool msm_vidc_bitrate_clock_scaling = true; +bool msm_vidc_debug_timeout = true; + +#define MAX_DBG_BUF_SIZE 4096 + +#define DYNAMIC_BUF_OWNER(__binfo) ({ \ + atomic_read(&__binfo->ref_count) == 2 ? "video driver" : "firmware";\ +}) + +struct core_inst_pair { + struct msm_vidc_core *core; + struct msm_vidc_inst *inst; +}; + +static u32 write_str(char *buffer, + size_t size, const char *fmt, ...) +{ + va_list args; + u32 len; + + va_start(args, fmt); + len = vscnprintf(buffer, size, fmt, args); + va_end(args); + return len; +} + +static ssize_t core_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_vidc_core *core = file->private_data; + struct hfi_device *hdev; + struct hal_fw_info fw_info = { {0} }; + char *dbuf, *cur, *end; + int i = 0, rc = 0; + ssize_t len = 0; + + if (!core || !core->device) { + dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); + return 0; + } + + dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL); + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + return -ENOMEM; + } + cur = dbuf; + end = cur + MAX_DBG_BUF_SIZE; + hdev = core->device; + + cur += write_str(cur, end - cur, "===============================\n"); + cur += write_str(cur, end - cur, "CORE %d: %pK\n", core->id, core); + cur += write_str(cur, end - cur, "===============================\n"); + cur += write_str(cur, end - cur, "Core state: %d\n", core->state); + rc = call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, &fw_info); + if (rc) { + dprintk(VIDC_WARN, "Failed to read FW info\n"); + goto err_fw_info; + } + + cur += write_str(cur, end - cur, + "FW version : %s\n", &fw_info.version); + cur += write_str(cur, end - cur, + "base addr: 0x%x\n", fw_info.base_addr); + cur += write_str(cur, end - cur, + "register_base: 0x%x\n", fw_info.register_base); + cur += write_str(cur, end - cur, + "register_size: %u\n", fw_info.register_size); + cur += write_str(cur, end - cur, "irq: %u\n", fw_info.irq); + +err_fw_info: + for (i = SYS_MSG_START; i < SYS_MSG_END; i++) { + cur += write_str(cur, end - cur, "completions[%d]: %s\n", i, + completion_done(&core->completions[SYS_MSG_INDEX(i)]) ? + "pending" : "done"); + } + len = simple_read_from_buffer(buf, count, ppos, + dbuf, cur - dbuf); + + kfree(dbuf); + return len; +} + +static const struct file_operations core_info_fops = { + .open = simple_open, + .read = core_info_read, +}; + +static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long ssr_trigger_val = 0; + int rc = 0; + struct msm_vidc_core *core = filp->private_data; + size_t size = MAX_SSR_STRING_LEN; + char kbuf[MAX_SSR_STRING_LEN + 1] = {0}; + + if (!count) + goto exit; + + if (count < size) + size = count; + + if (copy_from_user(kbuf, buf, size)) { + dprintk(VIDC_WARN, "%s User memory fault\n", __func__); + rc = -EFAULT; + goto exit; + } + + rc = kstrtoul(kbuf, 0, &ssr_trigger_val); + if (rc) { + dprintk(VIDC_WARN, "returning error err %d\n", rc); + rc = -EINVAL; + } else { + msm_vidc_trigger_ssr(core, ssr_trigger_val); + rc = count; + } +exit: + return rc; +} + +static const struct file_operations ssr_fops = { + .open = simple_open, + .write = trigger_ssr_write, +}; + +struct dentry *msm_vidc_debugfs_init_drv(void) +{ + bool ok = false; + struct dentry *dir = NULL; + + dir = debugfs_create_dir("msm_vidc", NULL); + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + goto failed_create_dir; + } + +#define __debugfs_create(__type, __name, __value) ({ \ + struct dentry *f = debugfs_create_##__type(__name, 0644, \ + dir, __value); \ + if (IS_ERR_OR_NULL(f)) { \ + dprintk(VIDC_ERR, "Failed creating debugfs file '%pd/%s'\n", \ + dir, __name); \ + f = NULL; \ + } \ + f; \ +}) + + ok = + __debugfs_create(x32, "debug_level", &msm_vidc_debug) && + __debugfs_create(x32, "fw_level", &msm_vidc_fw_debug) && + __debugfs_create(u32, "fw_debug_mode", &msm_vidc_fw_debug_mode) && + __debugfs_create(bool, "fw_coverage", &msm_vidc_fw_coverage) && + __debugfs_create(bool, "dcvs_dec_mode", &msm_vidc_dec_dcvs_mode) && + __debugfs_create(bool, "dcvs_enc_mode", &msm_vidc_enc_dcvs_mode) && + __debugfs_create(u32, "fw_low_power_mode", + &msm_vidc_fw_low_power_mode) && + __debugfs_create(u32, "debug_output", &msm_vidc_debug_out) && + __debugfs_create(u32, "hw_rsp_timeout", &msm_vidc_hw_rsp_timeout) && + __debugfs_create(bool, "sys_idle_indicator", + &msm_vidc_sys_idle_indicator) && + __debugfs_create(u32, "firmware_unload_delay", + &msm_vidc_firmware_unload_delay) && + __debugfs_create(bool, "disable_thermal_mitigation", + &msm_vidc_thermal_mitigation_disabled) && + __debugfs_create(bool, "bitrate_clock_scaling", + &msm_vidc_bitrate_clock_scaling) && + __debugfs_create(bool, "debug_timeout", + &msm_vidc_debug_timeout); + +#undef __debugfs_create + + if (!ok) + goto failed_create_dir; + + return dir; + +failed_create_dir: + if (dir) + debugfs_remove_recursive(vidc_driver->debugfs_root); + + return NULL; +} + +struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, + struct dentry *parent) +{ + struct dentry *dir = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + + if (!core) { + dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); + goto failed_create_dir; + } + + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id); + dir = debugfs_create_dir(debugfs_name, parent); + if (!dir) { + dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); + goto failed_create_dir; + } + + if (!debugfs_create_file("info", 0444, dir, core, &core_info_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } + if (!debugfs_create_file("trigger_ssr", 0200, + dir, core, &ssr_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } +failed_create_dir: + return dir; +} + +static int inst_info_open(struct inode *inode, struct file *file) +{ + dprintk(VIDC_INFO, "Open inode ptr: %pK\n", inode->i_private); + file->private_data = inode->i_private; + return 0; +} + +static int publish_unreleased_reference(struct msm_vidc_inst *inst, + char **dbuf, char *end) +{ + char *cur = *dbuf; + struct buffer_info *temp = NULL; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid param\n", __func__); + return -EINVAL; + } + + if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) { + cur += write_str(cur, end - cur, "Pending buffer references\n"); + + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (temp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + !temp->inactive && atomic_read(&temp->ref_count)) { + cur += write_str(cur, end - cur, + "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n", + temp->device_addr[0], + temp->fd[0], + atomic_read(&temp->ref_count), + DYNAMIC_BUF_OWNER(temp)); + } + } + mutex_unlock(&inst->registeredbufs.lock); + } + + *dbuf = cur; + return 0; +} + +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); +} + +static ssize_t inst_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct core_inst_pair *idata = file->private_data; + struct msm_vidc_core *core; + struct msm_vidc_inst *inst, *temp = NULL; + char *dbuf, *cur, *end; + int i, j; + ssize_t len = 0; + + if (!idata || !idata->core || !idata->inst) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return 0; + } + + core = idata->core; + inst = idata->inst; + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp == inst) + break; + } + inst = ((temp == inst) && kref_get_unless_zero(&inst->kref)) ? + inst : NULL; + mutex_unlock(&core->lock); + + if (!inst) { + dprintk(VIDC_ERR, "%s: Instance has become obsolete", __func__); + return 0; + } + + dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL); + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + len = -ENOMEM; + goto failed_alloc; + } + cur = dbuf; + end = cur + MAX_DBG_BUF_SIZE; + + cur += write_str(cur, end - cur, "==============================\n"); + cur += write_str(cur, end - cur, "INSTANCE: %pK (%s)\n", inst, + inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder"); + cur += write_str(cur, end - cur, "==============================\n"); + cur += write_str(cur, end - cur, "core: %pK\n", inst->core); + cur += write_str(cur, end - cur, "height: %d\n", + inst->prop.height[CAPTURE_PORT]); + cur += write_str(cur, end - cur, "width: %d\n", + inst->prop.width[CAPTURE_PORT]); + cur += write_str(cur, end - cur, "fps: %d\n", inst->prop.fps); + cur += write_str(cur, end - cur, "state: %d\n", inst->state); + cur += write_str(cur, end - cur, "secure: %d\n", + !!(inst->flags & VIDC_SECURE)); + cur += write_str(cur, end - cur, "-----------Formats-------------\n"); + for (i = 0; i < MAX_PORT_NUM; i++) { + cur += write_str(cur, end - cur, "capability: %s\n", + i == OUTPUT_PORT ? "Output" : "Capture"); + cur += write_str(cur, end - cur, "name : %s\n", + inst->fmts[i].name); + cur += write_str(cur, end - cur, "planes : %d\n", + inst->fmts[i].num_planes); + cur += write_str(cur, end - cur, + "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ? + "Output" : "Capture"); + + switch (inst->buffer_mode_set[i]) { + case HAL_BUFFER_MODE_STATIC: + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "static"); + break; + case HAL_BUFFER_MODE_RING: + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "ring"); + break; + case HAL_BUFFER_MODE_DYNAMIC: + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "dynamic"); + break; + default: + cur += write_str(cur, end - cur, + "buffer mode : unsupported\n"); + } + + cur += write_str(cur, end - cur, "count: %u\n", + inst->bufq[i].vb2_bufq.num_buffers); + + for (j = 0; j < inst->fmts[i].num_planes; j++) + cur += write_str(cur, end - cur, + "size for plane %d: %u\n", j, + inst->bufq[i].plane_sizes[j]); + + if (i < MAX_PORT_NUM - 1) + cur += write_str(cur, end - cur, "\n"); + } + cur += write_str(cur, end - cur, "-------------------------------\n"); + for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) { + cur += write_str(cur, end - cur, "completions[%d]: %s\n", i, + completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ? + "pending" : "done"); + } + + cur += write_str(cur, end - cur, "ETB Count: %d\n", inst->count.etb); + cur += write_str(cur, end - cur, "EBD Count: %d\n", inst->count.ebd); + cur += write_str(cur, end - cur, "FTB Count: %d\n", inst->count.ftb); + cur += write_str(cur, end - cur, "FBD Count: %d\n", inst->count.fbd); + + publish_unreleased_reference(inst, &cur, end); + len = simple_read_from_buffer(buf, count, ppos, + dbuf, cur - dbuf); + + kfree(dbuf); +failed_alloc: + kref_put(&inst->kref, put_inst_helper); + return len; +} + +static int inst_info_release(struct inode *inode, struct file *file) +{ + dprintk(VIDC_INFO, "Release inode ptr: %pK\n", inode->i_private); + file->private_data = NULL; + return 0; +} + +static const struct file_operations inst_info_fops = { + .open = inst_info_open, + .read = inst_info_read, + .release = inst_info_release, +}; + +struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, + struct dentry *parent) +{ + struct dentry *dir = NULL, *info = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + struct core_inst_pair *idata = NULL; + + if (!inst) { + dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst); + goto exit; + } + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%pK", inst); + + idata = kzalloc(sizeof(struct core_inst_pair), GFP_KERNEL); + if (!idata) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + goto exit; + } + + idata->core = inst->core; + idata->inst = inst; + + dir = debugfs_create_dir(debugfs_name, parent); + if (!dir) { + dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); + goto failed_create_dir; + } + + info = debugfs_create_file("info", 0444, dir, + idata, &inst_info_fops); + if (!info) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_file; + } + + dir->d_inode->i_private = info->d_inode->i_private; + inst->debug.pdata[FRAME_PROCESSING].sampling = true; + return dir; + +failed_create_file: + debugfs_remove_recursive(dir); + dir = NULL; +failed_create_dir: + kfree(idata); +exit: + return dir; +} + +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst) +{ + struct dentry *dentry = NULL; + + if (!inst || !inst->debugfs_root) + return; + + dentry = inst->debugfs_root; + if (dentry->d_inode) { + dprintk(VIDC_INFO, "Destroy %pK\n", dentry->d_inode->i_private); + kfree(dentry->d_inode->i_private); + dentry->d_inode->i_private = NULL; + } + debugfs_remove_recursive(dentry); + inst->debugfs_root = NULL; +} + +void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, + enum msm_vidc_debugfs_event e) +{ + struct msm_vidc_debug *d = &inst->debug; + char a[64] = "Frame processing"; + + switch (e) { + case MSM_VIDC_DEBUGFS_EVENT_ETB: + mutex_lock(&inst->lock); + inst->count.etb++; + mutex_unlock(&inst->lock); + if (inst->count.ebd && inst->count.ftb > inst->count.fbd) { + d->pdata[FRAME_PROCESSING].name[0] = '\0'; + tic(inst, FRAME_PROCESSING, a); + } + break; + case MSM_VIDC_DEBUGFS_EVENT_EBD: + mutex_lock(&inst->lock); + inst->count.ebd++; + mutex_unlock(&inst->lock); + if (inst->count.ebd && inst->count.ebd == inst->count.etb) { + toc(inst, FRAME_PROCESSING); + dprintk(VIDC_PROF, "EBD: FW needs input buffers\n"); + } + if (inst->count.ftb == inst->count.fbd) + dprintk(VIDC_PROF, "EBD: FW needs output buffers\n"); + break; + case MSM_VIDC_DEBUGFS_EVENT_FTB: { + inst->count.ftb++; + if (inst->count.ebd && inst->count.etb > inst->count.ebd) { + d->pdata[FRAME_PROCESSING].name[0] = '\0'; + tic(inst, FRAME_PROCESSING, a); + } + } + break; + case MSM_VIDC_DEBUGFS_EVENT_FBD: + inst->debug.samples++; + if (inst->count.ebd && inst->count.fbd == inst->count.ftb) { + toc(inst, FRAME_PROCESSING); + dprintk(VIDC_PROF, "FBD: FW needs output buffers\n"); + } + if (inst->count.etb == inst->count.ebd) + dprintk(VIDC_PROF, "FBD: FW needs input buffers\n"); + break; + default: + dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e); + break; + } +} + diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_debug.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_debug.h new file mode 100644 index 000000000000..51043e235574 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_debug.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2015, 2017-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_VIDC_DEBUG__ +#define __MSM_VIDC_DEBUG__ +#include +#include +#include "msm_vidc_internal.h" +#include "trace/events/msm_vidc.h" + +#ifndef VIDC_DBG_LABEL +#define VIDC_DBG_LABEL "msm_vidc" +#endif + +#define VIDC_DBG_TAG VIDC_DBG_LABEL ": %4s: " +#define VIDC_DBG_WARN_ENABLE (msm_vidc_debug & VIDC_INFO) + +/* To enable messages OR these values and + * echo the result to debugfs file. + * + * To enable all messages set debug_level = 0x101F + */ + +enum vidc_msg_prio { + VIDC_ERR = 0x0001, + VIDC_WARN = 0x0002, + VIDC_INFO = 0x0004, + VIDC_DBG = 0x0008, + VIDC_PROF = 0x0010, + VIDC_PKT = 0x0020, + VIDC_FW = 0x1000, +}; + +enum vidc_msg_out { + VIDC_OUT_PRINTK = 0, + VIDC_OUT_FTRACE, +}; + +enum msm_vidc_debugfs_event { + MSM_VIDC_DEBUGFS_EVENT_ETB, + MSM_VIDC_DEBUGFS_EVENT_EBD, + MSM_VIDC_DEBUGFS_EVENT_FTB, + MSM_VIDC_DEBUGFS_EVENT_FBD, +}; + +extern int msm_vidc_debug; +extern int msm_vidc_debug_out; +extern int msm_vidc_fw_debug; +extern int msm_vidc_fw_debug_mode; +extern int msm_vidc_fw_low_power_mode; +extern int msm_vidc_hw_rsp_timeout; +extern bool msm_vidc_fw_coverage; +extern int msm_vidc_vpe_csc_601_to_709; +extern bool msm_vidc_dec_dcvs_mode; +extern bool msm_vidc_enc_dcvs_mode; +extern bool msm_vidc_sys_idle_indicator; +extern int msm_vidc_firmware_unload_delay; +extern bool msm_vidc_thermal_mitigation_disabled; +extern bool msm_vidc_bitrate_clock_scaling; +extern bool msm_vidc_debug_timeout; + +static inline char *VIDC_MSG_PRIO2STRING(int __level) +{ + char *__str; + + switch (__level) { + case VIDC_ERR: + __str = "err"; + break; + case VIDC_WARN: + __str = "warn"; + break; + case VIDC_INFO: + __str = "info"; + break; + case VIDC_DBG: + __str = "dbg"; + break; + case VIDC_PROF: + __str = "prof"; + break; + case VIDC_PKT: + __str = "pkt"; + break; + case VIDC_FW: + __str = "fw"; + break; + default: + __str = "????"; + break; + } + return __str; +} + +#define dprintk(__level, __fmt, arg...) \ + do { \ + if (msm_vidc_debug & __level) { \ + if (msm_vidc_debug_out == VIDC_OUT_PRINTK) { \ + pr_info(VIDC_DBG_TAG __fmt, \ + VIDC_MSG_PRIO2STRING(__level), \ + ## arg); \ + } \ + } \ +} while (0) + +struct dentry *msm_vidc_debugfs_init_drv(void); +struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, + struct dentry *parent); +struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, + struct dentry *parent); +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst); +void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, + enum msm_vidc_debugfs_event e); + +static inline void tic(struct msm_vidc_inst *i, enum profiling_points p, + char *b) +{ + struct timeval __ddl_tv; + + if (!i->debug.pdata[p].name[0]) + memcpy(i->debug.pdata[p].name, b, 64); + if ((msm_vidc_debug & VIDC_PROF) && + i->debug.pdata[p].sampling) { + do_gettimeofday(&__ddl_tv); + i->debug.pdata[p].start = + (__ddl_tv.tv_sec * 1000) + (__ddl_tv.tv_usec / 1000); + i->debug.pdata[p].sampling = false; + } +} + +static inline void toc(struct msm_vidc_inst *i, enum profiling_points p) +{ + struct timeval __ddl_tv; + + if ((msm_vidc_debug & VIDC_PROF) && + !i->debug.pdata[p].sampling) { + do_gettimeofday(&__ddl_tv); + i->debug.pdata[p].stop = (__ddl_tv.tv_sec * 1000) + + (__ddl_tv.tv_usec / 1000); + i->debug.pdata[p].cumulative += i->debug.pdata[p].stop - + i->debug.pdata[p].start; + i->debug.pdata[p].sampling = true; + } +} + +static inline void show_stats(struct msm_vidc_inst *i) +{ + int x; + + for (x = 0; x < MAX_PROFILING_POINTS; x++) { + if (i->debug.pdata[x].name[0] && + (msm_vidc_debug & VIDC_PROF)) { + if (i->debug.samples) { + dprintk(VIDC_PROF, "%s averaged %d ms/sample\n", + i->debug.pdata[x].name, + i->debug.pdata[x].cumulative / + i->debug.samples); + } + + dprintk(VIDC_PROF, "%s Samples: %d\n", + i->debug.pdata[x].name, + i->debug.samples); + } + } +} + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h new file mode 100644 index 000000000000..b27524e2b868 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_internal.h @@ -0,0 +1,413 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_VIDC_INTERNAL_H_ +#define _MSM_VIDC_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vidc_hfi_api.h" + +#define MSM_VIDC_DRV_NAME "msm_vidc_driver" +//#define MSM_VIDC_VERSION KERNEL_VERSION(0, 0, 1) +#define MAX_DEBUGFS_NAME 50 +#define DEFAULT_TIMEOUT 3 +#define DEFAULT_HEIGHT 1088 +#define DEFAULT_WIDTH 1920 +#define MIN_SUPPORTED_WIDTH 32 +#define MIN_SUPPORTED_HEIGHT 32 +#define DEFAULT_FPS 15 + +/* Maintains the number of FTB's between each FBD over a window */ +#define DCVS_FTB_WINDOW 32 + +#define V4L2_EVENT_VIDC_BASE 10 + +#define SYS_MSG_START HAL_SYS_INIT_DONE +#define SYS_MSG_END HAL_SYS_ERROR +#define SESSION_MSG_START HAL_SESSION_EVENT_CHANGE +#define SESSION_MSG_END HAL_SESSION_ERROR +#define SYS_MSG_INDEX(__msg) (__msg - SYS_MSG_START) +#define SESSION_MSG_INDEX(__msg) (__msg - SESSION_MSG_START) + + +#define MAX_NAME_LENGTH 64 + +#define EXTRADATA_IDX(__num_planes) ((__num_planes) ? (__num_planes) - 1 : 0) + +#define NUM_MBS_PER_SEC(__height, __width, __fps) \ + (NUM_MBS_PER_FRAME(__height, __width) * __fps) + +#define NUM_MBS_PER_FRAME(__height, __width) \ + ((ALIGN(__height, 16) / 16) * (ALIGN(__width, 16) / 16)) + +enum vidc_ports { + OUTPUT_PORT, + CAPTURE_PORT, + MAX_PORT_NUM +}; + +enum vidc_core_state { + VIDC_CORE_UNINIT = 0, + VIDC_CORE_INIT, + VIDC_CORE_INIT_DONE, + VIDC_CORE_INVALID +}; + +/* Do not change the enum values unless + * you know what you are doing + */ +enum instance_state { + MSM_VIDC_CORE_UNINIT_DONE = 0x0001, + MSM_VIDC_CORE_INIT, + MSM_VIDC_CORE_INIT_DONE, + MSM_VIDC_OPEN, + MSM_VIDC_OPEN_DONE, + MSM_VIDC_LOAD_RESOURCES, + MSM_VIDC_LOAD_RESOURCES_DONE, + MSM_VIDC_START, + MSM_VIDC_START_DONE, + MSM_VIDC_STOP, + MSM_VIDC_STOP_DONE, + MSM_VIDC_RELEASE_RESOURCES, + MSM_VIDC_RELEASE_RESOURCES_DONE, + MSM_VIDC_CLOSE, + MSM_VIDC_CLOSE_DONE, + MSM_VIDC_CORE_UNINIT, + MSM_VIDC_CORE_INVALID +}; + +struct buf_info { + struct list_head list; + struct vb2_buffer *buf; +}; + +struct msm_vidc_list { + struct list_head list; + struct mutex lock; +}; + +static inline void INIT_MSM_VIDC_LIST(struct msm_vidc_list *mlist) +{ + mutex_init(&mlist->lock); + INIT_LIST_HEAD(&mlist->list); +} + +static inline void DEINIT_MSM_VIDC_LIST(struct msm_vidc_list *mlist) +{ + mutex_destroy(&mlist->lock); +} + +enum buffer_owner { + DRIVER, + FIRMWARE, + CLIENT, + MAX_OWNER +}; + +struct eos_buf { + struct list_head list; + struct msm_smem smem; +}; + +struct internal_buf { + struct list_head list; + enum hal_buffer buffer_type; + struct msm_smem smem; + enum buffer_owner buffer_ownership; +}; + +struct msm_vidc_format { + char name[MAX_NAME_LENGTH]; + u8 description[32]; + u32 fourcc; + int num_planes; + int type; + u32 (*get_frame_size)(int plane, u32 height, u32 width); +}; + +struct msm_vidc_drv { + struct mutex lock; + struct list_head cores; + int num_cores; + struct dentry *debugfs_root; + int thermal_level; + u32 platform_version; + u32 capability_version; +}; + +struct msm_video_device { + int type; + struct video_device vdev; +}; + +struct session_prop { + u32 width[MAX_PORT_NUM]; + u32 height[MAX_PORT_NUM]; + u32 fps; + u32 bitrate; + u32 operating_rate; +}; + +struct buf_queue { + struct vb2_queue vb2_bufq; + struct mutex lock; + unsigned int plane_sizes[VB2_MAX_PLANES]; + int num_planes; +}; + + +enum profiling_points { + SYS_INIT = 0, + SESSION_INIT, + LOAD_RESOURCES, + FRAME_PROCESSING, + FW_IDLE, + MAX_PROFILING_POINTS, +}; + +struct buf_count { + int etb; + int ftb; + int fbd; + int ebd; +}; + +struct dcvs_stats { + int num_ftb[DCVS_FTB_WINDOW]; + bool transition_turbo; + int ftb_index; + int ftb_counter; + bool prev_freq_lowered; + bool prev_freq_increased; + int threshold_disp_buf_high; + int threshold_disp_buf_low; + int load; + int load_low; + int load_high; + int min_threshold; + int max_threshold; + bool is_clock_scaled; + int etb_counter; + bool is_power_save_mode; + u32 supported_codecs; +}; + +struct profile_data { + int start; + int stop; + int cumulative; + char name[64]; + int sampling; + int average; +}; + +struct msm_vidc_debug { + struct profile_data pdata[MAX_PROFILING_POINTS]; + int profile; + int samples; +}; + +enum msm_vidc_modes { + VIDC_SECURE = BIT(0), + VIDC_TURBO = BIT(1), + VIDC_THUMBNAIL = BIT(2), + VIDC_LOW_POWER = BIT(3), + VIDC_REALTIME = BIT(4), +}; + +struct msm_vidc_core { + struct list_head list; + struct mutex lock; + int id; + struct hfi_device *device; + struct msm_video_device vdev[MSM_VIDC_MAX_DEVICES]; + struct v4l2_device v4l2_dev; + struct list_head instances; + struct dentry *debugfs_root; + enum vidc_core_state state; + struct completion completions[SYS_MSG_END - SYS_MSG_START + 1]; + enum msm_vidc_hfi_type hfi_type; + struct msm_vidc_platform_resources resources; + u32 enc_codec_supported; + u32 dec_codec_supported; + u32 codec_count; + struct msm_vidc_capability *capabilities; + struct delayed_work fw_unload_work; + bool smmu_fault_handled; +}; + +struct msm_vidc_inst { + struct list_head list; + struct mutex sync_lock, lock; + struct msm_vidc_core *core; + enum session_type session_type; + void *session; + struct session_prop prop; + enum instance_state state; + struct msm_vidc_format fmts[MAX_PORT_NUM]; + struct buf_queue bufq[MAX_PORT_NUM]; + struct msm_vidc_list pendingq; + struct msm_vidc_list scratchbufs; + struct msm_vidc_list persistbufs; + struct msm_vidc_list pending_getpropq; + struct msm_vidc_list outputbufs; + struct msm_vidc_list eosbufs; + struct msm_vidc_list registeredbufs; + struct buffer_requirements buff_req; + struct v4l2_ctrl_handler ctrl_handler; + struct completion completions[SESSION_MSG_END - SESSION_MSG_START + 1]; + struct v4l2_ctrl **cluster; + struct v4l2_fh event_handler; + struct msm_smem *extradata_handle; + bool in_reconfig; + u32 reconfig_width; + u32 reconfig_height; + u32 seqchanged_count; + struct dentry *debugfs_root; + void *priv; + struct msm_vidc_debug debug; + struct buf_count count; + struct dcvs_stats dcvs; + enum msm_vidc_modes flags; + struct msm_vidc_capability capability; + u32 buffer_size_limit; + enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM]; + atomic_t seq_hdr_reqs; + struct v4l2_ctrl **ctrls; + bool dcvs_mode; + enum msm_vidc_pixel_depth bit_depth; + struct kref kref; + unsigned long instant_bitrate; + u32 buffers_held_in_driver; + atomic_t in_flush; + u32 pic_struct; + u32 colour_space; +}; + +extern struct msm_vidc_drv *vidc_driver; + +struct msm_vidc_ctrl_cluster { + struct v4l2_ctrl **cluster; + struct list_head list; +}; + +struct msm_vidc_ctrl { + u32 id; + char name[MAX_NAME_LENGTH]; + enum v4l2_ctrl_type type; + s32 minimum; + s32 maximum; + s32 default_value; + u32 step; + u64 menu_skip_mask; + u32 flags; + const char * const *qmenu; +}; + +void handle_cmd_response(enum hal_command_response cmd, void *data); +int msm_vidc_trigger_ssr(struct msm_vidc_core *core, + enum hal_ssr_trigger_type type); +int msm_vidc_check_session_supported(struct msm_vidc_inst *inst); +int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst); +void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type); + +struct crop_info { + u32 nLeft; + u32 nTop; + u32 nWidth; + u32 nHeight; + u32 width_height[MAX_PORT_NUM]; +}; + +struct buffer_info { + struct list_head list; + int type; + int num_planes; + int fd[VIDEO_MAX_PLANES]; + int buff_off[VIDEO_MAX_PLANES]; + int size[VIDEO_MAX_PLANES]; + unsigned long uvaddr[VIDEO_MAX_PLANES]; + phys_addr_t device_addr[VIDEO_MAX_PLANES]; + struct msm_smem smem[VIDEO_MAX_PLANES]; + enum v4l2_memory memory; + u32 v4l2_index; + bool pending_deletion; + atomic_t ref_count; + bool dequeued; + bool inactive; + bool mapped[VIDEO_MAX_PLANES]; + int same_fd_ref[VIDEO_MAX_PLANES]; + struct timeval timestamp; + struct crop_info crop_data; +}; + +struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list, + phys_addr_t device_addr); +int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo); +int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo); + +int qbuf_cache_operations(struct msm_vidc_inst *inst, + struct buffer_info *binfo); +int dqbuf_cache_operations(struct msm_vidc_inst *inst, + struct v4l2_buffer *b, + struct buffer_info *binfo); +int qbuf_dynamic_buf(struct msm_vidc_inst *inst, + struct buffer_info *binfo); +int unmap_and_deregister_buf(struct msm_vidc_inst *inst, + struct buffer_info *binfo); + +void msm_comm_handle_thermal_event(void); +int msm_smem_alloc(size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel, + void *res, u32 session_type, struct msm_smem *smem); +int msm_smem_free(struct msm_smem *mem); +int msm_smem_cache_operations(struct dma_buf *dbuf, + enum smem_cache_ops, unsigned long offset, unsigned long size); +struct context_bank_info *msm_smem_get_context_bank(u32 session_type, + bool is_secure, struct msm_vidc_platform_resources *res, + enum hal_buffer buffer_type); +int msm_smem_map_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem); +int msm_smem_unmap_dma_buf(struct msm_vidc_inst *inst, struct msm_smem *smem); +struct dma_buf *msm_smem_get_dma_buf(int fd); +void msm_smem_put_dma_buf(void *dma_buf); +bool msm_smem_compare_buffers(int fd, void *dma_buf); +struct msm_smem *msm_smem_user_to_kernel(struct msm_vidc_inst *inst, + int fd, u32 offset, + u32 size, enum hal_buffer buffer_type); + +void msm_vidc_fw_unload_handler(struct work_struct *work); +/* XXX: normally should be in msm_vidc.h, but that's meant for public APIs, + * whereas this is private + */ +int msm_vidc_destroy(struct msm_vidc_inst *inst); +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c new file mode 100644 index 000000000000..ae245f4b5d30 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.c @@ -0,0 +1,1617 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "msm_vidc_debug.h" +#include "msm_vidc_resources.h" +#include "msm_vidc_res_parse.h" +#include "venus_boot.h" +#include "soc/qcom/secure_buffer.h" +#include + +enum clock_properties { + CLOCK_PROP_HAS_SCALING = 1 << 0, +}; + +#define PERF_GOV "performance" + +static inline struct device *msm_iommu_get_ctx(const char *ctx_name) +{ + return NULL; +} + +static int msm_vidc_populate_legacy_context_bank( + struct msm_vidc_platform_resources *res); + +static size_t get_u32_array_num_elements(struct device_node *np, + char *name) +{ + int len; + size_t num_elements = 0; + + if (!of_get_property(np, name, &len)) { + dprintk(VIDC_ERR, "Failed to read %s from device tree\n", + name); + goto fail_read; + } + + num_elements = len / sizeof(u32); + if (num_elements <= 0) { + dprintk(VIDC_ERR, "%s not specified in device tree\n", + name); + goto fail_read; + } + return num_elements; + +fail_read: + return 0; +} +static bool is_compatible(char *compat) +{ + return !!of_find_compatible_node(NULL, NULL, compat); +} +static inline enum imem_type read_imem_type(struct platform_device *pdev) +{ + return is_compatible("qcom,msm-ocmem") ? IMEM_OCMEM : + is_compatible("qcom,msm-vmem") ? IMEM_VMEM : + IMEM_NONE; + +} + +static inline void msm_vidc_free_allowed_clocks_table( + struct msm_vidc_platform_resources *res) +{ + res->allowed_clks_tbl = NULL; +} + +static inline void msm_vidc_free_cycles_per_mb_table( + struct msm_vidc_platform_resources *res) +{ + res->clock_freq_tbl.clk_prof_entries = NULL; +} + +static inline void msm_vidc_free_platform_version_table( + struct msm_vidc_platform_resources *res) +{ + res->pf_ver_tbl = NULL; +} + +static inline void msm_vidc_free_capability_version_table( + struct msm_vidc_platform_resources *res) +{ + res->pf_cap_tbl = NULL; +} + +static inline void msm_vidc_free_freq_table( + struct msm_vidc_platform_resources *res) +{ + res->load_freq_tbl = NULL; +} + +static inline void msm_vidc_free_dcvs_table( + struct msm_vidc_platform_resources *res) +{ + res->dcvs_tbl = NULL; +} + +static inline void msm_vidc_free_dcvs_limit( + struct msm_vidc_platform_resources *res) +{ + res->dcvs_limit = NULL; +} + +static inline void msm_vidc_free_imem_ab_table( + struct msm_vidc_platform_resources *res) +{ + res->imem_ab_tbl = NULL; +} + +static inline void msm_vidc_free_reg_table( + struct msm_vidc_platform_resources *res) +{ + res->reg_set.reg_tbl = NULL; +} + +static inline void msm_vidc_free_qdss_addr_table( + struct msm_vidc_platform_resources *res) +{ + res->qdss_addr_set.addr_tbl = NULL; +} + +static inline void msm_vidc_free_bus_vectors( + struct msm_vidc_platform_resources *res) +{ + kfree(res->bus_set.bus_tbl); + res->bus_set.bus_tbl = NULL; + res->bus_set.count = 0; +} + +static inline void msm_vidc_free_buffer_usage_table( + struct msm_vidc_platform_resources *res) +{ + res->buffer_usage_set.buffer_usage_tbl = NULL; +} + +static inline void msm_vidc_free_regulator_table( + struct msm_vidc_platform_resources *res) +{ + int c = 0; + + for (c = 0; c < res->regulator_set.count; ++c) { + struct regulator_info *rinfo = + &res->regulator_set.regulator_tbl[c]; + + rinfo->name = NULL; + } + + res->regulator_set.regulator_tbl = NULL; + res->regulator_set.count = 0; +} + +static inline void msm_vidc_free_clock_table( + struct msm_vidc_platform_resources *res) +{ + res->clock_set.clock_tbl = NULL; + res->clock_set.count = 0; +} + +void msm_vidc_free_platform_resources( + struct msm_vidc_platform_resources *res) +{ + msm_vidc_free_clock_table(res); + msm_vidc_free_regulator_table(res); + msm_vidc_free_freq_table(res); + msm_vidc_free_platform_version_table(res); + msm_vidc_free_capability_version_table(res); + msm_vidc_free_dcvs_table(res); + msm_vidc_free_dcvs_limit(res); + msm_vidc_free_cycles_per_mb_table(res); + msm_vidc_free_allowed_clocks_table(res); + msm_vidc_free_reg_table(res); + msm_vidc_free_qdss_addr_table(res); + msm_vidc_free_bus_vectors(res); + msm_vidc_free_buffer_usage_table(res); +} + +static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res) +{ + struct reg_set *reg_set; + struct platform_device *pdev = res->pdev; + int i; + int rc = 0; + + if (!of_find_property(pdev->dev.of_node, "qcom,reg-presets", NULL)) { + /* qcom,reg-presets is an optional property. It likely won't be + * present if we don't have any register settings to program + */ + dprintk(VIDC_DBG, "qcom,reg-presets not found\n"); + return 0; + } + + reg_set = &res->reg_set; + reg_set->count = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,reg-presets"); + reg_set->count /= sizeof(*reg_set->reg_tbl) / sizeof(u32); + + if (!reg_set->count) { + dprintk(VIDC_DBG, "no elements in reg set\n"); + return rc; + } + + reg_set->reg_tbl = devm_kzalloc(&pdev->dev, reg_set->count * + sizeof(*(reg_set->reg_tbl)), GFP_KERNEL); + if (!reg_set->reg_tbl) { + dprintk(VIDC_ERR, "%s Failed to alloc register table\n", + __func__); + return -ENOMEM; + } + + if (of_property_read_u32_array(pdev->dev.of_node, "qcom,reg-presets", + (u32 *)reg_set->reg_tbl, reg_set->count * 2)) { + dprintk(VIDC_ERR, "Failed to read register table\n"); + msm_vidc_free_reg_table(res); + return -EINVAL; + } + for (i = 0; i < reg_set->count; i++) { + dprintk(VIDC_DBG, + "reg = %x, value = %x\n", + reg_set->reg_tbl[i].reg, + reg_set->reg_tbl[i].value + ); + } + return rc; +} +static int msm_vidc_load_qdss_table(struct msm_vidc_platform_resources *res) +{ + struct addr_set *qdss_addr_set; + struct platform_device *pdev = res->pdev; + int i; + int rc = 0; + + if (!of_find_property(pdev->dev.of_node, "qcom,qdss-presets", NULL)) { + /* qcom,qdss-presets is an optional property. It likely won't be + * present if we don't have any register settings to program + */ + dprintk(VIDC_DBG, "qcom,qdss-presets not found\n"); + return rc; + } + + qdss_addr_set = &res->qdss_addr_set; + qdss_addr_set->count = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,qdss-presets"); + qdss_addr_set->count /= sizeof(*qdss_addr_set->addr_tbl) / sizeof(u32); + + if (!qdss_addr_set->count) { + dprintk(VIDC_DBG, "no elements in qdss reg set\n"); + return rc; + } + + qdss_addr_set->addr_tbl = devm_kzalloc(&pdev->dev, + qdss_addr_set->count * sizeof(*qdss_addr_set->addr_tbl), + GFP_KERNEL); + if (!qdss_addr_set->addr_tbl) { + dprintk(VIDC_ERR, "%s Failed to alloc register table\n", + __func__); + rc = -ENOMEM; + goto err_qdss_addr_tbl; + } + + rc = of_property_read_u32_array(pdev->dev.of_node, "qcom,qdss-presets", + (u32 *)qdss_addr_set->addr_tbl, qdss_addr_set->count * 2); + if (rc) { + dprintk(VIDC_ERR, "Failed to read qdss address table\n"); + msm_vidc_free_qdss_addr_table(res); + rc = -EINVAL; + goto err_qdss_addr_tbl; + } + + for (i = 0; i < qdss_addr_set->count; i++) { + dprintk(VIDC_DBG, "qdss addr = %x, value = %x\n", + qdss_addr_set->addr_tbl[i].start, + qdss_addr_set->addr_tbl[i].size); + } +err_qdss_addr_tbl: + return rc; +} + +static int msm_vidc_load_imem_ab_table(struct msm_vidc_platform_resources *res) +{ + int num_elements = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, "qcom,imem-ab-tbl", NULL)) { + /* optional property */ + dprintk(VIDC_DBG, "qcom,imem-freq-tbl not found\n"); + return 0; + } + + num_elements = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,imem-ab-tbl"); + num_elements /= (sizeof(*res->imem_ab_tbl) / sizeof(u32)); + if (!num_elements) { + dprintk(VIDC_ERR, "no elements in imem ab table\n"); + return -EINVAL; + } + + res->imem_ab_tbl = devm_kzalloc(&pdev->dev, num_elements * + sizeof(*res->imem_ab_tbl), GFP_KERNEL); + if (!res->imem_ab_tbl) { + dprintk(VIDC_ERR, "Failed to alloc imem_ab_tbl\n"); + return -ENOMEM; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "qcom,imem-ab-tbl", (u32 *)res->imem_ab_tbl, + num_elements * sizeof(*res->imem_ab_tbl) / sizeof(u32))) { + dprintk(VIDC_ERR, "Failed to read imem_ab_tbl\n"); + msm_vidc_free_imem_ab_table(res); + return -EINVAL; + } + + res->imem_ab_tbl_size = num_elements; + + return 0; +} + +/** + * msm_vidc_load_u32_table() - load dtsi table entries + * @pdev: A pointer to the platform device. + * @of_node: A pointer to the device node. + * @table_name: A pointer to the dtsi table entry name. + * @struct_size: The size of the structure which is nothing but + * a single entry in the dtsi table. + * @table: A pointer to the table pointer which needs to be + * filled by the dtsi table entries. + * @num_elements: Number of elements pointer which needs to be filled + * with the number of elements in the table. + * + * This is a generic implementation to load single or multiple array + * table from dtsi. The array elements should be of size equal to u32. + * + * Return: Return '0' for success else appropriate error value. + */ +int msm_vidc_load_u32_table(struct platform_device *pdev, + struct device_node *of_node, char *table_name, int struct_size, + u32 **table, u32 *num_elements) +{ + int rc = 0, num_elemts = 0; + u32 *ptbl = NULL; + + if (!of_find_property(of_node, table_name, NULL)) { + dprintk(VIDC_DBG, "%s not found\n", table_name); + return 0; + } + + num_elemts = get_u32_array_num_elements(of_node, table_name); + if (!num_elemts) { + dprintk(VIDC_ERR, "no elements in %s\n", table_name); + return 0; + } + num_elemts /= struct_size / sizeof(u32); + + ptbl = devm_kzalloc(&pdev->dev, num_elemts * struct_size, GFP_KERNEL); + if (!ptbl) { + dprintk(VIDC_ERR, "Failed to alloc table %s\n", table_name); + return -ENOMEM; + } + + if (of_property_read_u32_array(of_node, table_name, ptbl, + num_elemts * struct_size / sizeof(u32))) { + dprintk(VIDC_ERR, "Failed to read %s\n", table_name); + return -EINVAL; + } + + *table = ptbl; + if (num_elements) + *num_elements = num_elemts; + + return rc; +} + +static int msm_vidc_load_platform_version_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, + "qcom,platform-version", NULL)) { + dprintk(VIDC_DBG, "qcom,platform-version not found\n"); + return 0; + } + + rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node, + "qcom,platform-version", + sizeof(*res->pf_ver_tbl), + (u32 **)&res->pf_ver_tbl, + NULL); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to read platform version table\n", + __func__); + return rc; + } + + return 0; +} + +static int msm_vidc_load_capability_version_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, + "qcom,capability-version", NULL)) { + dprintk(VIDC_DBG, "qcom,capability-version not found\n"); + return 0; + } + + rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node, + "qcom,capability-version", + sizeof(*res->pf_cap_tbl), + (u32 **)&res->pf_cap_tbl, + NULL); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to read platform version table\n", + __func__); + return rc; + } + + return 0; +} + +static void clock_override(struct platform_device *pdev, + struct msm_vidc_platform_resources *platform_res, + struct allowed_clock_rates_table *clk_table) +{ + struct resource *res; + void __iomem *base = NULL; + u32 config_efuse, bin; + u32 venus_uplift_freq; + u32 is_speed_bin = 7; + int rc = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "efuse"); + if (!res) { + dprintk(VIDC_DBG, + "Failed to get resource efuse\n"); + return; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,venus-uplift-freq", + &venus_uplift_freq); + if (rc) { + dprintk(VIDC_DBG, + "Failed to determine venus-uplift-freq: %d\n", rc); + return; + } + + if (!of_find_property(pdev->dev.of_node, + "qcom,speedbin-version", NULL)) { + dprintk(VIDC_DBG, "qcom,speedbin-version not found\n"); + return; + } + + rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node, + "qcom,speedbin-version", + sizeof(*platform_res->pf_speedbin_tbl), + (u32 **)&platform_res->pf_speedbin_tbl, + NULL); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to read speedbin version table\n", + __func__); + return; + } + base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!base) { + dev_warn(&pdev->dev, + "Unable to ioremap efuse reg address. Defaulting to 0.\n"); + return; + } + + config_efuse = readl_relaxed(base); + //devm_iounmap(&pdev->dev, base); + + bin = (config_efuse >> platform_res->pf_speedbin_tbl->version_shift) & + platform_res->pf_speedbin_tbl->version_mask; + + if (bin == is_speed_bin) { + dprintk(VIDC_DBG, + "Venus speed binning available overwriting %d to %d\n", + clk_table[0].clock_rate, venus_uplift_freq); + clk_table[0].clock_rate = venus_uplift_freq; + } +} + +static int msm_vidc_load_allowed_clocks_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, + "qcom,allowed-clock-rates", NULL)) { + dprintk(VIDC_DBG, "qcom,allowed-clock-rates not found\n"); + return 0; + } + + rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node, + "qcom,allowed-clock-rates", + sizeof(*res->allowed_clks_tbl), + (u32 **)&res->allowed_clks_tbl, + &res->allowed_clks_tbl_size); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed to read allowed clocks table\n", __func__); + return rc; + } + if (res->allowed_clks_tbl_size) + clock_override(pdev, res, res->allowed_clks_tbl); + + return 0; +} + +static int msm_vidc_load_cycles_per_mb_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0, i = 0; + struct clock_freq_table *clock_freq_tbl = &res->clock_freq_tbl; + struct clock_profile_entry *entry = NULL; + struct device_node *parent_node = NULL; + struct device_node *child_node = NULL; + struct platform_device *pdev = res->pdev; + + parent_node = of_find_node_by_name(pdev->dev.of_node, + "qcom,clock-freq-tbl"); + if (!parent_node) { + dprintk(VIDC_DBG, "Node qcom,clock-freq-tbl not found.\n"); + return 0; + } + + clock_freq_tbl->count = 0; + for_each_child_of_node(parent_node, child_node) + clock_freq_tbl->count++; + + if (!clock_freq_tbl->count) { + dprintk(VIDC_DBG, "No child nodes in qcom,clock-freq-tbl\n"); + return 0; + } + + clock_freq_tbl->clk_prof_entries = devm_kzalloc(&pdev->dev, + sizeof(*clock_freq_tbl->clk_prof_entries) * + clock_freq_tbl->count, GFP_KERNEL); + if (!clock_freq_tbl->clk_prof_entries) { + dprintk(VIDC_DBG, "no memory to allocate clk_prof_entries\n"); + return -ENOMEM; + } + + for_each_child_of_node(parent_node, child_node) { + + if (i >= clock_freq_tbl->count) { + dprintk(VIDC_ERR, + "qcom,clock-freq-tbl: invalid child node %d, max is %d\n", + i, clock_freq_tbl->count); + break; + } + + entry = &clock_freq_tbl->clk_prof_entries[i]; + dprintk(VIDC_DBG, "qcom,clock-freq-tbl: profile[%d]\n", i); + + if (of_find_property(child_node, "qcom,codec-mask", NULL)) { + rc = of_property_read_u32(child_node, + "qcom,codec-mask", &entry->codec_mask); + if (rc) { + dprintk(VIDC_ERR, + "qcom,codec-mask not found\n"); + goto error; + } + } else { + entry->codec_mask = 0; + } + dprintk(VIDC_DBG, "codec_mask %#x\n", entry->codec_mask); + + if (of_find_property(child_node, "qcom,cycles-per-mb", NULL)) { + rc = of_property_read_u32(child_node, + "qcom,cycles-per-mb", &entry->cycles); + if (rc) { + dprintk(VIDC_ERR, + "qcom,cycles-per-mb not found\n"); + goto error; + } + } else { + entry->cycles = 0; + } + dprintk(VIDC_DBG, "cycles_per_mb %d\n", entry->cycles); + + if (of_find_property(child_node, + "qcom,low-power-mode-factor", NULL)) { + rc = of_property_read_u32(child_node, + "qcom,low-power-mode-factor", + &entry->low_power_factor); + if (rc) { + dprintk(VIDC_ERR, + "qcom,low-power-mode-factor not found\n"); + goto error; + } + } else { + entry->low_power_factor = 0; + } + dprintk(VIDC_DBG, "low_power_factor %d\n", + entry->low_power_factor); + + i++; + } + +error: + return rc; +} +/* A comparator to compare loads (needed later on) */ +static int cmp(const void *a, const void *b) +{ + /* want to sort in reverse so flip the comparison */ + return ((struct load_freq_table *)b)->load - + ((struct load_freq_table *)a)->load; +} +static int msm_vidc_load_freq_table(struct msm_vidc_platform_resources *res) +{ + int rc = 0; + int num_elements = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, "qcom,load-freq-tbl", NULL)) { + /* qcom,load-freq-tbl is an optional property. It likely won't + * be present on cores that we can't clock scale on. + */ + dprintk(VIDC_DBG, "qcom,load-freq-tbl not found\n"); + return 0; + } + + num_elements = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,load-freq-tbl"); + num_elements /= sizeof(*res->load_freq_tbl) / sizeof(u32); + if (!num_elements) { + dprintk(VIDC_ERR, "no elements in frequency table\n"); + return rc; + } + + res->load_freq_tbl = devm_kzalloc(&pdev->dev, num_elements * + sizeof(*res->load_freq_tbl), GFP_KERNEL); + if (!res->load_freq_tbl) { + dprintk(VIDC_ERR, + "%s Failed to alloc load_freq_tbl\n", + __func__); + return -ENOMEM; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "qcom,load-freq-tbl", (u32 *)res->load_freq_tbl, + num_elements * sizeof(*res->load_freq_tbl) / sizeof(u32))) { + dprintk(VIDC_ERR, "Failed to read frequency table\n"); + msm_vidc_free_freq_table(res); + return -EINVAL; + } + + res->load_freq_tbl_size = num_elements; + + /* The entries in the DT might not be sorted (for aesthetic purposes). + * Given that we expect the loads in descending order for our scaling + * logic to work, just sort it ourselves + */ + sort(res->load_freq_tbl, res->load_freq_tbl_size, + sizeof(*res->load_freq_tbl), cmp, NULL); + return rc; +} + +static int msm_vidc_load_dcvs_table(struct msm_vidc_platform_resources *res) +{ + int rc = 0; + int num_elements = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, "qcom,dcvs-tbl", NULL)) { + /* + * qcom,dcvs-tbl is an optional property. Incase qcom,dcvs-limit + * property is present, it becomes mandatory. It likely won't + * be present on targets that does not support the feature + */ + dprintk(VIDC_DBG, "qcom,dcvs-tbl not found\n"); + return 0; + } + + num_elements = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,dcvs-tbl"); + num_elements /= sizeof(*res->dcvs_tbl) / sizeof(u32); + if (!num_elements) { + dprintk(VIDC_ERR, "no elements in dcvs table\n"); + return rc; + } + + res->dcvs_tbl = devm_kzalloc(&pdev->dev, num_elements * + sizeof(*res->dcvs_tbl), GFP_KERNEL); + if (!res->dcvs_tbl) { + dprintk(VIDC_ERR, + "%s Failed to alloc dcvs_tbl\n", + __func__); + return -ENOMEM; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "qcom,dcvs-tbl", (u32 *)res->dcvs_tbl, + num_elements * sizeof(*res->dcvs_tbl) / sizeof(u32))) { + dprintk(VIDC_ERR, "Failed to read dcvs table\n"); + msm_vidc_free_dcvs_table(res); + return -EINVAL; + } + res->dcvs_tbl_size = num_elements; + + return rc; +} + +static int msm_vidc_load_dcvs_limit(struct msm_vidc_platform_resources *res) +{ + int rc = 0; + int num_elements = 0; + struct platform_device *pdev = res->pdev; + + if (!of_find_property(pdev->dev.of_node, "qcom,dcvs-limit", NULL)) { + /* + * qcom,dcvs-limit is an optional property. Incase qcom,dcvs-tbl + * property is present, it becomes mandatory. It likely won't + * be present on targets that does not support the feature + */ + dprintk(VIDC_DBG, "qcom,dcvs-limit not found\n"); + return 0; + } + + num_elements = get_u32_array_num_elements(pdev->dev.of_node, + "qcom,dcvs-limit"); + num_elements /= sizeof(*res->dcvs_limit) / sizeof(u32); + if (!num_elements) { + dprintk(VIDC_ERR, "no elements in dcvs limit\n"); + res->dcvs_limit = NULL; + return rc; + } + + res->dcvs_limit = devm_kzalloc(&pdev->dev, num_elements * + sizeof(*res->dcvs_limit), GFP_KERNEL); + if (!res->dcvs_limit) { + dprintk(VIDC_ERR, + "%s Failed to alloc dcvs_limit\n", + __func__); + return -ENOMEM; + } + if (of_property_read_u32_array(pdev->dev.of_node, + "qcom,dcvs-limit", (u32 *)res->dcvs_limit, + num_elements * sizeof(*res->dcvs_limit) / sizeof(u32))) { + dprintk(VIDC_ERR, "Failed to read dcvs limit\n"); + msm_vidc_free_dcvs_limit(res); + return -EINVAL; + } + + return rc; +} + + +static int msm_vidc_populate_bus(struct device *dev, + struct msm_vidc_platform_resources *res) +{ + struct bus_set *buses = &res->bus_set; + const char *temp_name = NULL; + struct bus_info *bus = NULL, *temp_table; + u32 range[2]; + int rc = 0; + + temp_table = krealloc(buses->bus_tbl, sizeof(*temp_table) * + (buses->count + 1), GFP_KERNEL); + if (!temp_table) { + dprintk(VIDC_ERR, "%s: Failed to allocate memory", __func__); + rc = -ENOMEM; + goto err_bus; + } + + buses->bus_tbl = temp_table; + bus = &buses->bus_tbl[buses->count]; + + rc = of_property_read_string(dev->of_node, "label", &temp_name); + if (rc) { + dprintk(VIDC_ERR, "'label' not found in node\n"); + goto err_bus; + } + /* need a non-const version of name, hence copying it over */ + bus->name = devm_kstrdup(dev, temp_name, GFP_KERNEL); + if (!bus->name) { + rc = -ENOMEM; + goto err_bus; + } + + rc = of_property_read_u32(dev->of_node, "qcom,bus-master", + &bus->master); + if (rc) { + dprintk(VIDC_ERR, "'qcom,bus-master' not found in node\n"); + goto err_bus; + } + + rc = of_property_read_u32(dev->of_node, "qcom,bus-slave", &bus->slave); + if (rc) { + dprintk(VIDC_ERR, "'qcom,bus-slave' not found in node\n"); + goto err_bus; + } + + rc = of_property_read_string(dev->of_node, "qcom,mode", + &bus->mode); + if (!rc && !strcmp(bus->mode, PERF_GOV)) + bus->is_prfm_gov_used = true; + + rc = of_property_read_u32_array(dev->of_node, "qcom,bus-range-kbps", + range, ARRAY_SIZE(range)); + if (rc) { + rc = 0; + dprintk(VIDC_DBG, + "'qcom,range' not found defaulting to <0 INT_MAX>\n"); + range[0] = 0; + range[1] = INT_MAX; + } + + bus->range[0] = range[0]; /* min */ + bus->range[1] = range[1]; /* max */ + + buses->count++; + bus->dev = dev; + dprintk(VIDC_DBG, "Found bus %s [%d->%d] with mode %s\n", + bus->name, bus->master, bus->slave, bus->mode); + +err_bus: + return rc; +} + +static int msm_vidc_load_buffer_usage_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + struct buffer_usage_set *buffer_usage_set = &res->buffer_usage_set; + + if (!of_find_property(pdev->dev.of_node, + "qcom,buffer-type-tz-usage-table", NULL)) { + /* qcom,buffer-type-tz-usage-table is an optional property. It + * likely won't be present if the core doesn't support content + * protection + */ + dprintk(VIDC_DBG, "buffer-type-tz-usage-table not found\n"); + return 0; + } + + buffer_usage_set->count = get_u32_array_num_elements( + pdev->dev.of_node, "qcom,buffer-type-tz-usage-table"); + buffer_usage_set->count /= + sizeof(*buffer_usage_set->buffer_usage_tbl) / sizeof(u32); + if (!buffer_usage_set->count) { + dprintk(VIDC_DBG, "no elements in buffer usage set\n"); + return 0; + } + + buffer_usage_set->buffer_usage_tbl = devm_kzalloc(&pdev->dev, + buffer_usage_set->count * + sizeof(*buffer_usage_set->buffer_usage_tbl), + GFP_KERNEL); + if (!buffer_usage_set->buffer_usage_tbl) { + dprintk(VIDC_ERR, "%s Failed to alloc buffer usage table\n", + __func__); + rc = -ENOMEM; + goto err_load_buf_usage; + } + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,buffer-type-tz-usage-table", + (u32 *)buffer_usage_set->buffer_usage_tbl, + buffer_usage_set->count * + sizeof(*buffer_usage_set->buffer_usage_tbl) / sizeof(u32)); + if (rc) { + dprintk(VIDC_ERR, "Failed to read buffer usage table\n"); + goto err_load_buf_usage; + } + + return 0; +err_load_buf_usage: + msm_vidc_free_buffer_usage_table(res); + return rc; +} + +static int msm_vidc_load_regulator_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = res->pdev; + struct regulator_set *regulators = &res->regulator_set; + struct device_node *domains_parent_node = NULL; + struct property *domains_property = NULL; + int reg_count = 0; + + regulators->count = 0; + regulators->regulator_tbl = NULL; + + domains_parent_node = pdev->dev.of_node; + for_each_property_of_node(domains_parent_node, domains_property) { + const char *search_string = "-supply"; + char *supply; + bool matched = false; + + /* check if current property is possibly a regulator */ + supply = strnstr(domains_property->name, search_string, + strlen(domains_property->name) + 1); + matched = supply && (*(supply + strlen(search_string)) == '\0'); + if (!matched) + continue; + + reg_count++; + } + + regulators->regulator_tbl = devm_kzalloc(&pdev->dev, + sizeof(*regulators->regulator_tbl) * + reg_count, GFP_KERNEL); + + if (!regulators->regulator_tbl) { + rc = -ENOMEM; + dprintk(VIDC_ERR, + "Failed to alloc memory for regulator table\n"); + goto err_reg_tbl_alloc; + } + + for_each_property_of_node(domains_parent_node, domains_property) { + const char *search_string = "-supply"; + char *supply; + bool matched = false; + struct device_node *regulator_node = NULL; + struct regulator_info *rinfo = NULL; + + /* check if current property is possibly a regulator */ + supply = strnstr(domains_property->name, search_string, + strlen(domains_property->name) + 1); + matched = supply && (supply[strlen(search_string)] == '\0'); + if (!matched) + continue; + + /* make sure prop isn't being misused */ + regulator_node = of_parse_phandle(domains_parent_node, + domains_property->name, 0); + if (IS_ERR(regulator_node)) { + dprintk(VIDC_WARN, "%s is not a phandle\n", + domains_property->name); + continue; + } + regulators->count++; + + /* populate regulator info */ + rinfo = ®ulators->regulator_tbl[regulators->count - 1]; + rinfo->name = devm_kzalloc(&pdev->dev, + (supply - domains_property->name) + 1, GFP_KERNEL); + if (!rinfo->name) { + rc = -ENOMEM; + dprintk(VIDC_ERR, + "Failed to alloc memory for regulator name\n"); + goto err_reg_name_alloc; + } + strlcpy(rinfo->name, domains_property->name, + (supply - domains_property->name) + 1); + + rinfo->has_hw_power_collapse = of_property_read_bool( + regulator_node, "qcom,support-hw-trigger"); + + dprintk(VIDC_DBG, "Found regulator %s: h/w collapse = %s\n", + rinfo->name, + rinfo->has_hw_power_collapse ? "yes" : "no"); + } + + if (!regulators->count) + dprintk(VIDC_DBG, "No regulators found"); + + return 0; + +err_reg_name_alloc: +err_reg_tbl_alloc: + msm_vidc_free_regulator_table(res); + return rc; +} + +static int msm_vidc_load_clock_table( + struct msm_vidc_platform_resources *res) +{ + int rc = 0, num_clocks = 0, c = 0; + struct platform_device *pdev = res->pdev; + int *clock_props = NULL; + struct clock_set *clocks = &res->clock_set; + + num_clocks = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (num_clocks <= 0) { + dprintk(VIDC_DBG, "No clocks found\n"); + clocks->count = 0; + rc = 0; + goto err_load_clk_table_fail; + } + + clock_props = devm_kzalloc(&pdev->dev, num_clocks * + sizeof(*clock_props), GFP_KERNEL); + if (!clock_props) { + dprintk(VIDC_ERR, "No memory to read clock properties\n"); + rc = -ENOMEM; + goto err_load_clk_table_fail; + } + + rc = of_property_read_u32_array(pdev->dev.of_node, + "qcom,clock-configs", clock_props, + num_clocks); + if (rc) { + dprintk(VIDC_ERR, "Failed to read clock properties: %d\n", rc); + goto err_load_clk_prop_fail; + } + + clocks->clock_tbl = devm_kzalloc(&pdev->dev, sizeof(*clocks->clock_tbl) + * num_clocks, GFP_KERNEL); + if (!clocks->clock_tbl) { + dprintk(VIDC_ERR, "Failed to allocate memory for clock tbl\n"); + rc = -ENOMEM; + goto err_load_clk_prop_fail; + } + + clocks->count = num_clocks; + dprintk(VIDC_DBG, "Found %d clocks\n", num_clocks); + + for (c = 0; c < num_clocks; ++c) { + struct clock_info *vc = &res->clock_set.clock_tbl[c]; + + of_property_read_string_index(pdev->dev.of_node, + "clock-names", c, &vc->name); + + if (clock_props[c] & CLOCK_PROP_HAS_SCALING) { + vc->has_scaling = true; + vc->count = res->load_freq_tbl_size; + vc->load_freq_tbl = res->load_freq_tbl; + } else { + vc->count = 0; + vc->load_freq_tbl = NULL; + vc->has_scaling = false; + } + + dprintk(VIDC_DBG, "Found clock %s: scale-able = %s\n", vc->name, + vc->count ? "yes" : "no"); + } + + + return 0; + +err_load_clk_prop_fail: +err_load_clk_table_fail: + return rc; +} + +int read_platform_resources_from_dt( + struct msm_vidc_platform_resources *res) +{ + struct platform_device *pdev = res->pdev; + struct resource *kres = NULL; + int rc = 0; + uint32_t firmware_base = 0; + + if (!pdev->dev.of_node) { + dprintk(VIDC_ERR, "DT node not found\n"); + return -ENOENT; + } + + INIT_LIST_HEAD(&res->context_banks); + + res->firmware_base = (phys_addr_t)firmware_base; + + kres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res->register_base = kres ? kres->start : -1; + res->register_size = kres ? (kres->end + 1 - kres->start) : -1; + + kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + res->irq = kres ? kres->start : -1; + + of_property_read_u32(pdev->dev.of_node, + "qcom,imem-size", &res->imem_size); + res->imem_type = read_imem_type(pdev); + + res->sys_idle_indicator = of_property_read_bool(pdev->dev.of_node, + "qcom,enable-idle-indicator"); + + res->thermal_mitigable = + of_property_read_bool(pdev->dev.of_node, + "qcom,enable-thermal-mitigation"); + + rc = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name", + &res->fw_name); + if (rc) { + dprintk(VIDC_ERR, "Failed to read firmware name: %d\n", rc); + goto err_load_freq_table; + } + dprintk(VIDC_DBG, "Firmware filename: %s\n", res->fw_name); + + rc = of_property_read_string(pdev->dev.of_node, "qcom,hfi-version", + &res->hfi_version); + if (rc) + dprintk(VIDC_DBG, "HFI packetization will default to legacy\n"); + + rc = msm_vidc_load_platform_version_table(res); + if (rc) + dprintk(VIDC_ERR, "Failed to load pf version table: %d\n", rc); + + rc = msm_vidc_load_capability_version_table(res); + if (rc) + dprintk(VIDC_ERR, + "Failed to load pf capability table: %d\n", rc); + + rc = msm_vidc_load_freq_table(res); + if (rc) { + dprintk(VIDC_ERR, "Failed to load freq table: %d\n", rc); + goto err_load_freq_table; + } + + rc = msm_vidc_load_dcvs_table(res); + if (rc) + dprintk(VIDC_WARN, "Failed to load dcvs table: %d\n", rc); + + rc = msm_vidc_load_dcvs_limit(res); + if (rc) + dprintk(VIDC_WARN, "Failed to load dcvs limit: %d\n", rc); + + rc = msm_vidc_load_imem_ab_table(res); + if (rc) + dprintk(VIDC_WARN, "Failed to load freq table: %d\n", rc); + + rc = msm_vidc_load_qdss_table(res); + if (rc) + dprintk(VIDC_WARN, "Failed to load qdss reg table: %d\n", rc); + + rc = msm_vidc_load_reg_table(res); + if (rc) { + dprintk(VIDC_ERR, "Failed to load reg table: %d\n", rc); + goto err_load_reg_table; + } + + rc = msm_vidc_load_buffer_usage_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load buffer usage table: %d\n", rc); + goto err_load_buffer_usage_table; + } + + rc = msm_vidc_load_regulator_table(res); + if (rc) { + dprintk(VIDC_ERR, "Failed to load list of regulators %d\n", rc); + goto err_load_regulator_table; + } + + rc = msm_vidc_load_clock_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load clock table: %d\n", rc); + goto err_load_clock_table; + } + + rc = msm_vidc_load_cycles_per_mb_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load cycles per mb table: %d\n", rc); + goto err_load_cycles_per_mb_table; + } + + rc = msm_vidc_load_allowed_clocks_table(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to load allowed clocks table: %d\n", rc); + goto err_load_allowed_clocks_table; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-hw-load", + &res->max_load); + if (rc) { + dprintk(VIDC_ERR, + "Failed to determine max load supported: %d\n", rc); + goto err_load_max_hw_load; + } + + rc = msm_vidc_populate_legacy_context_bank(res); + if (rc) { + dprintk(VIDC_ERR, + "Failed to setup context banks %d\n", rc); + goto err_setup_legacy_cb; + } + + res->use_non_secure_pil = of_property_read_bool(pdev->dev.of_node, + "qcom,use-non-secure-pil"); + + if (res->use_non_secure_pil || !is_iommu_present(res)) { + of_property_read_u32(pdev->dev.of_node, "qcom,fw-bias", + &firmware_base); + res->firmware_base = (phys_addr_t)firmware_base; + dprintk(VIDC_DBG, + "Using fw-bias : %pa", &res->firmware_base); + } + + res->sw_power_collapsible = of_property_read_bool(pdev->dev.of_node, + "qcom,sw-power-collapse"); + dprintk(VIDC_DBG, "Power collapse supported = %s\n", + res->sw_power_collapsible ? "yes" : "no"); + + res->never_unload_fw = of_property_read_bool(pdev->dev.of_node, + "qcom,never-unload-fw"); + + of_property_read_u32(pdev->dev.of_node, + "qcom,pm-qos-latency-us", &res->pm_qos_latency_us); + + res->slave_side_cp = of_property_read_bool(pdev->dev.of_node, + "qcom,slave-side-cp"); + dprintk(VIDC_DBG, "Slave side cp = %s\n", + res->slave_side_cp ? "yes" : "no"); + + of_property_read_u32(pdev->dev.of_node, + "qcom,max-secure-instances", + &res->max_secure_inst_count); + + return rc; + +err_setup_legacy_cb: +err_load_max_hw_load: + msm_vidc_free_allowed_clocks_table(res); +err_load_allowed_clocks_table: + msm_vidc_free_cycles_per_mb_table(res); +err_load_cycles_per_mb_table: + msm_vidc_free_clock_table(res); +err_load_clock_table: + msm_vidc_free_regulator_table(res); +err_load_regulator_table: + msm_vidc_free_buffer_usage_table(res); +err_load_buffer_usage_table: + msm_vidc_free_reg_table(res); +err_load_reg_table: + msm_vidc_free_freq_table(res); +err_load_freq_table: + return rc; +} +static int msm_vidc_setup_context_bank(struct context_bank_info *cb, + struct device *dev) +{ + int rc = 0; + struct bus_type *bus; + + if (!dev || !cb) { + dprintk(VIDC_ERR, + "%s: Invalid Input params\n", __func__); + return -EINVAL; + } + cb->dev = dev; + + bus = cb->dev->bus; + if (IS_ERR_OR_NULL(bus)) { + dprintk(VIDC_ERR, "%s - failed to get bus type\n", __func__); + rc = PTR_ERR(bus) ?: -ENODEV; + goto remove_cb; + } + + cb->domain = iommu_get_domain_for_dev(cb->dev); + + /* + * configure device segment size and segment boundary to ensure + * iommu mapping returns one mapping (which is required for partial + * cache operations) + */ + if (!dev->dma_parms) + dev->dma_parms = + devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL); + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + dma_set_seg_boundary(dev, (unsigned long)DMA_BIT_MASK(64)); + + dprintk(VIDC_DBG, "Attached %s and created mapping\n", dev_name(dev)); + dprintk(VIDC_DBG, + "Context bank name:%s, buffer_type: %#x, is_secure: %d, address range start: %#x, size: %#x, dev: %pK, mapping: %pK", + cb->name, cb->buffer_type, cb->is_secure, cb->addr_range.start, + cb->addr_range.size, cb->dev, cb->domain); + + return rc; + +remove_cb: + return rc; +} + +int msm_vidc_smmu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags, void *token) +{ + struct msm_vidc_core *core = token; + struct msm_vidc_inst *inst; + struct buffer_info *temp; + struct internal_buf *buf; + int i = 0; + bool is_decode = false; + enum vidc_ports port; + + if (!domain || !core) { + dprintk(VIDC_ERR, "%s - invalid param %pK %pK\n", + __func__, domain, core); + return -EINVAL; + } + + if (core->smmu_fault_handled) + return -EINVAL; + + dprintk(VIDC_ERR, "%s - faulting address: %lx\n", __func__, iova); + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + is_decode = inst->session_type == MSM_VIDC_DECODER; + port = is_decode ? OUTPUT_PORT : CAPTURE_PORT; + dprintk(VIDC_ERR, + "%s session, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n", + is_decode ? "Decode" : "Encode", inst->fmts[port].name, + inst->prop.height[port], inst->prop.width[port], + inst->prop.fps, 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(temp, &inst->registeredbufs.list, list) + for (i = 0; i < temp->num_planes; i++) + dprintk(VIDC_ERR, + "type: %d plane: %d addr: %pa size: %d\n", + temp->type, i, &temp->device_addr[i], + temp->size[i]); + + 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: %pa 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: %pa 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: %pa size: %u\n", + buf->buffer_type, &buf->smem.device_addr, + buf->smem.size); + mutex_unlock(&inst->outputbufs.lock); + } + core->smmu_fault_handled = true; + mutex_unlock(&core->lock); + /* + * Return -ENOSYS to elicit the default behaviour of smmu driver. + * If we return -ENOSYS, then smmu driver assumes page fault handler + * is not installed and prints a list of useful debug information like + * FAR, SID etc. This information is not printed if we return 0. + */ + return -EINVAL; +} + +static int msm_vidc_populate_context_bank(struct device *dev, + struct msm_vidc_core *core) +{ + int rc = 0; + struct context_bank_info *cb = NULL; + struct device_node *np = NULL; + + if (!dev || !core) { + dprintk(VIDC_ERR, "%s - invalid inputs\n", __func__); + return -EINVAL; + } + + np = dev->of_node; + cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL); + if (!cb) { + dprintk(VIDC_ERR, "%s - Failed to allocate cb\n", __func__); + return -ENOMEM; + } + + INIT_LIST_HEAD(&cb->list); + list_add_tail(&cb->list, &core->resources.context_banks); + + rc = of_property_read_string(np, "label", &cb->name); + if (rc) { + dprintk(VIDC_DBG, + "Failed to read cb label from device tree\n"); + rc = 0; + } + + dprintk(VIDC_DBG, "%s: context bank has name %s\n", __func__, cb->name); + rc = of_property_read_u32_array(np, "virtual-addr-pool", + (u32 *)&cb->addr_range, 2); + if (rc) { + dprintk(VIDC_ERR, + "Could not read addr pool for context bank : %s %d\n", + cb->name, rc); + goto err_setup_cb; + } + + cb->is_secure = of_property_read_bool(np, "qcom,secure-context-bank"); + dprintk(VIDC_DBG, "context bank %s : secure = %d\n", + cb->name, cb->is_secure); + + /* setup buffer type for each sub device*/ + rc = of_property_read_u32(np, "buffer-types", &cb->buffer_type); + if (rc) { + dprintk(VIDC_ERR, "failed to load buffer_type info %d\n", rc); + rc = -ENOENT; + goto err_setup_cb; + } + dprintk(VIDC_DBG, + "context bank %s address start = %x address size = %x buffer_type = %x\n", + cb->name, cb->addr_range.start, + cb->addr_range.size, cb->buffer_type); + + rc = msm_vidc_setup_context_bank(cb, dev); + if (rc) { + dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc); + goto err_setup_cb; + } + + iommu_set_fault_handler(cb->domain, + msm_vidc_smmu_fault_handler, (void *)core); + + return 0; + +err_setup_cb: + list_del(&cb->list); + return rc; +} + +static int msm_vidc_populate_legacy_context_bank( + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct platform_device *pdev = NULL; + struct device_node *domains_parent_node = NULL; + struct device_node *domains_child_node = NULL; + struct device_node *ctx_node = NULL; + struct context_bank_info *cb; + + if (!res || !res->pdev) { + dprintk(VIDC_ERR, "%s - invalid inputs\n", __func__); + return -EINVAL; + } + pdev = res->pdev; + + domains_parent_node = of_find_node_by_name(pdev->dev.of_node, + "qcom,vidc-iommu-domains"); + if (!domains_parent_node) { + dprintk(VIDC_DBG, + "%s legacy iommu domains not present\n", __func__); + return 0; + } + + /* set up each context bank for legacy DT bindings*/ + for_each_child_of_node(domains_parent_node, + domains_child_node) { + cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL); + if (!cb) { + dprintk(VIDC_ERR, + "%s - Failed to allocate cb\n", __func__); + return -ENOMEM; + } + INIT_LIST_HEAD(&cb->list); + list_add_tail(&cb->list, &res->context_banks); + + ctx_node = of_parse_phandle(domains_child_node, + "qcom,vidc-domain-phandle", 0); + if (!ctx_node) { + dprintk(VIDC_ERR, + "%s Unable to parse pHandle\n", __func__); + rc = -EBADHANDLE; + goto err_setup_cb; + } + + rc = of_property_read_string(ctx_node, "label", &(cb->name)); + if (rc) { + dprintk(VIDC_ERR, + "%s Could not find label\n", __func__); + goto err_setup_cb; + } + + rc = of_property_read_u32_array(ctx_node, + "qcom,virtual-addr-pool", (u32 *)&cb->addr_range, 2); + if (rc) { + dprintk(VIDC_ERR, + "%s Could not read addr pool for group : %s (%d)\n", + __func__, cb->name, rc); + goto err_setup_cb; + } + + cb->is_secure = + of_property_read_bool(ctx_node, "qcom,secure-domain"); + + rc = of_property_read_u32(domains_child_node, + "qcom,vidc-buffer-types", &cb->buffer_type); + if (rc) { + dprintk(VIDC_ERR, + "%s Could not read buffer type (%d)\n", + __func__, rc); + goto err_setup_cb; + } + + cb->dev = msm_iommu_get_ctx(cb->name); + if (IS_ERR_OR_NULL(cb->dev)) { + dprintk(VIDC_ERR, "%s could not get device for cb %s\n", + __func__, cb->name); + rc = -ENOENT; + goto err_setup_cb; + } + + rc = msm_vidc_setup_context_bank(cb, cb->dev); + if (rc) { + dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc); + goto err_setup_cb; + } + dprintk(VIDC_DBG, + "%s: context bank %s secure %d addr start = %#x addr size = %#x buffer_type = %#x\n", + __func__, cb->name, cb->is_secure, cb->addr_range.start, + cb->addr_range.size, cb->buffer_type); + } + return rc; + +err_setup_cb: + list_del(&cb->list); + return rc; +} + +int read_context_bank_resources_from_dt(struct platform_device *pdev) +{ + struct msm_vidc_core *core; + int rc = 0; + + if (!pdev) { + dprintk(VIDC_ERR, "Invalid platform device\n"); + return -EINVAL; + } else if (!pdev->dev.parent) { + dprintk(VIDC_ERR, "Failed to find a parent for %s\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + core = dev_get_drvdata(pdev->dev.parent); + if (!core) { + dprintk(VIDC_ERR, "Failed to find cookie in parent device %s", + dev_name(pdev->dev.parent)); + return -EINVAL; + } + + if (of_property_read_bool(pdev->dev.of_node, "qcom,fw-context-bank")) { + if (core->resources.use_non_secure_pil) { + struct context_bank_info *cb; + + cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL); + if (!cb) { + dprintk(VIDC_ERR, "alloc venus cb failed\n"); + return -ENOMEM; + } + + cb->dev = &pdev->dev; + rc = venus_boot_init(&core->resources, cb); + if (rc) { + dprintk(VIDC_ERR, + "Failed to init non-secure PIL %d\n", rc); + } + } + } else { + rc = msm_vidc_populate_context_bank(&pdev->dev, core); + if (rc) + dprintk(VIDC_ERR, "Failed to probe context bank\n"); + else + dprintk(VIDC_DBG, "Successfully probed context bank\n"); + } + return rc; +} + +int read_bus_resources_from_dt(struct platform_device *pdev) +{ + struct msm_vidc_core *core; + + if (!pdev) { + dprintk(VIDC_ERR, "Invalid platform device\n"); + return -EINVAL; + } else if (!pdev->dev.parent) { + dprintk(VIDC_ERR, "Failed to find a parent for %s\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + core = dev_get_drvdata(pdev->dev.parent); + if (!core) { + dprintk(VIDC_ERR, "Failed to find cookie in parent device %s", + dev_name(pdev->dev.parent)); + return -EINVAL; + } + + return msm_vidc_populate_bus(&pdev->dev, &core->resources); +} diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.h new file mode 100644 index 000000000000..1358f8130ede --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_res_parse.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DT_PARSE +#define DT_PARSE +#include +#include "msm_vidc_resources.h" +void msm_vidc_free_platform_resources( + struct msm_vidc_platform_resources *res); + +int read_hfi_type(struct platform_device *pdev); + +int read_platform_resources_from_dt( + struct msm_vidc_platform_resources *res); + +int read_context_bank_resources_from_dt(struct platform_device *pdev); + +int read_bus_resources_from_dt(struct platform_device *pdev); + +int msm_vidc_load_u32_table(struct platform_device *pdev, + struct device_node *of_node, char *table_name, int struct_size, + u32 **table, u32 *num_elements); + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/msm_vidc_resources.h b/drivers/media/platform/msm/vidc_3x/msm_vidc_resources.h new file mode 100644 index 000000000000..11ffeee70789 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/msm_vidc_resources.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_VIDC_RESOURCES_H__ +#define __MSM_VIDC_RESOURCES_H__ + +#include +#include +#define MAX_BUFFER_TYPES 32 + +struct version_table { + u32 version_mask; + u32 version_shift; +}; + +struct load_freq_table { + u32 load; + u32 freq; + u32 supported_codecs; +}; + +struct dcvs_table { + u32 load; + u32 load_low; + u32 load_high; + u32 supported_codecs; +}; + +struct dcvs_limit { + u32 min_mbpf; + u32 fps; +}; + +struct imem_ab_table { + u32 core_freq; + u32 imem_ab; +}; + +struct reg_value_pair { + u32 reg; + u32 value; +}; + +struct reg_set { + struct reg_value_pair *reg_tbl; + int count; +}; + +struct addr_range { + u32 start; + u32 size; +}; + +struct addr_set { + struct addr_range *addr_tbl; + int count; +}; + +struct context_bank_info { + struct list_head list; + const char *name; + u32 buffer_type; + bool is_secure; + struct addr_range addr_range; + struct device *dev; + struct iommu_domain *domain; +}; + +struct buffer_usage_table { + u32 buffer_type; + u32 tz_usage; +}; + +struct buffer_usage_set { + struct buffer_usage_table *buffer_usage_tbl; + u32 count; +}; + +struct regulator_info { + struct regulator *regulator; + bool has_hw_power_collapse; + char *name; +}; + +struct regulator_set { + struct regulator_info *regulator_tbl; + u32 count; +}; + +struct clock_info { + const char *name; + struct clk *clk; + struct load_freq_table *load_freq_tbl; + u32 count; + bool has_scaling; +}; + +struct clock_set { + struct clock_info *clock_tbl; + u32 count; +}; + +struct bus_info { + char *name; + int master; + int slave; + unsigned int range[2]; + struct device *dev; + struct msm_bus_client_handle *client; + bool is_prfm_gov_used; + const char *mode; +}; + +struct bus_set { + struct bus_info *bus_tbl; + u32 count; +}; + +enum imem_type { + IMEM_NONE, + IMEM_OCMEM, + IMEM_VMEM, + IMEM_MAX, +}; + +struct allowed_clock_rates_table { + u32 clock_rate; +}; + +struct clock_profile_entry { + u32 codec_mask; + u32 cycles; + u32 low_power_factor; +}; + +struct clock_freq_table { + struct clock_profile_entry *clk_prof_entries; + u32 count; +}; + +struct msm_vidc_platform_resources { + phys_addr_t firmware_base; + phys_addr_t register_base; + uint32_t register_size; + uint32_t irq; + struct version_table *pf_ver_tbl; + struct version_table *pf_cap_tbl; + struct version_table *pf_speedbin_tbl; + struct allowed_clock_rates_table *allowed_clks_tbl; + u32 allowed_clks_tbl_size; + struct clock_freq_table clock_freq_tbl; + struct load_freq_table *load_freq_tbl; + uint32_t load_freq_tbl_size; + struct dcvs_table *dcvs_tbl; + uint32_t dcvs_tbl_size; + struct dcvs_limit *dcvs_limit; + struct imem_ab_table *imem_ab_tbl; + u32 imem_ab_tbl_size; + struct reg_set reg_set; + struct addr_set qdss_addr_set; + struct buffer_usage_set buffer_usage_set; + uint32_t imem_size; + enum imem_type imem_type; + uint32_t max_load; + struct platform_device *pdev; + struct regulator_set regulator_set; + struct clock_set clock_set; + struct bus_set bus_set; + bool use_non_secure_pil; + bool sw_power_collapsible; + bool sys_idle_indicator; + bool slave_side_cp; + struct list_head context_banks; + bool thermal_mitigable; + const char *fw_name; + const char *hfi_version; + bool never_unload_fw; + uint32_t pm_qos_latency_us; + uint32_t max_inst_count; + uint32_t max_secure_inst_count; +}; + +static inline bool is_iommu_present(struct msm_vidc_platform_resources *res) +{ + return !list_empty(&res->context_banks); +} + +extern uint32_t msm_vidc_pwr_collapse_delay; + +#endif + diff --git a/drivers/media/platform/msm/vidc_3x/venus_boot.c b/drivers/media/platform/msm/vidc_3x/venus_boot.c new file mode 100644 index 000000000000..d06b78c5d073 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/venus_boot.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2016, 2018, 2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define VIDC_DBG_LABEL "venus_boot" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vidc_debug.h" +#include "vidc_hfi_io.h" +#include "venus_boot.h" + +/* VENUS WRAPPER registers */ +#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v1 \ + (VIDC_WRAPPER_BASE_OFFS + 0x1018) +#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v1 \ + (VIDC_WRAPPER_BASE_OFFS + 0x101C) +#define VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v1 \ + (VIDC_WRAPPER_BASE_OFFS + 0x1020) +#define VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v1 \ + (VIDC_WRAPPER_BASE_OFFS + 0x1024) + +#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v2 \ + (VIDC_WRAPPER_BASE_OFFS + 0x1020) +#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v2 \ + (VIDC_WRAPPER_BASE_OFFS + 0x1024) +#define VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v2 \ + (VIDC_WRAPPER_BASE_OFFS + 0x1028) +#define VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v2 \ + (VIDC_WRAPPER_BASE_OFFS + 0x102C) + +#define VENUS_WRAPPER_SW_RESET (VIDC_WRAPPER_BASE_OFFS + 0x3000) + +/* VENUS VBIF registers */ +#define VENUS_VBIF_CLKON_FORCE_ON BIT(0) + +#define VENUS_VBIF_ADDR_TRANS_EN (VIDC_VBIF_BASE_OFFS + 0x1000) +#define VENUS_VBIF_AT_OLD_BASE (VIDC_VBIF_BASE_OFFS + 0x1004) +#define VENUS_VBIF_AT_OLD_HIGH (VIDC_VBIF_BASE_OFFS + 0x1008) +#define VENUS_VBIF_AT_NEW_BASE (VIDC_VBIF_BASE_OFFS + 0x1010) +#define VENUS_VBIF_AT_NEW_HIGH (VIDC_VBIF_BASE_OFFS + 0x1018) + + +/* Poll interval in uS */ +#define POLL_INTERVAL_US 50 + +#define VENUS_REGION_SIZE 0x00500000 + +static struct { + struct msm_vidc_platform_resources *resources; + struct regulator *gdsc; + const char *reg_name; + void __iomem *reg_base; + struct device *iommu_ctx_bank_dev; + struct iommu_domain *domain; + dma_addr_t fw_iova; + bool is_booted; + bool hw_ver_checked; + u32 fw_sz; + u32 hw_ver_major; + u32 hw_ver_minor; + void *venus_notif_hdle; +} *venus_data = NULL; + +/* Get venus clocks and set rates for rate-settable clocks */ +static int venus_clock_setup(void) +{ + int i, rc = 0; + unsigned long rate; + struct msm_vidc_platform_resources *res = venus_data->resources; + struct clock_info *cl; + + for (i = 0; i < res->clock_set.count; i++) { + cl = &res->clock_set.clock_tbl[i]; + /* Make sure rate-settable clocks' rates are set */ + if (!clk_get_rate(cl->clk) && cl->count) { + rate = clk_round_rate(cl->clk, 0); + rc = clk_set_rate(cl->clk, rate); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set clock rate %lu %s: %d\n", + rate, cl->name, rc); + break; + } + } + } + + return rc; +} + +static int venus_clock_prepare_enable(void) +{ + int i, rc = 0; + struct msm_vidc_platform_resources *res = venus_data->resources; + struct clock_info *cl; + + for (i = 0; i < res->clock_set.count; i++) { + cl = &res->clock_set.clock_tbl[i]; + rc = clk_prepare_enable(cl->clk); + if (rc) { + dprintk(VIDC_ERR, "failed to enable %s\n", cl->name); + for (i--; i >= 0; i--) { + cl = &res->clock_set.clock_tbl[i]; + clk_disable_unprepare(cl->clk); + } + return rc; + } + } + + return rc; +} + +static void venus_clock_disable_unprepare(void) +{ + int i; + struct msm_vidc_platform_resources *res = venus_data->resources; + struct clock_info *cl; + + for (i = 0; i < res->clock_set.count; i++) { + cl = &res->clock_set.clock_tbl[i]; + clk_disable_unprepare(cl->clk); + } +} + +static int venus_setup_cb(struct device *dev, + u32 size) +{ + venus_data->domain = iommu_get_domain_for_dev(dev); + if (IS_ERR_OR_NULL(venus_data->domain)) { + dprintk(VIDC_ERR, "%s: failed to create mapping for %s\n", + __func__, dev_name(dev)); + return -ENODEV; + } + dprintk(VIDC_DBG, + "%s Attached device %pK and created domain %pK for %s\n", + __func__, dev, venus_data->domain, dev_name(dev)); + return 0; +} + +static int pil_venus_mem_setup(size_t size) +{ + int rc = 0; + + if (!venus_data->domain) { + size = round_up(size, SZ_4K); + rc = venus_setup_cb(venus_data->iommu_ctx_bank_dev, size); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to setup context bank for venus : %s\n", + __func__, + dev_name(venus_data->iommu_ctx_bank_dev)); + return rc; + } + venus_data->fw_sz = size; + } + + return rc; +} + +static int pil_venus_auth_and_reset(void) +{ + int rc; + + phys_addr_t fw_bias = venus_data->resources->firmware_base; + void __iomem *reg_base = venus_data->reg_base; + u32 ver; + bool iommu_present = is_iommu_present(venus_data->resources); + struct device *dev = venus_data->iommu_ctx_bank_dev; + + if (!fw_bias) { + dprintk(VIDC_ERR, "FW bias is not valid\n"); + return -EINVAL; + } + venus_data->fw_iova = (dma_addr_t)NULL; + /* Get Venus version number */ + if (!venus_data->hw_ver_checked) { + ver = readl_relaxed(reg_base + VIDC_WRAPPER_HW_VERSION); + venus_data->hw_ver_minor = (ver & 0x0FFF0000) >> 16; + venus_data->hw_ver_major = (ver & 0xF0000000) >> 28; + venus_data->hw_ver_checked = true; + } + + if (iommu_present) { + u32 cpa_start_addr, cpa_end_addr, fw_start_addr, fw_end_addr; + /* Get the cpa and fw start/end addr based on Venus version */ + if (venus_data->hw_ver_major == 0x1 && + venus_data->hw_ver_minor <= 1) { + cpa_start_addr = + VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v1; + cpa_end_addr = + VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v1; + fw_start_addr = + VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v1; + fw_end_addr = + VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v1; + } else { + cpa_start_addr = + VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v2; + cpa_end_addr = + VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v2; + fw_start_addr = + VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v2; + fw_end_addr = + VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v2; + } + + /* Program CPA start and end address */ + writel_relaxed(0, reg_base + cpa_start_addr); + writel_relaxed(venus_data->fw_sz, reg_base + cpa_end_addr); + + /* Program FW start and end address */ + writel_relaxed(0, reg_base + fw_start_addr); + writel_relaxed(venus_data->fw_sz, reg_base + fw_end_addr); + } else { + rc = regulator_enable(venus_data->gdsc); + if (rc) { + dprintk(VIDC_ERR, "GDSC enable failed\n"); + goto err; + } + + rc = venus_clock_prepare_enable(); + if (rc) { + dprintk(VIDC_ERR, "Clock prepare and enable failed\n"); + regulator_disable(venus_data->gdsc); + goto err; + } + + writel_relaxed(0, reg_base + VENUS_VBIF_AT_OLD_BASE); + writel_relaxed(VENUS_REGION_SIZE, + reg_base + VENUS_VBIF_AT_OLD_HIGH); + writel_relaxed(fw_bias, reg_base + VENUS_VBIF_AT_NEW_BASE); + writel_relaxed(fw_bias + VENUS_REGION_SIZE, + reg_base + VENUS_VBIF_AT_NEW_HIGH); + writel_relaxed(0x7F007F, reg_base + VENUS_VBIF_ADDR_TRANS_EN); + venus_clock_disable_unprepare(); + regulator_disable(venus_data->gdsc); + } + /* Make sure all register writes are committed. */ + mb(); + + /* + * Need to wait 10 cycles of internal clocks before bringing ARM9 + * out of reset. + */ + udelay(1); + + if (iommu_present) { + phys_addr_t pa = fw_bias; + + dprintk(VIDC_DBG, "Attached and created mapping for %s\n", + dev_name(dev)); + + /* Map virtual addr space 0 - fw_sz to fw phys addr space */ + rc = iommu_map(venus_data->domain, + venus_data->fw_iova, pa, venus_data->fw_sz, + IOMMU_READ|IOMMU_WRITE|IOMMU_PRIV); + if (!rc) { + dprintk(VIDC_DBG, + "%s - Successfully mapped and performed test translation!\n", + dev_name(dev)); + } + + if (rc || (venus_data->fw_iova != 0)) { + dprintk(VIDC_ERR, "%s - Failed to setup IOMMU\n", + dev_name(dev)); + goto err; + } + } + /* Bring Arm9 out of reset */ + writel_relaxed(0, reg_base + VENUS_WRAPPER_SW_RESET); + + venus_data->is_booted = true; + return 0; + +err: + return rc; +} + +static int pil_venus_shutdown(void) +{ + void __iomem *reg_base = venus_data->reg_base; + u32 reg; + int rc; + + if (!venus_data->is_booted) + return 0; + + /* Assert the reset to ARM9 */ + reg = readl_relaxed(reg_base + VENUS_WRAPPER_SW_RESET); + reg |= BIT(4); + writel_relaxed(reg, reg_base + VENUS_WRAPPER_SW_RESET); + + /* Make sure reset is asserted before the mapping is removed */ + mb(); + + if (is_iommu_present(venus_data->resources)) { + iommu_unmap(venus_data->domain, venus_data->fw_iova, + venus_data->fw_sz); + } + /* + * Force the VBIF clk to be on to avoid AXI bridge halt ack failure + * for certain Venus version. + */ + if (venus_data->hw_ver_major == 0x1 && + (venus_data->hw_ver_minor == 0x2 || + venus_data->hw_ver_minor == 0x3)) { + reg = readl_relaxed(reg_base + VIDC_VENUS_VBIF_CLK_ON); + reg |= VENUS_VBIF_CLKON_FORCE_ON; + writel_relaxed(reg, reg_base + VIDC_VENUS_VBIF_CLK_ON); + } + + /* Halt AXI and AXI OCMEM VBIF Access */ + reg = readl_relaxed(reg_base + VENUS_VBIF_AXI_HALT_CTRL0); + reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ; + writel_relaxed(reg, reg_base + VENUS_VBIF_AXI_HALT_CTRL0); + + /* Request for AXI bus port halt */ + rc = readl_poll_timeout(reg_base + VENUS_VBIF_AXI_HALT_CTRL1, + reg, reg & VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK, + POLL_INTERVAL_US, + VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (rc) + dprintk(VIDC_ERR, "Port halt timeout\n"); + + venus_data->is_booted = false; + + return 0; +} + +static int venus_notifier_cb(struct notifier_block *this, unsigned long code, + void *ss_handle) +{ + struct notif_data *data = (struct notif_data *)ss_handle; + static bool venus_data_set; + int ret; + + if (!data->no_auth) + return NOTIFY_DONE; + + if (!venus_data_set) { + ret = venus_clock_setup(); + if (ret) + return ret; + + ret = of_property_read_string(data->pdev->dev.of_node, + "qcom,proxy-reg-names", &venus_data->reg_name); + if (ret) + return ret; + + venus_data->gdsc = devm_regulator_get( + &data->pdev->dev, venus_data->reg_name); + if (IS_ERR(venus_data->gdsc)) { + dprintk(VIDC_ERR, "Failed to get Venus GDSC\n"); + return -ENODEV; + } + + venus_data_set = true; + } + + if (code != SUBSYS_AFTER_POWERUP && code != SUBSYS_AFTER_SHUTDOWN) + return NOTIFY_DONE; + + ret = regulator_enable(venus_data->gdsc); + if (ret) { + dprintk(VIDC_ERR, "GDSC enable failed\n"); + return ret; + } + + ret = venus_clock_prepare_enable(); + if (ret) { + dprintk(VIDC_ERR, "Clock prepare and enable failed\n"); + goto err_clks; + } + + if (code == SUBSYS_AFTER_POWERUP) { + if (is_iommu_present(venus_data->resources)) + pil_venus_mem_setup(VENUS_REGION_SIZE); + pil_venus_auth_and_reset(); + } else if (code == SUBSYS_AFTER_SHUTDOWN) + pil_venus_shutdown(); + + venus_clock_disable_unprepare(); + regulator_disable(venus_data->gdsc); + + return NOTIFY_DONE; +err_clks: + regulator_disable(venus_data->gdsc); + return ret; +} + +static struct notifier_block venus_notifier = { + .notifier_call = venus_notifier_cb, +}; + +int venus_boot_init(struct msm_vidc_platform_resources *res, + struct context_bank_info *cb) +{ + int rc = 0; + + if (!res || !cb) { + dprintk(VIDC_ERR, "Invalid platform resource handle\n"); + return -EINVAL; + } + venus_data = kzalloc(sizeof(*venus_data), GFP_KERNEL); + if (!venus_data) + return -ENOMEM; + + venus_data->resources = res; + venus_data->iommu_ctx_bank_dev = cb->dev; + if (!venus_data->iommu_ctx_bank_dev) { + dprintk(VIDC_ERR, "Invalid venus context bank device\n"); + return -ENODEV; + } + venus_data->reg_base = ioremap_nocache(res->register_base, + (unsigned long)res->register_size); + if (!venus_data->reg_base) { + dprintk(VIDC_ERR, + "could not map reg addr %pa of size %d\n", + &res->register_base, res->register_size); + rc = -ENOMEM; + goto err_ioremap_fail; + } + venus_data->venus_notif_hdle = subsys_notif_register_notifier("venus", + &venus_notifier); + if (IS_ERR(venus_data->venus_notif_hdle)) { + dprintk(VIDC_ERR, "register event notification failed\n"); + rc = PTR_ERR(venus_data->venus_notif_hdle); + goto err_subsys_notif; + } + + return rc; + +err_subsys_notif: +err_ioremap_fail: + kfree(venus_data); + return rc; +} + +void venus_boot_deinit(void) +{ + venus_data->resources = NULL; + subsys_notif_unregister_notifier(venus_data->venus_notif_hdle, + &venus_notifier); + kfree(venus_data); +} diff --git a/drivers/media/platform/msm/vidc_3x/venus_boot.h b/drivers/media/platform/msm/vidc_3x/venus_boot.h new file mode 100644 index 000000000000..e8f641d663d7 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/venus_boot.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014, 2019-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VENUS_BOOT_H__ +#define __VENUS_BOOT_H__ +#include "msm_vidc_resources.h" + +int venus_boot_init(struct msm_vidc_platform_resources *res, + struct context_bank_info *cb); +void venus_boot_deinit(void); + +#endif /* __VENUS_BOOT_H__ */ diff --git a/drivers/media/platform/msm/vidc_3x/venus_hfi.c b/drivers/media/platform/msm/vidc_3x/venus_hfi.c new file mode 100644 index 000000000000..12231205d528 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/venus_hfi.c @@ -0,0 +1,4663 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2016, 2018-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hfi_packetization.h" +#include "msm_vidc_debug.h" +#include "venus_hfi.h" +#include "vidc_hfi_io.h" + +#define FIRMWARE_SIZE 0X00A00000 +#define REG_ADDR_OFFSET_BITMASK 0x000FFFFF +#define QDSS_IOVA_START 0x80001000 +#define MIN_PAYLOAD_SIZE 3 + +static struct hal_device_data hal_ctxt; + +#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8 +struct tzbsp_memprot { + u32 cp_start; + u32 cp_size; + u32 cp_nonpixel_start; + u32 cp_nonpixel_size; +}; + +struct tzbsp_resp { + int ret; +}; + +#define TZBSP_VIDEO_SET_STATE 0xa + +/* Poll interval in uS */ +#define POLL_INTERVAL_US 50 + +enum tzbsp_video_state { + TZBSP_VIDEO_STATE_SUSPEND = 0, + TZBSP_VIDEO_STATE_RESUME = 1, + TZBSP_VIDEO_STATE_RESTORE_THRESHOLD = 2, +}; + +struct tzbsp_video_set_state_req { + u32 state; /* should be tzbsp_video_state enum value */ + u32 spare; /* reserved for future, should be zero */ +}; + +const struct msm_vidc_gov_data DEFAULT_BUS_VOTE = { + .data = NULL, + .data_count = 0, + .imem_size = 0, +}; + +const int max_packets = 250; + +static void venus_hfi_pm_handler(struct work_struct *work); +static DECLARE_DELAYED_WORK(venus_hfi_pm_work, venus_hfi_pm_handler); +static inline int __resume(struct venus_hfi_device *device); +static inline int __suspend(struct venus_hfi_device *device); +static int __disable_regulators(struct venus_hfi_device *device); +static int __enable_regulators(struct venus_hfi_device *device); +static inline int __prepare_enable_clks(struct venus_hfi_device *device); +static inline void __disable_unprepare_clks(struct venus_hfi_device *device); +static int __scale_clocks_load(struct venus_hfi_device *device, int load, + struct vidc_clk_scale_data *data, + unsigned long instant_bitrate); +static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet); +static int __initialize_packetization(struct venus_hfi_device *device); +static struct hal_session *__get_session(struct venus_hfi_device *device, + u32 session_id); +static int __iface_cmdq_write(struct venus_hfi_device *device, + void *pkt); +static int __load_fw(struct venus_hfi_device *device); +static void __unload_fw(struct venus_hfi_device *device); +static int __tzbsp_set_video_state(enum tzbsp_video_state state); + + +/** + * Utility function to enforce some of our assumptions. Spam calls to this + * in hotspots in code to double check some of the assumptions that we hold. + */ +static inline void __strict_check(struct venus_hfi_device *device) +{ + if (!mutex_is_locked(&device->lock)) { + dprintk(VIDC_WARN, + "device->lock mutex is not locked\n"); + WARN_ON(VIDC_DBG_WARN_ENABLE); + } +} + +static inline void __set_state(struct venus_hfi_device *device, + enum venus_hfi_state state) +{ + device->state = state; +} + +static inline bool __core_in_valid_state(struct venus_hfi_device *device) +{ + return device->state != VENUS_STATE_DEINIT; +} + +static void __dump_packet(u8 *packet) +{ + u32 c = 0, packet_size = *(u32 *)packet; + const int row_size = 32; + /* row must contain enough for 0xdeadbaad * 8 to be converted into + * "de ad ba ab " * 8 + '\0' + */ + char row[3 * row_size]; + + for (c = 0; c * row_size < packet_size; ++c) { + int bytes_to_read = ((c + 1) * row_size > packet_size) ? + packet_size % row_size : row_size; + hex_dump_to_buffer(packet + c * row_size, bytes_to_read, + row_size, 4, row, sizeof(row), false); + dprintk(VIDC_PKT, "%s\n", row); + } +} + +static void __sim_modify_cmd_packet(u8 *packet, struct venus_hfi_device *device) +{ + struct hfi_cmd_sys_session_init_packet *sys_init; + struct hal_session *session = NULL; + u8 i; + phys_addr_t fw_bias = 0; + + if (!device || !packet) { + dprintk(VIDC_ERR, "Invalid Param\n"); + return; + } else if (!device->hal_data->firmware_base + || is_iommu_present(device->res)) { + return; + } + + fw_bias = device->hal_data->firmware_base; + sys_init = (struct hfi_cmd_sys_session_init_packet *)packet; + + session = __get_session(device, sys_init->session_id); + if (!session) { + dprintk(VIDC_DBG, "%s :Invalid session id: %x\n", + __func__, sys_init->session_id); + return; + } + + switch (sys_init->packet_type) { + case HFI_CMD_SESSION_EMPTY_BUFFER: + if (session->is_decoder) { + struct hfi_cmd_session_empty_buffer_compressed_packet + *pkt = (struct + hfi_cmd_session_empty_buffer_compressed_packet + *) packet; + pkt->packet_buffer -= fw_bias; + } else { + struct + hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + *pkt = (struct + hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + *) packet; + pkt->packet_buffer -= fw_bias; + } + break; + case HFI_CMD_SESSION_FILL_BUFFER: + { + struct hfi_cmd_session_fill_buffer_packet *pkt = + (struct hfi_cmd_session_fill_buffer_packet *)packet; + pkt->packet_buffer -= fw_bias; + break; + } + case HFI_CMD_SESSION_SET_BUFFERS: + { + struct hfi_cmd_session_set_buffers_packet *pkt = + (struct hfi_cmd_session_set_buffers_packet *)packet; + if (pkt->buffer_type == HFI_BUFFER_OUTPUT || + pkt->buffer_type == HFI_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + buff->buffer_addr -= fw_bias; + if (buff->extra_data_addr >= fw_bias) + buff->extra_data_addr -= fw_bias; + } else { + for (i = 0; i < pkt->num_buffers; i++) + pkt->rg_buffer_info[i] -= fw_bias; + } + break; + } + case HFI_CMD_SESSION_RELEASE_BUFFERS: + { + struct hfi_cmd_session_release_buffer_packet *pkt = + (struct hfi_cmd_session_release_buffer_packet *)packet; + if (pkt->buffer_type == HFI_BUFFER_OUTPUT || + pkt->buffer_type == HFI_BUFFER_OUTPUT2) { + struct hfi_buffer_info *buff; + + buff = (struct hfi_buffer_info *) pkt->rg_buffer_info; + buff->buffer_addr -= fw_bias; + buff->extra_data_addr -= fw_bias; + } else { + for (i = 0; i < pkt->num_buffers; i++) + pkt->rg_buffer_info[i] -= fw_bias; + } + break; + } + case HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER: + { + struct hfi_cmd_session_parse_sequence_header_packet *pkt = + (struct hfi_cmd_session_parse_sequence_header_packet *) + packet; + pkt->packet_buffer -= fw_bias; + break; + } + case HFI_CMD_SESSION_GET_SEQUENCE_HEADER: + { + struct hfi_cmd_session_get_sequence_header_packet *pkt = + (struct hfi_cmd_session_get_sequence_header_packet *) + packet; + pkt->packet_buffer -= fw_bias; + break; + } + default: + break; + } +} + +static int __acquire_regulator(struct regulator_info *rinfo) +{ + int rc = 0; + + if (rinfo->has_hw_power_collapse) { + rc = regulator_set_mode(rinfo->regulator, + REGULATOR_MODE_NORMAL); + if (rc) { + /* + * This is somewhat fatal, but nothing we can do + * about it. We can't disable the regulator w/o + * getting it back under s/w control + */ + dprintk(VIDC_WARN, + "Failed to acquire regulator control: %s\n", + rinfo->name); + } else { + + dprintk(VIDC_DBG, + "Acquire regulator control from HW: %s\n", + rinfo->name); + + } + } + + if (!regulator_is_enabled(rinfo->regulator)) { + dprintk(VIDC_WARN, "Regulator is not enabled %s\n", + rinfo->name); + WARN_ON(VIDC_DBG_WARN_ENABLE); + } + + return rc; +} + +static int __hand_off_regulator(struct regulator_info *rinfo) +{ + int rc = 0; + + if (rinfo->has_hw_power_collapse) { + rc = regulator_set_mode(rinfo->regulator, + REGULATOR_MODE_FAST); + if (rc) { + dprintk(VIDC_WARN, + "Failed to hand off regulator control: %s\n", + rinfo->name); + } else { + dprintk(VIDC_DBG, + "Hand off regulator control to HW: %s\n", + rinfo->name); + } + } + + return rc; +} + +static int __hand_off_regulators(struct venus_hfi_device *device) +{ + struct regulator_info *rinfo; + int rc = 0, c = 0; + + venus_hfi_for_each_regulator(device, rinfo) { + rc = __hand_off_regulator(rinfo); + /* + * If one regulator hand off failed, driver should take + * the control for other regulators back. + */ + if (rc) + goto err_reg_handoff_failed; + c++; + } + + return rc; +err_reg_handoff_failed: + venus_hfi_for_each_regulator_reverse_continue(device, rinfo, c) + __acquire_regulator(rinfo); + + return rc; +} + +static int __write_queue(struct vidc_iface_q_info *qinfo, u8 *packet, + bool *rx_req_is_set) +{ + struct hfi_queue_header *queue; + u32 packet_size_in_words, new_write_idx; + u32 empty_space, read_idx; + u32 *write_ptr; + + if (!qinfo || !packet) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } else if (!qinfo->q_array.align_virtual_addr) { + dprintk(VIDC_WARN, "Queues have already been freed\n"); + return -EINVAL; + } + + queue = (struct hfi_queue_header *) qinfo->q_hdr; + if (!queue) { + dprintk(VIDC_ERR, "queue not present\n"); + return -ENOENT; + } + + if (msm_vidc_debug & VIDC_PKT) { + dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo); + __dump_packet(packet); + } + + packet_size_in_words = (*(u32 *)packet) >> 2; + if (!packet_size_in_words) { + dprintk(VIDC_ERR, "Zero packet size\n"); + return -ENODATA; + } + + read_idx = queue->qhdr_read_idx; + + empty_space = (queue->qhdr_write_idx >= read_idx) ? + (queue->qhdr_q_size - (queue->qhdr_write_idx - read_idx)) : + (read_idx - queue->qhdr_write_idx); + if (empty_space <= packet_size_in_words) { + queue->qhdr_tx_req = 1; + dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n", + empty_space, packet_size_in_words); + return -ENOTEMPTY; + } + + queue->qhdr_tx_req = 0; + + new_write_idx = (queue->qhdr_write_idx + packet_size_in_words); + write_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) + + (queue->qhdr_write_idx << 2)); + if (new_write_idx < queue->qhdr_q_size) { + memcpy(write_ptr, packet, packet_size_in_words << 2); + } else { + new_write_idx -= queue->qhdr_q_size; + memcpy(write_ptr, packet, (packet_size_in_words - + new_write_idx) << 2); + memcpy((void *)qinfo->q_array.align_virtual_addr, + packet + ((packet_size_in_words - new_write_idx) << 2), + new_write_idx << 2); + } + + /* Memory barrier to make sure packet is written before updating the + * write index + */ + mb(); + queue->qhdr_write_idx = new_write_idx; + if (rx_req_is_set) + *rx_req_is_set = queue->qhdr_rx_req == 1; + /* Memory barrier to make sure write index is updated before an + * interrupt is raised on venus. + */ + mb(); + return 0; +} + +static void __hal_sim_modify_msg_packet(u8 *packet, + struct venus_hfi_device *device) +{ + struct hfi_msg_sys_session_init_done_packet *sys_idle; + struct hal_session *session = NULL; + phys_addr_t fw_bias = 0; + + if (!device || !packet) { + dprintk(VIDC_ERR, "Invalid Param\n"); + return; + } else if (!device->hal_data->firmware_base + || is_iommu_present(device->res)) { + return; + } + + fw_bias = device->hal_data->firmware_base; + sys_idle = (struct hfi_msg_sys_session_init_done_packet *)packet; + session = __get_session(device, sys_idle->session_id); + + if (!session) { + dprintk(VIDC_DBG, "%s: Invalid session id: %x\n", + __func__, sys_idle->session_id); + return; + } + + switch (sys_idle->packet_type) { + case HFI_MSG_SESSION_FILL_BUFFER_DONE: + if (session->is_decoder) { + struct + hfi_msg_session_fbd_uncompressed_plane0_packet + *pkt_uc = (struct + hfi_msg_session_fbd_uncompressed_plane0_packet + *) packet; + pkt_uc->packet_buffer += fw_bias; + } else { + struct + hfi_msg_session_fill_buffer_done_compressed_packet + *pkt = (struct + hfi_msg_session_fill_buffer_done_compressed_packet + *) packet; + pkt->packet_buffer += fw_bias; + } + break; + case HFI_MSG_SESSION_EMPTY_BUFFER_DONE: + { + struct hfi_msg_session_empty_buffer_done_packet *pkt = + (struct hfi_msg_session_empty_buffer_done_packet *)packet; + pkt->packet_buffer += fw_bias; + break; + } + case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE: + { + struct + hfi_msg_session_get_sequence_header_done_packet + *pkt = + (struct hfi_msg_session_get_sequence_header_done_packet *) + packet; + pkt->sequence_header += fw_bias; + break; + } + default: + break; + } +} + +static int __read_queue(struct vidc_iface_q_info *qinfo, u8 *packet, + u32 *pb_tx_req_is_set) +{ + struct hfi_queue_header *queue; + u32 packet_size_in_words, new_read_idx; + u32 *read_ptr; + u32 receive_request = 0; + int rc = 0; + + if (!qinfo || !packet || !pb_tx_req_is_set) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } else if (!qinfo->q_array.align_virtual_addr) { + dprintk(VIDC_WARN, "Queues have already been freed\n"); + return -EINVAL; + } + + /*Memory barrier to make sure data is valid before + *reading it + */ + mb(); + queue = (struct hfi_queue_header *) qinfo->q_hdr; + + if (!queue) { + dprintk(VIDC_ERR, "Queue memory is not allocated\n"); + return -ENOMEM; + } + + /* + * Do not set receive request for debug queue, if set, + * Venus generates interrupt for debug messages even + * when there is no response message available. + * In general debug queue will not become full as it + * is being emptied out for every interrupt from Venus. + * Venus will anyway generates interrupt if it is full. + */ + if (queue->qhdr_type & HFI_Q_ID_CTRL_TO_HOST_MSG_Q) + receive_request = 1; + + if (queue->qhdr_read_idx == queue->qhdr_write_idx) { + queue->qhdr_rx_req = receive_request; + *pb_tx_req_is_set = 0; + dprintk(VIDC_DBG, + "%s queue is empty, rx_req = %u, tx_req = %u, read_idx = %u\n", + receive_request ? "message" : "debug", + queue->qhdr_rx_req, queue->qhdr_tx_req, + queue->qhdr_read_idx); + return -ENODATA; + } + + read_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) + + (queue->qhdr_read_idx << 2)); + packet_size_in_words = (*read_ptr) >> 2; + if (!packet_size_in_words) { + dprintk(VIDC_ERR, "Zero packet size\n"); + return -ENODATA; + } + + new_read_idx = queue->qhdr_read_idx + packet_size_in_words; + if (((packet_size_in_words << 2) <= VIDC_IFACEQ_VAR_HUGE_PKT_SIZE) + && queue->qhdr_read_idx <= queue->qhdr_q_size) { + if (new_read_idx < queue->qhdr_q_size) { + memcpy(packet, read_ptr, + packet_size_in_words << 2); + } else { + new_read_idx -= queue->qhdr_q_size; + memcpy(packet, read_ptr, + (packet_size_in_words - new_read_idx) << 2); + memcpy(packet + ((packet_size_in_words - + new_read_idx) << 2), + (u8 *)qinfo->q_array.align_virtual_addr, + new_read_idx << 2); + } + } else { + dprintk(VIDC_WARN, + "BAD packet received, read_idx: %#x, pkt_size: %d\n", + queue->qhdr_read_idx, packet_size_in_words << 2); + dprintk(VIDC_WARN, "Dropping this packet\n"); + new_read_idx = queue->qhdr_write_idx; + rc = -ENODATA; + } + + queue->qhdr_read_idx = new_read_idx; + + if (queue->qhdr_read_idx != queue->qhdr_write_idx) + queue->qhdr_rx_req = 0; + else + queue->qhdr_rx_req = receive_request; + + *pb_tx_req_is_set = (queue->qhdr_tx_req == 1) ? 1 : 0; + + if (msm_vidc_debug & VIDC_PKT) { + dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo); + __dump_packet(packet); + } + + return rc; +} + +static int __smem_alloc(struct venus_hfi_device *dev, + struct vidc_mem_addr *mem, u32 size, u32 align, + u32 flags, u32 usage) +{ + struct msm_smem *alloc = &mem->mem_data; + int rc = 0; + + if (!dev || !mem || !size) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + dprintk(VIDC_INFO, "start to alloc size: %d, flags: %d\n", size, flags); + rc = msm_smem_alloc(size, align, flags, usage, 1, (void *)dev->res, + MSM_VIDC_UNKNOWN, alloc); + if (rc) { + dprintk(VIDC_ERR, "Alloc failed\n"); + rc = -ENOMEM; + goto fail_smem_alloc; + } + + dprintk(VIDC_DBG, "%s: ptr = %pK, size = %d\n", + __func__, + alloc->kvaddr, size); + rc = msm_smem_cache_operations(alloc->dma_buf, 0, alloc->size, + SMEM_CACHE_CLEAN); + if (rc) { + dprintk(VIDC_WARN, "Failed to clean cache\n"); + dprintk(VIDC_WARN, "This may result in undefined behavior\n"); + } + + mem->mem_size = alloc->size; + mem->align_virtual_addr = alloc->kvaddr; + mem->align_device_addr = alloc->device_addr; + return rc; +fail_smem_alloc: + return rc; +} + +static void __smem_free(struct venus_hfi_device *dev, struct msm_smem *mem) +{ + if (!dev || !mem) { + dprintk(VIDC_ERR, "invalid param %pK %pK\n", dev, mem); + return; + } + + msm_smem_free(mem); +} + +static void __write_register(struct venus_hfi_device *device, + u32 reg, u32 value) +{ + u32 hwiosymaddr = reg; + u8 *base_addr; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return; + } + + __strict_check(device); + + if (!device->power_enabled) { + dprintk(VIDC_WARN, + "HFI Write register failed : Power is OFF\n"); + WARN_ON(VIDC_DBG_WARN_ENABLE); + return; + } + + base_addr = device->hal_data->register_base; + dprintk(VIDC_DBG, "Base addr: %pK, written to: %#x, Value: %#x...\n", + base_addr, hwiosymaddr, value); + base_addr += hwiosymaddr; + writel_relaxed(value, base_addr); + /* + * Memory barrier to make sure value is written into the register. + */ + wmb(); +} + +static int __read_register(struct venus_hfi_device *device, u32 reg) +{ + int rc = 0; + u8 *base_addr; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } + + __strict_check(device); + + if (!device->power_enabled) { + dprintk(VIDC_WARN, + "HFI Read register failed : Power is OFF\n"); + WARN_ON(VIDC_DBG_WARN_ENABLE); + return -EINVAL; + } + + base_addr = device->hal_data->register_base; + + rc = readl_relaxed(base_addr + reg); + /* + * Memory barrier to make sure value is read correctly from the + * register. + */ + rmb(); + dprintk(VIDC_DBG, "Base addr: %pK, read from: %#x, value: %#x...\n", + base_addr, reg, rc); + + return rc; +} + +static void __set_registers(struct venus_hfi_device *device) +{ + struct reg_set *reg_set; + int i; + + if (!device->res) { + dprintk(VIDC_ERR, + "device resources null, cannot set registers\n"); + return; + } + + reg_set = &device->res->reg_set; + for (i = 0; i < reg_set->count; i++) { + __write_register(device, reg_set->reg_tbl[i].reg, + reg_set->reg_tbl[i].value); + } +} + +/* + * The existence of this function is a hack for 8996 (or certain Venus versions) + * to overcome a hardware bug. Whenever the GDSCs momentarily power collapse + * (after calling __hand_off_regulators()), the values of the threshold + * registers (typically programmed by TZ) are incorrectly reset. As a result + * reprogram these registers at certain agreed upon points. + */ +static void __set_threshold_registers(struct venus_hfi_device *device) +{ + u32 version = __read_register(device, VIDC_WRAPPER_HW_VERSION); + + version &= ~GENMASK(15, 0); + if (version != (0x3 << 28 | 0x43 << 16)) + return; + + if (__tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESTORE_THRESHOLD)) + dprintk(VIDC_ERR, "Failed to restore threshold values\n"); +} + +static bool __is_session_supported(unsigned long sessions_supported, + enum vidc_vote_data_session session_type) +{ + bool same_codec, same_session_type; + int codec_bit, session_type_bit; + unsigned long session = session_type; + + if (!sessions_supported || !session) + return false; + + /* ffs returns a 1 indexed, test_bit takes a 0 indexed...index */ + codec_bit = ffs(session) - 1; + session_type_bit = codec_bit + 1; + + same_codec = test_bit(codec_bit, &sessions_supported) == + test_bit(codec_bit, &session); + same_session_type = test_bit(session_type_bit, &sessions_supported) == + test_bit(session_type_bit, &session); + + return same_codec && same_session_type; +} + +bool venus_hfi_is_session_supported(unsigned long sessions_supported, + enum vidc_vote_data_session session_type) +{ + return __is_session_supported(sessions_supported, session_type); +} + +static int __vote_bandwidth(struct bus_info *bus, + unsigned long *freq) +{ + int rc = 0; + uint64_t ab = 0; + + if (*freq) + *freq = clamp_t(typeof(*freq), *freq, bus->range[0], + bus->range[1]); + + /* Bus Driver expects values in Bps */ + ab = *freq * 1000; + dprintk(VIDC_PROF, "Voting bus %s to ab %llu\n", bus->name, ab); + rc = msm_bus_scale_update_bw(bus->client, ab, 0); + if (rc) + dprintk(VIDC_ERR, "Failed voting bus %s to ab %llu, rc=%d\n", + bus->name, ab, rc); + + return rc; +} + +static int __unvote_buses(struct venus_hfi_device *device) +{ + int rc = 0; + struct bus_info *bus = NULL; + unsigned long freq = 0, zero = 0; + + venus_hfi_for_each_bus(device, bus) { + if (!bus->is_prfm_gov_used) { + freq = __calc_bw(bus, &device->bus_vote); + rc = __vote_bandwidth(bus, &freq); + } else + rc = __vote_bandwidth(bus, &zero); + + if (rc) + goto err_unknown_device; + } + + if (rc) + dprintk(VIDC_WARN, "Failed to unvote some buses\n"); + +err_unknown_device: + return rc; +} + +static int __vote_buses(struct venus_hfi_device *device, + struct vidc_bus_vote_data *data, int num_data) +{ + int rc = 0; + struct bus_info *bus = NULL; + struct vidc_bus_vote_data *new_data = NULL; + unsigned long freq = 0, zero = 0; + + if (!num_data) { + dprintk(VIDC_DBG, "No vote data available\n"); + goto no_data_count; + } else if (!data) { + dprintk(VIDC_ERR, "Invalid voting data\n"); + return -EINVAL; + } + + new_data = kmemdup(data, num_data * sizeof(*new_data), GFP_KERNEL); + if (!new_data) { + dprintk(VIDC_ERR, "Can't alloc memory to cache bus votes\n"); + rc = -ENOMEM; + goto err_no_mem; + } + +no_data_count: + kfree(device->bus_vote.data); + device->bus_vote.data = new_data; + device->bus_vote.data_count = num_data; + device->bus_vote.imem_size = device->res->imem_size; + + venus_hfi_for_each_bus(device, bus) { + if (!bus->is_prfm_gov_used) { + freq = __calc_bw(bus, &device->bus_vote); + rc = __vote_bandwidth(bus, &freq); + } else + rc = __vote_bandwidth(bus, &zero); + + if (rc) + return rc; + } + +err_no_mem: + return rc; +} + +static int venus_hfi_vote_buses(void *dev, struct vidc_bus_vote_data *d, int n) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + + if (!device) + return -EINVAL; + + mutex_lock(&device->lock); + rc = __vote_buses(device, d, n); + mutex_unlock(&device->lock); + + return rc; + +} +static int __core_set_resource(struct venus_hfi_device *device, + struct vidc_resource_hdr *resource_hdr, void *resource_value) +{ + struct hfi_cmd_sys_set_resource_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + + if (!device || !resource_hdr || !resource_value) { + dprintk(VIDC_ERR, "set_res: Invalid Params\n"); + return -EINVAL; + } + + pkt = (struct hfi_cmd_sys_set_resource_packet *) packet; + + rc = call_hfi_pkt_op(device, sys_set_resource, + pkt, resource_hdr, resource_value); + if (rc) { + dprintk(VIDC_ERR, "set_res: failed to create packet\n"); + goto err_create_pkt; + } + + rc = __iface_cmdq_write(device, pkt); + if (rc) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static DECLARE_COMPLETION(release_resources_done); + +static int __alloc_imem(struct venus_hfi_device *device, unsigned long size) +{ + struct imem *imem = NULL; + int rc = 0; + + if (!device) + return -EINVAL; + + imem = &device->resources.imem; + if (imem->type) { + dprintk(VIDC_ERR, "IMEM of type %d already allocated\n", + imem->type); + return -ENOMEM; + } + + switch (device->res->imem_type) { + case IMEM_VMEM: + { + phys_addr_t vmem_buffer = 0; + + rc = vmem_allocate(size, &vmem_buffer); + if (rc) { + if (rc == -ENOTSUPP) { + dprintk(VIDC_DBG, + "Target does not support vmem\n"); + rc = 0; + } + goto imem_alloc_failed; + } else if (!vmem_buffer) { + rc = -ENOMEM; + goto imem_alloc_failed; + } + + imem->vmem = vmem_buffer; + break; + } + case IMEM_NONE: + rc = 0; + break; + + default: + rc = -ENOTSUPP; + goto imem_alloc_failed; + } + + imem->type = device->res->imem_type; + dprintk(VIDC_DBG, "Allocated %ld bytes of IMEM of type %d\n", size, + imem->type); + return 0; +imem_alloc_failed: + imem->type = IMEM_NONE; + return rc; +} + +static int __free_imem(struct venus_hfi_device *device) +{ + struct imem *imem = NULL; + int rc = 0; + + if (!device) + return -EINVAL; + + imem = &device->resources.imem; + switch (imem->type) { + case IMEM_NONE: + /* Follow the semantics of free(NULL), which is a no-op. */ + break; + case IMEM_VMEM: + vmem_free(imem->vmem); + break; + default: + rc = -ENOTSUPP; + goto imem_free_failed; + } + + imem->type = IMEM_NONE; + return 0; + +imem_free_failed: + return rc; +} + +static int __set_imem(struct venus_hfi_device *device, struct imem *imem) +{ + struct vidc_resource_hdr rhdr; + phys_addr_t addr = 0; + int rc = 0; + + if (!device || !device->res || !imem) { + dprintk(VIDC_ERR, "Invalid params, core: %pK, imem: %pK\n", + device, imem); + return -EINVAL; + } + + rhdr.resource_handle = imem; /* cookie */ + rhdr.size = device->res->imem_size; + rhdr.resource_id = VIDC_RESOURCE_NONE; + + switch (imem->type) { + case IMEM_VMEM: + rhdr.resource_id = VIDC_RESOURCE_VMEM; + addr = imem->vmem; + break; + case IMEM_NONE: + dprintk(VIDC_DBG, "%s Target does not support IMEM", __func__); + rc = 0; + goto imem_set_failed; + default: + dprintk(VIDC_ERR, "IMEM of type %d unsupported\n", imem->type); + rc = -ENOTSUPP; + goto imem_set_failed; + } + + WARN_ON(!addr); + + rc = __core_set_resource(device, &rhdr, (void *)addr); + if (rc) { + dprintk(VIDC_ERR, "Failed to set IMEM on driver\n"); + goto imem_set_failed; + } + + dprintk(VIDC_DBG, + "Managed to set IMEM buffer of type %d sized %d bytes at %pa\n", + rhdr.resource_id, rhdr.size, &addr); + + rc = __vote_buses(device, device->bus_vote.data, + device->bus_vote.data_count); + if (rc) { + dprintk(VIDC_ERR, + "Failed to vote for buses after setting imem: %d\n", + rc); + } + +imem_set_failed: + return rc; +} + +static int __tzbsp_set_video_state(enum tzbsp_video_state state) +{ + struct tzbsp_video_set_state_req cmd = {0}; + int tzbsp_rsp = 0; + int rc = 0; + struct scm_desc desc = {0}; + + desc.args[0] = cmd.state = state; + desc.args[1] = cmd.spare = 0; + desc.arginfo = SCM_ARGS(2); + + rc = scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT, + TZBSP_VIDEO_SET_STATE), &desc); + tzbsp_rsp = desc.ret[0]; + + if (rc) { + dprintk(VIDC_ERR, "Failed scm_call %d\n", rc); + return rc; + } + + dprintk(VIDC_DBG, "Set state %d, resp %d\n", state, tzbsp_rsp); + if (tzbsp_rsp) { + dprintk(VIDC_ERR, + "Failed to set video core state to suspend: %d\n", + tzbsp_rsp); + return -EINVAL; + } + + return 0; +} + +static inline int __boot_firmware(struct venus_hfi_device *device) +{ + int rc = 0; + u32 ctrl_status = 0, count = 0, max_tries = 100; + + __write_register(device, VIDC_CTRL_INIT, 0x1); + while (!ctrl_status && count < max_tries) { + ctrl_status = __read_register(device, VIDC_CPU_CS_SCIACMDARG0); + if ((ctrl_status & 0xFE) == 0x4) { + dprintk(VIDC_ERR, "invalid setting for UC_REGION\n"); + break; + } + + usleep_range(500, 1000); + count++; + } + + if (count >= max_tries) { + dprintk(VIDC_ERR, "Error booting up vidc firmware\n"); + rc = -ETIME; + } + return rc; +} + +static struct clock_info *__get_clock(struct venus_hfi_device *device, + char *name) +{ + struct clock_info *vc; + + venus_hfi_for_each_clock(device, vc) { + if (!strcmp(vc->name, name)) + return vc; + } + + dprintk(VIDC_WARN, "%s Clock %s not found\n", __func__, name); + + return NULL; +} + +static unsigned long __get_clock_rate(struct clock_info *clock, + int num_mbs_per_sec, struct vidc_clk_scale_data *data) +{ + int num_rows = clock->count; + struct load_freq_table *table = clock->load_freq_tbl; + unsigned long freq = table[0].freq, max_freq = 0; + int i = 0, j = 0; + unsigned long instance_freq[VIDC_MAX_SESSIONS] = {0}; + + if (!data && !num_rows) { + freq = 0; + goto print_clk; + } + + if ((!num_mbs_per_sec || !data) && num_rows) { + freq = table[num_rows - 1].freq; + goto print_clk; + } + + for (i = 0; i < num_rows; i++) { + if (num_mbs_per_sec > table[i].load) + break; + for (j = 0; j < data->num_sessions; j++) { + bool matches = __is_session_supported( + table[i].supported_codecs, data->session[j]); + + if (!matches) + continue; + instance_freq[j] = table[i].freq; + } + } + for (i = 0; i < data->num_sessions; i++) + max_freq = max(instance_freq[i], max_freq); + + freq = max_freq ? : freq; +print_clk: + dprintk(VIDC_PROF, "Required clock rate = %lu num_mbs_per_sec %d\n", + freq, num_mbs_per_sec); + return freq; +} + +static unsigned long __get_clock_rate_with_bitrate(struct clock_info *clock, + int num_mbs_per_sec, struct vidc_clk_scale_data *data, + unsigned long instant_bitrate) +{ + int num_rows = clock->count; + struct load_freq_table *table = clock->load_freq_tbl; + unsigned long freq = table[0].freq, max_freq = 0; + unsigned long base_freq, supported_clk[VIDC_MAX_SESSIONS] = {0}; + int i, j; + + if (!data && !num_rows) { + freq = 0; + goto print_clk; + } + if ((!num_mbs_per_sec || !data) && num_rows) { + freq = table[num_rows - 1].freq; + goto print_clk; + } + + /* Get clock rate based on current load only */ + base_freq = __get_clock_rate(clock, num_mbs_per_sec, data); + + /* + * Supported bitrate = 40% of clock frequency + * Check if the instant bitrate is supported by the base frequency. + * If not, move on to the next frequency which supports the bitrate. + */ + + for (j = 0; j < data->num_sessions; j++) { + unsigned long supported_bitrate = 0; + + for (i = num_rows - 1; i >= 0; i--) { + bool matches = __is_session_supported( + table[i].supported_codecs, data->session[j]); + + if (!matches) + continue; + freq = table[i].freq; + + supported_bitrate = freq * 40/100; + /* + * Store this frequency for each instance, we need + * to select the maximum freq among all the instances. + */ + if (freq >= base_freq && + supported_bitrate >= instant_bitrate) { + supported_clk[j] = freq; + break; + } + } + + /* + * Current bitrate is higher than max supported load. + * Select max frequency to handle this load. + */ + if (i < 0) + supported_clk[j] = table[0].freq; + } + + for (i = 0; i < data->num_sessions; i++) + max_freq = max(supported_clk[i], max_freq); + + freq = max_freq ? : base_freq; + + if (base_freq == freq) + dprintk(VIDC_DBG, "Stay at base freq: %lu bitrate = %lu\n", + freq, instant_bitrate); + else + dprintk(VIDC_DBG, "Move up clock freq: %lu bitrate = %lu\n", + freq, instant_bitrate); +print_clk: + dprintk(VIDC_PROF, "Required clock rate = %lu num_mbs_per_sec %d\n", + freq, num_mbs_per_sec); + return freq; +} + +static unsigned long venus_hfi_get_core_clock_rate(void *dev, bool actual_rate) +{ + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + struct clock_info *vc; + + if (!device) { + dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, device); + return -EINVAL; + } + + if (actual_rate) { + vc = __get_clock(device, "core_clk"); + if (vc) + return clk_get_rate(vc->clk); + else + return 0; + } else { + return device->scaled_rate; + } +} + +static int venus_hfi_suspend(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } else if (!device->res->sw_power_collapsible) { + return -ENOTSUPP; + } + + mutex_lock(&device->lock); + + if (device->power_enabled) { + dprintk(VIDC_DBG, "Venus is busy\n"); + rc = -EBUSY; + } else { + dprintk(VIDC_DBG, "Venus is power suspended\n"); + rc = 0; + } + + mutex_unlock(&device->lock); + return rc; +} + +static enum hal_default_properties venus_hfi_get_default_properties(void *dev) +{ + enum hal_default_properties prop = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } + + mutex_lock(&device->lock); + + if (device->packetization_type == HFI_PACKETIZATION_3XX) + prop = HAL_VIDEO_DYNAMIC_BUF_MODE; + + mutex_unlock(&device->lock); + return prop; +} + +static int __halt_axi(struct venus_hfi_device *device) +{ + u32 reg; + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid input: %pK\n", device); + return -EINVAL; + } + + /* + * Driver needs to make sure that clocks are enabled to read Venus AXI + * registers. If not skip AXI HALT. + */ + if (!device->power_enabled) { + dprintk(VIDC_WARN, + "Clocks are OFF, skipping AXI HALT\n"); + WARN_ON(VIDC_DBG_WARN_ENABLE); + return -EINVAL; + } + + /* Halt AXI and AXI IMEM VBIF Access */ + reg = __read_register(device, VENUS_VBIF_AXI_HALT_CTRL0); + reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ; + __write_register(device, VENUS_VBIF_AXI_HALT_CTRL0, reg); + + /* Request for AXI bus port halt */ + rc = readl_poll_timeout(device->hal_data->register_base + + VENUS_VBIF_AXI_HALT_CTRL1, + reg, reg & VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK, + POLL_INTERVAL_US, + VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (rc) + dprintk(VIDC_WARN, "AXI bus port halt timeout\n"); + + return rc; +} + +static int __scale_clocks_cycles_per_mb(struct venus_hfi_device *device, + struct vidc_clk_scale_data *data, unsigned long instant_bitrate) +{ + int rc = 0, i = 0, j = 0; + struct clock_info *cl; + struct clock_freq_table *clk_freq_tbl = NULL; + struct allowed_clock_rates_table *allowed_clks_tbl = NULL; + struct clock_profile_entry *entry = NULL; + u64 total_freq = 0, rate = 0; + + clk_freq_tbl = &device->res->clock_freq_tbl; + allowed_clks_tbl = device->res->allowed_clks_tbl; + + if (!data) { + dprintk(VIDC_DBG, "%s: NULL scale data\n", __func__); + total_freq = device->clk_freq; + goto get_clock_freq; + } + + device->clk_bitrate = instant_bitrate; + + for (i = 0; i < data->num_sessions; i++) { + /* + * for each active session iterate through all possible + * sessions and get matching session's cycles per mb + * from dtsi and multiply with the session's load to + * get the frequency required for the session. + * accumulate all session's frequencies to get the + * total clock frequency. + */ + for (j = 0; j < clk_freq_tbl->count; j++) { + bool matched = false; + u64 freq = 0; + + entry = &clk_freq_tbl->clk_prof_entries[j]; + + matched = __is_session_supported(entry->codec_mask, + data->session[i]); + if (!matched) + continue; + + freq = entry->cycles * data->load[i]; + + if (data->power_mode[i] == VIDC_POWER_LOW && + entry->low_power_factor) { + /* low_power_factor is in Q16 format */ + freq = (freq * entry->low_power_factor) >> 16; + } + + total_freq += freq; + + dprintk(VIDC_DBG, + "session[%d] %#x: cycles (%d), load (%d), freq (%llu), factor (%d)\n", + i, data->session[i], entry->cycles, + data->load[i], freq, + entry->low_power_factor); + } + } + +get_clock_freq: + /* + * get required clock rate from allowed clock rates table + */ + for (i = device->res->allowed_clks_tbl_size - 1; i >= 0; i--) { + rate = allowed_clks_tbl[i].clock_rate; + if (rate >= total_freq) + break; + } + + venus_hfi_for_each_clock(device, cl) { + if (!cl->has_scaling) + continue; + + device->clk_freq = rate; + rc = clk_set_rate(cl->clk, rate); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to set clock rate %llu %s: %d\n", + __func__, rate, cl->name, rc); + return rc; + } + if (!strcmp(cl->name, "core_clk")) + device->scaled_rate = rate; + + dprintk(VIDC_DBG, + "scaling clock %s to %llu (required freq %llu)\n", + cl->name, rate, total_freq); + } + + return rc; +} + +static int __scale_clocks_load(struct venus_hfi_device *device, int load, + struct vidc_clk_scale_data *data, unsigned long instant_bitrate) +{ + struct clock_info *cl; + + device->clk_bitrate = instant_bitrate; + + venus_hfi_for_each_clock(device, cl) { + if (cl->has_scaling) { + + unsigned long rate = 0; + int rc; + /* + * load_fw and power_on needs to be addressed. + * differently. Below check enforces the same. + */ + if (!device->clk_bitrate && !data && !load && + device->clk_freq) + rate = device->clk_freq; + + if (!rate) { + if (!device->clk_bitrate) + rate = __get_clock_rate(cl, load, + data); + else + rate = __get_clock_rate_with_bitrate(cl, + load, data, + instant_bitrate); + } + device->clk_freq = rate; + rc = clk_set_rate(cl->clk, rate); + if (rc) { + dprintk(VIDC_ERR, + "Failed to set clock rate %lu %s: %d\n", + rate, cl->name, rc); + return rc; + } + + if (!strcmp(cl->name, "core_clk")) + device->scaled_rate = rate; + + dprintk(VIDC_PROF, "Scaling clock %s to %lu\n", + cl->name, rate); + } + } + + return 0; +} + +static int __scale_clocks(struct venus_hfi_device *device, + int load, struct vidc_clk_scale_data *data, + unsigned long instant_bitrate) +{ + int rc = 0; + + if (device->res->clock_freq_tbl.clk_prof_entries && + device->res->allowed_clks_tbl) + rc = __scale_clocks_cycles_per_mb(device, + data, instant_bitrate); + else if (device->res->load_freq_tbl) + rc = __scale_clocks_load(device, load, data, instant_bitrate); + else + dprintk(VIDC_DBG, "Clock scaling is not supported\n"); + + return rc; +} +static int venus_hfi_scale_clocks(void *dev, int load, + struct vidc_clk_scale_data *data, + unsigned long instant_bitrate) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + + if (!device) { + dprintk(VIDC_ERR, "Invalid args: %pK\n", device); + return -EINVAL; + } + + mutex_lock(&device->lock); + + if (__resume(device)) { + dprintk(VIDC_ERR, "Resume from power collapse failed\n"); + rc = -ENODEV; + goto exit; + } + + rc = __scale_clocks(device, load, data, instant_bitrate); +exit: + mutex_unlock(&device->lock); + return rc; +} + +/* Writes into cmdq without raising an interrupt */ +static int __iface_cmdq_write_relaxed(struct venus_hfi_device *device, + void *pkt, bool *requires_interrupt) +{ + struct vidc_iface_q_info *q_info; + struct vidc_hal_cmd_pkt_hdr *cmd_packet; + int result = -E2BIG; + + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + __strict_check(device); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_ERR, "%s - fw not in init state\n", __func__); + result = -EINVAL; + goto err_q_null; + } + + cmd_packet = (struct vidc_hal_cmd_pkt_hdr *)pkt; + device->last_packet_type = cmd_packet->packet_type; + + q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX]; + if (!q_info) { + dprintk(VIDC_ERR, "cannot write to shared Q's\n"); + goto err_q_null; + } + + if (!q_info->q_array.align_virtual_addr) { + dprintk(VIDC_ERR, "cannot write to shared CMD Q's\n"); + result = -ENODATA; + goto err_q_null; + } + + __sim_modify_cmd_packet((u8 *)pkt, device); + if (__resume(device)) { + dprintk(VIDC_ERR, "%s: Power on failed\n", __func__); + goto err_q_write; + } + + if (!__write_queue(q_info, (u8 *)pkt, requires_interrupt)) { + if (device->res->sw_power_collapsible) { + cancel_delayed_work(&venus_hfi_pm_work); + if (!queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, + msecs_to_jiffies( + msm_vidc_pwr_collapse_delay))) { + dprintk(VIDC_DBG, + "PM work already scheduled\n"); + } + } + + result = 0; + } else { + dprintk(VIDC_ERR, "__iface_cmdq_write: queue full\n"); + } + +err_q_write: +err_q_null: + return result; +} + +static int __iface_cmdq_write(struct venus_hfi_device *device, void *pkt) +{ + bool needs_interrupt = false; + int rc = __iface_cmdq_write_relaxed(device, pkt, &needs_interrupt); + + if (!rc && needs_interrupt) { + /* Consumer of cmdq prefers that we raise an interrupt */ + rc = 0; + __write_register(device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + } + + return rc; +} + +static int __iface_msgq_read(struct venus_hfi_device *device, void *pkt) +{ + u32 tx_req_is_set = 0; + int rc = 0; + struct vidc_iface_q_info *q_info; + + if (!pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + __strict_check(device); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__); + rc = -EINVAL; + goto read_error_null; + } + + q_info = &device->iface_queues[VIDC_IFACEQ_MSGQ_IDX]; + if (q_info->q_array.align_virtual_addr == NULL) { + dprintk(VIDC_ERR, "cannot read from shared MSG Q's\n"); + rc = -ENODATA; + goto read_error_null; + } + + if (!__read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) { + __hal_sim_modify_msg_packet((u8 *)pkt, device); + if (tx_req_is_set) + __write_register(device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + rc = 0; + } else + rc = -ENODATA; + +read_error_null: + return rc; +} + +static int __iface_dbgq_read(struct venus_hfi_device *device, void *pkt) +{ + u32 tx_req_is_set = 0; + int rc = 0; + struct vidc_iface_q_info *q_info; + + if (!pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + __strict_check(device); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__); + rc = -EINVAL; + goto dbg_error_null; + } + + q_info = &device->iface_queues[VIDC_IFACEQ_DBGQ_IDX]; + if (q_info->q_array.align_virtual_addr == NULL) { + dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n"); + rc = -ENODATA; + goto dbg_error_null; + } + + if (!__read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) { + if (tx_req_is_set) + __write_register(device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + rc = 0; + } else + rc = -ENODATA; + +dbg_error_null: + return rc; +} + +static void __set_queue_hdr_defaults(struct hfi_queue_header *q_hdr) +{ + q_hdr->qhdr_status = 0x1; + q_hdr->qhdr_type = VIDC_IFACEQ_DFLT_QHDR; + q_hdr->qhdr_q_size = VIDC_IFACEQ_QUEUE_SIZE / 4; + q_hdr->qhdr_pkt_size = 0; + q_hdr->qhdr_rx_wm = 0x1; + q_hdr->qhdr_tx_wm = 0x1; + q_hdr->qhdr_rx_req = 0x1; + q_hdr->qhdr_tx_req = 0x0; + q_hdr->qhdr_rx_irq_status = 0x0; + q_hdr->qhdr_tx_irq_status = 0x0; + q_hdr->qhdr_read_idx = 0x0; + q_hdr->qhdr_write_idx = 0x0; +} + +static void __interface_queues_release(struct venus_hfi_device *device) +{ + int i; + struct hfi_mem_map_table *qdss; + struct hfi_mem_map *mem_map; + int num_entries = device->res->qdss_addr_set.count; + unsigned long mem_map_table_base_addr; + struct context_bank_info *cb; + + if (device->qdss.align_virtual_addr) { + qdss = (struct hfi_mem_map_table *) + device->qdss.align_virtual_addr; + qdss->mem_map_num_entries = num_entries; + mem_map_table_base_addr = + device->qdss.align_device_addr + + sizeof(struct hfi_mem_map_table); + qdss->mem_map_table_base_addr = + (u32)mem_map_table_base_addr; + if ((unsigned long)qdss->mem_map_table_base_addr != + mem_map_table_base_addr) { + dprintk(VIDC_ERR, + "Invalid mem_map_table_base_addr %#lx", + mem_map_table_base_addr); + } + + mem_map = (struct hfi_mem_map *)(qdss + 1); + cb = msm_smem_get_context_bank(MSM_VIDC_UNKNOWN, + false, device->res, HAL_BUFFER_INTERNAL_CMD_QUEUE); + + for (i = 0; cb && i < num_entries; i++) { + iommu_unmap(cb->domain, + mem_map[i].virtual_addr, + mem_map[i].size); + } + + __smem_free(device, &device->qdss.mem_data); + } + + __smem_free(device, &device->iface_q_table.mem_data); + __smem_free(device, &device->sfr.mem_data); + + for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) { + device->iface_queues[i].q_hdr = NULL; + device->iface_queues[i].q_array.align_virtual_addr = NULL; + device->iface_queues[i].q_array.align_device_addr = 0; + } + + device->iface_q_table.align_virtual_addr = NULL; + device->iface_q_table.align_device_addr = 0; + + device->qdss.align_virtual_addr = NULL; + device->qdss.align_device_addr = 0; + + device->sfr.align_virtual_addr = NULL; + device->sfr.align_device_addr = 0; + + device->mem_addr.align_virtual_addr = NULL; + device->mem_addr.align_device_addr = 0; + +} + +static int __get_qdss_iommu_virtual_addr(struct venus_hfi_device *dev, + struct hfi_mem_map *mem_map, struct iommu_domain *domain) +{ + int i; + int rc = 0; + dma_addr_t iova = QDSS_IOVA_START; + int num_entries = dev->res->qdss_addr_set.count; + struct addr_range *qdss_addr_tbl = dev->res->qdss_addr_set.addr_tbl; + + if (!num_entries) + return -ENODATA; + + for (i = 0; i < num_entries; i++) { + if (domain) { + rc = iommu_map(domain, iova, + qdss_addr_tbl[i].start, + qdss_addr_tbl[i].size, + IOMMU_READ | IOMMU_WRITE); + + if (rc) { + dprintk(VIDC_ERR, + "IOMMU QDSS mapping failed for addr %#x\n", + qdss_addr_tbl[i].start); + rc = -ENOMEM; + break; + } + } else { + iova = qdss_addr_tbl[i].start; + } + + mem_map[i].virtual_addr = (u32)iova; + mem_map[i].physical_addr = qdss_addr_tbl[i].start; + mem_map[i].size = qdss_addr_tbl[i].size; + mem_map[i].attr = 0x0; + + iova += mem_map[i].size; + } + + if (i < num_entries) { + dprintk(VIDC_ERR, + "QDSS mapping failed, Freeing other entries %d\n", i); + + for (--i; domain && i >= 0; i--) { + iommu_unmap(domain, + mem_map[i].virtual_addr, + mem_map[i].size); + } + } + + return rc; +} + +static void __setup_ucregion_memory_map(struct venus_hfi_device *device) +{ + __write_register(device, VIDC_UC_REGION_ADDR, + (u32)device->iface_q_table.align_device_addr); + __write_register(device, VIDC_UC_REGION_SIZE, SHARED_QSIZE); + __write_register(device, VIDC_CPU_CS_SCIACMDARG2, + (u32)device->iface_q_table.align_device_addr); + __write_register(device, VIDC_CPU_CS_SCIACMDARG1, 0x01); + if (device->sfr.align_device_addr) + __write_register(device, VIDC_SFR_ADDR, + (u32)device->sfr.align_device_addr); + if (device->qdss.align_device_addr) + __write_register(device, VIDC_MMAP_ADDR, + (u32)device->qdss.align_device_addr); +} + +static int __interface_queues_init(struct venus_hfi_device *dev) +{ + struct hfi_queue_table_header *q_tbl_hdr; + struct hfi_queue_header *q_hdr; + u32 i; + int rc = 0; + struct hfi_mem_map_table *qdss; + struct hfi_mem_map *mem_map; + struct vidc_iface_q_info *iface_q; + struct hfi_sfr_struct *vsfr; + struct vidc_mem_addr *mem_addr; + int offset = 0; + int num_entries = dev->res->qdss_addr_set.count; + u32 value = 0; + phys_addr_t fw_bias = 0; + size_t q_size; + unsigned long mem_map_table_base_addr; + struct context_bank_info *cb; + + q_size = SHARED_QSIZE - ALIGNED_SFR_SIZE - ALIGNED_QDSS_SIZE; + mem_addr = &dev->mem_addr; + if (!is_iommu_present(dev->res)) + fw_bias = dev->hal_data->firmware_base; + rc = __smem_alloc(dev, mem_addr, q_size, 1, SMEM_UNCACHED, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (rc) { + dprintk(VIDC_ERR, "iface_q_table_alloc_fail\n"); + goto fail_alloc_queue; + } + + dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr; + dev->iface_q_table.align_device_addr = mem_addr->align_device_addr - + fw_bias; + dev->iface_q_table.mem_size = VIDC_IFACEQ_TABLE_SIZE; + dev->iface_q_table.mem_data = mem_addr->mem_data; + offset += dev->iface_q_table.mem_size; + + for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) { + iface_q = &dev->iface_queues[i]; + iface_q->q_array.align_device_addr = mem_addr->align_device_addr + + offset - fw_bias; + iface_q->q_array.align_virtual_addr = + mem_addr->align_virtual_addr + offset; + iface_q->q_array.mem_size = VIDC_IFACEQ_QUEUE_SIZE; + offset += iface_q->q_array.mem_size; + iface_q->q_hdr = VIDC_IFACEQ_GET_QHDR_START_ADDR( + dev->iface_q_table.align_virtual_addr, i); + __set_queue_hdr_defaults(iface_q->q_hdr); + } + + if ((msm_vidc_fw_debug_mode & HFI_DEBUG_MODE_QDSS) && num_entries) { + rc = __smem_alloc(dev, mem_addr, + ALIGNED_QDSS_SIZE, 1, SMEM_UNCACHED, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (rc) { + dprintk(VIDC_WARN, + "qdss_alloc_fail: QDSS messages logging will not work\n"); + dev->qdss.align_device_addr = 0; + } else { + dev->qdss.align_device_addr = + mem_addr->align_device_addr - fw_bias; + dev->qdss.align_virtual_addr = + mem_addr->align_virtual_addr; + dev->qdss.mem_size = ALIGNED_QDSS_SIZE; + dev->qdss.mem_data = mem_addr->mem_data; + } + } + + rc = __smem_alloc(dev, mem_addr, + ALIGNED_SFR_SIZE, 1, SMEM_UNCACHED, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + if (rc) { + dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work\n"); + dev->sfr.align_device_addr = 0; + } else { + dev->sfr.align_device_addr = mem_addr->align_device_addr - + fw_bias; + dev->sfr.align_virtual_addr = mem_addr->align_virtual_addr; + dev->sfr.mem_size = ALIGNED_SFR_SIZE; + dev->sfr.mem_data = mem_addr->mem_data; + } + + q_tbl_hdr = (struct hfi_queue_table_header *) + dev->iface_q_table.align_virtual_addr; + q_tbl_hdr->qtbl_version = 0; + q_tbl_hdr->qtbl_size = VIDC_IFACEQ_TABLE_SIZE; + q_tbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_queue_table_header); + q_tbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_queue_header); + q_tbl_hdr->qtbl_num_q = VIDC_IFACEQ_NUMQ; + q_tbl_hdr->qtbl_num_active_q = VIDC_IFACEQ_NUMQ; + + iface_q = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = (u32)iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_HOST_TO_CTRL_CMD_Q; + if ((phys_addr_t)q_hdr->qhdr_start_addr != + iface_q->q_array.align_device_addr) { + dprintk(VIDC_ERR, "Invalid CMDQ device address (%pa)", + &iface_q->q_array.align_device_addr); + } + + iface_q = &dev->iface_queues[VIDC_IFACEQ_MSGQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = (u32)iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_MSG_Q; + if ((phys_addr_t)q_hdr->qhdr_start_addr != + iface_q->q_array.align_device_addr) { + dprintk(VIDC_ERR, "Invalid MSGQ device address (%pa)", + &iface_q->q_array.align_device_addr); + } + + iface_q = &dev->iface_queues[VIDC_IFACEQ_DBGQ_IDX]; + q_hdr = iface_q->q_hdr; + q_hdr->qhdr_start_addr = (u32)iface_q->q_array.align_device_addr; + q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q; + /* + * Set receive request to zero on debug queue as there is no + * need of interrupt from video hardware for debug messages + */ + q_hdr->qhdr_rx_req = 0; + if ((phys_addr_t)q_hdr->qhdr_start_addr != + iface_q->q_array.align_device_addr) { + dprintk(VIDC_ERR, "Invalid DBGQ device address (%pa)", + &iface_q->q_array.align_device_addr); + } + + value = (u32)dev->iface_q_table.align_device_addr; + if ((phys_addr_t)value != + dev->iface_q_table.align_device_addr) { + dprintk(VIDC_ERR, + "Invalid iface_q_table device address (%pa)", + &dev->iface_q_table.align_device_addr); + } + + if (dev->qdss.align_virtual_addr) { + qdss = (struct hfi_mem_map_table *)dev->qdss.align_virtual_addr; + qdss->mem_map_num_entries = num_entries; + mem_map_table_base_addr = dev->qdss.align_device_addr + + sizeof(struct hfi_mem_map_table); + qdss->mem_map_table_base_addr = + (u32)mem_map_table_base_addr; + if ((phys_addr_t)qdss->mem_map_table_base_addr != + mem_map_table_base_addr) { + dprintk(VIDC_ERR, + "Invalid mem_map_table_base_addr (%#lx)", + mem_map_table_base_addr); + } + + mem_map = (struct hfi_mem_map *)(qdss + 1); + cb = msm_smem_get_context_bank(MSM_VIDC_UNKNOWN, false, + dev->res, HAL_BUFFER_INTERNAL_CMD_QUEUE); + + if (!cb) { + dprintk(VIDC_ERR, + "%s: failed to get context bank\n", __func__); + return -EINVAL; + } + + rc = __get_qdss_iommu_virtual_addr(dev, mem_map, cb->domain); + if (rc) { + dprintk(VIDC_ERR, + "IOMMU mapping failed, Freeing qdss memdata\n"); + __smem_free(dev, &dev->qdss.mem_data); + dev->qdss.align_virtual_addr = NULL; + dev->qdss.align_device_addr = 0; + } + + value = (u32)dev->qdss.align_device_addr; + if ((phys_addr_t)value != + dev->qdss.align_device_addr) { + dprintk(VIDC_ERR, "Invalid qdss device address (%pa)", + &dev->qdss.align_device_addr); + } + } + + vsfr = (struct hfi_sfr_struct *) dev->sfr.align_virtual_addr; + vsfr->bufSize = ALIGNED_SFR_SIZE; + value = (u32)dev->sfr.align_device_addr; + if ((phys_addr_t)value != + dev->sfr.align_device_addr) { + dprintk(VIDC_ERR, "Invalid sfr device address (%pa)", + &dev->sfr.align_device_addr); + } + + __setup_ucregion_memory_map(dev); + return 0; +fail_alloc_queue: + return -ENOMEM; +} + +static int __sys_set_debug(struct venus_hfi_device *device, u32 debug) +{ + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + rc = call_hfi_pkt_op(device, sys_debug_config, pkt, debug); + if (rc) { + dprintk(VIDC_WARN, + "Debug mode setting to FW failed\n"); + return -ENOTEMPTY; + } + + if (__iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int __sys_set_coverage(struct venus_hfi_device *device, u32 mode) +{ + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + rc = call_hfi_pkt_op(device, sys_coverage_config, + pkt, mode); + if (rc) { + dprintk(VIDC_WARN, + "Coverage mode setting to FW failed\n"); + return -ENOTEMPTY; + } + + if (__iface_cmdq_write(device, pkt)) { + dprintk(VIDC_WARN, "Failed to send coverage pkt to f/w\n"); + return -ENOTEMPTY; + } + + return 0; +} + +static int __sys_set_idle_message(struct venus_hfi_device *device, + bool enable) +{ + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + if (!enable) { + dprintk(VIDC_DBG, "sys_idle_indicator is not enabled\n"); + return 0; + } + + call_hfi_pkt_op(device, sys_idle_indicator, pkt, enable); + if (__iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int __sys_set_power_control(struct venus_hfi_device *device, + bool enable) +{ + struct regulator_info *rinfo; + bool supported = false; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hfi_cmd_sys_set_property_packet *pkt = + (struct hfi_cmd_sys_set_property_packet *) &packet; + + venus_hfi_for_each_regulator(device, rinfo) { + if (rinfo->has_hw_power_collapse) { + supported = true; + break; + } + } + + if (!supported) + return 0; + + call_hfi_pkt_op(device, sys_power_control, pkt, enable); + if (__iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int venus_hfi_core_init(void *device) +{ + struct hfi_cmd_sys_init_packet pkt; + struct hfi_cmd_sys_get_property_packet version_pkt; + int rc = 0; + struct list_head *ptr, *next; + struct hal_session *session = NULL; + struct venus_hfi_device *dev; + + if (!device) { + dprintk(VIDC_ERR, "Invalid device\n"); + return -ENODEV; + } + + dev = device; + mutex_lock(&dev->lock); + + init_completion(&release_resources_done); + + rc = __load_fw(dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to load Venus FW\n"); + goto err_load_fw; + } + + __set_state(dev, VENUS_STATE_INIT); + + list_for_each_safe(ptr, next, &dev->sess_head) { + /* This means that session list is not empty. Kick stale + * sessions out of our valid instance list, but keep the + * list_head inited so that list_del (in the future, called + * by session_clean()) will be valid. When client doesn't close + * them, then it is a genuine leak which driver can't fix. + */ + session = list_entry(ptr, struct hal_session, list); + list_del_init(&session->list); + } + + INIT_LIST_HEAD(&dev->sess_head); + + __set_registers(dev); + + dprintk(VIDC_DBG, "Dev_Virt: %pa, Reg_Virt: %pK\n", + &dev->hal_data->firmware_base, + dev->hal_data->register_base); + + rc = __interface_queues_init(dev); + if (rc) { + dprintk(VIDC_ERR, "failed to init queues\n"); + rc = -ENOMEM; + goto err_core_init; + } + + rc = __boot_firmware(dev); + if (rc) { + dprintk(VIDC_ERR, "Failed to start core\n"); + rc = -ENODEV; + goto err_core_init; + } + + rc = call_hfi_pkt_op(dev, sys_init, &pkt, HFI_VIDEO_ARCH_OX); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sys init pkt\n"); + goto err_core_init; + } + + if (__iface_cmdq_write(dev, &pkt)) { + rc = -ENOTEMPTY; + goto err_core_init; + } + + rc = call_hfi_pkt_op(dev, sys_image_version, &version_pkt); + if (rc || __iface_cmdq_write(dev, &version_pkt)) + dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n"); + + if (dev->res->pm_qos_latency_us) { +#ifdef CONFIG_SMP + dev->qos.type = PM_QOS_REQ_AFFINE_IRQ; + dev->qos.irq = dev->hal_data->irq; +#endif + pm_qos_add_request(&dev->qos, PM_QOS_CPU_DMA_LATENCY, + dev->res->pm_qos_latency_us); + } + + mutex_unlock(&dev->lock); + return rc; +err_core_init: + __set_state(dev, VENUS_STATE_DEINIT); + __unload_fw(dev); +err_load_fw: + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_core_release(void *dev) +{ + struct venus_hfi_device *device = dev; + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + mutex_lock(&device->lock); + + if (device->res->pm_qos_latency_us && + pm_qos_request_active(&device->qos)) + pm_qos_remove_request(&device->qos); + __set_state(device, VENUS_STATE_DEINIT); + __unload_fw(device); + + mutex_unlock(&device->lock); + + return rc; +} + +static void __core_clear_interrupt(struct venus_hfi_device *device) +{ + u32 intr_status = 0; + + if (!device) { + dprintk(VIDC_ERR, "%s: NULL device\n", __func__); + return; + } + + intr_status = __read_register(device, VIDC_WRAPPER_INTR_STATUS); + + if (intr_status & VIDC_WRAPPER_INTR_STATUS_A2H_BMSK || + intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK || + intr_status & + VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK) { + device->intr_status |= intr_status; + device->reg_count++; + dprintk(VIDC_DBG, + "INTERRUPT for device: %pK: times: %d interrupt_status: %d\n", + device, device->reg_count, intr_status); + } else { + device->spur_count++; + dprintk(VIDC_INFO, + "SPURIOUS_INTR for device: %pK: times: %d interrupt_status: %d\n", + device, device->spur_count, intr_status); + } + + __write_register(device, VIDC_CPU_CS_A2HSOFTINTCLR, 1); + __write_register(device, VIDC_WRAPPER_INTR_CLEAR, intr_status); + dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt\n"); +} + +static int venus_hfi_core_ping(void *device) +{ + struct hfi_cmd_sys_ping_packet pkt; + int rc = 0; + struct venus_hfi_device *dev; + + if (!device) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + dev = device; + mutex_lock(&dev->lock); + + rc = call_hfi_pkt_op(dev, sys_ping, &pkt); + if (rc) { + dprintk(VIDC_ERR, "core_ping: failed to create packet\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_core_trigger_ssr(void *device, + enum hal_ssr_trigger_type type) +{ + struct hfi_cmd_sys_test_ssr_packet pkt; + int rc = 0; + struct venus_hfi_device *dev; + + if (!device) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + dev = device; + mutex_lock(&dev->lock); + + rc = call_hfi_pkt_op(dev, ssr_cmd, type, &pkt); + if (rc) { + dprintk(VIDC_ERR, "core_ping: failed to create packet\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_session_set_property(void *sess, + enum hal_property ptype, void *pdata) +{ + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + struct hfi_cmd_session_set_property_packet *pkt = + (struct hfi_cmd_session_set_property_packet *) &packet; + struct hal_session *session = sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session || !session->device || !pdata) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + dprintk(VIDC_INFO, "in set_prop,with prop id: %#x\n", ptype); + + rc = call_hfi_pkt_op(device, session_set_property, + pkt, session, ptype, pdata); + + if (rc == -ENOTSUPP) { + dprintk(VIDC_DBG, + "set property: unsupported prop id: %#x\n", ptype); + rc = 0; + goto err_set_prop; + } else if (rc) { + dprintk(VIDC_ERR, "set property: failed to create packet\n"); + rc = -EINVAL; + goto err_set_prop; + } + + if (__iface_cmdq_write(session->device, pkt)) { + rc = -ENOTEMPTY; + goto err_set_prop; + } + +err_set_prop: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_get_property(void *sess, + enum hal_property ptype) +{ + struct hfi_cmd_session_get_property_packet pkt = {0}; + struct hal_session *session = sess; + int rc = 0; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + dprintk(VIDC_INFO, "%s: property id: %d\n", __func__, ptype); + + rc = call_hfi_pkt_op(device, session_get_property, + &pkt, session, ptype); + if (rc) { + dprintk(VIDC_ERR, "get property profile: pkt failed\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) { + rc = -ENOTEMPTY; + dprintk(VIDC_ERR, "%s cmdq_write error\n", __func__); + } + +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static void __set_default_sys_properties(struct venus_hfi_device *device) +{ + if (__sys_set_debug(device, msm_vidc_fw_debug)) + dprintk(VIDC_WARN, "Setting fw_debug msg ON failed\n"); + if (__sys_set_idle_message(device, + device->res->sys_idle_indicator || + !msm_vidc_sys_idle_indicator)) + dprintk(VIDC_WARN, "Setting idle response ON failed\n"); + if (__sys_set_power_control(device, msm_vidc_fw_low_power_mode)) + dprintk(VIDC_WARN, "Setting h/w power collapse ON failed\n"); +} + +static void __session_clean(struct hal_session *session) +{ + dprintk(VIDC_DBG, "deleted the session: %pK\n", session); + list_del(&session->list); + /* Poison the session handle with zeros */ + *session = (struct hal_session){ {0} }; + kfree(session); +} + +static int venus_hfi_session_clean(void *session) +{ + struct hal_session *sess_close; + struct venus_hfi_device *device; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess_close = session; + device = sess_close->device; + + if (!device) { + dprintk(VIDC_ERR, "Invalid device handle %s\n", __func__); + return -EINVAL; + } + + mutex_lock(&device->lock); + + __session_clean(sess_close); + __flush_debug_queue(device, NULL); + + mutex_unlock(&device->lock); + return 0; +} + +static int venus_hfi_session_init(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type, + void **new_session) +{ + struct hfi_cmd_sys_session_init_packet pkt; + struct venus_hfi_device *dev; + struct hal_session *s; + + if (!device || !new_session) { + dprintk(VIDC_ERR, "%s - invalid input\n", __func__); + return -EINVAL; + } + + dev = device; + mutex_lock(&dev->lock); + + s = kzalloc(sizeof(struct hal_session), GFP_KERNEL); + if (!s) { + dprintk(VIDC_ERR, "new session fail: Out of memory\n"); + goto err_session_init_fail; + } + + s->session_id = session_id; + s->is_decoder = (session_type == HAL_VIDEO_DOMAIN_DECODER); + s->device = dev; + s->codec = codec_type; + s->domain = session_type; + dprintk(VIDC_DBG, + "%s: inst %pK, session %pK, codec 0x%x, domain 0x%x\n", + __func__, session_id, s, s->codec, s->domain); + + list_add_tail(&s->list, &dev->sess_head); + + __set_default_sys_properties(device); + + if (call_hfi_pkt_op(dev, session_init, &pkt, + s, session_type, codec_type)) { + dprintk(VIDC_ERR, "session_init: failed to create packet\n"); + goto err_session_init_fail; + } + + *new_session = s; + if (__iface_cmdq_write(dev, &pkt)) + goto err_session_init_fail; + + mutex_unlock(&dev->lock); + return 0; + +err_session_init_fail: + if (s) + __session_clean(s); + *new_session = NULL; + mutex_unlock(&dev->lock); + return -EINVAL; +} + +static int __send_session_cmd(struct hal_session *session, int pkt_type) +{ + struct vidc_hal_session_cmd_pkt pkt; + int rc = 0; + struct venus_hfi_device *device = session->device; + + rc = call_hfi_pkt_op(device, session_cmd, + &pkt, pkt_type, session); + if (rc == -EPERM) + return 0; + + if (rc) { + dprintk(VIDC_ERR, "send session cmd: create pkt failed\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static int venus_hfi_session_end(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + + if (!msm_vidc_fw_coverage) { + if (__sys_set_coverage(sess->device, msm_vidc_fw_coverage)) + dprintk(VIDC_WARN, "Fw_coverage msg ON failed\n"); + } + + rc = __send_session_cmd(session, HFI_CMD_SYS_SESSION_END); + + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_abort(void *sess) +{ + struct hal_session *session; + struct venus_hfi_device *device; + int rc = 0; + + session = sess; + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + device = session->device; + + mutex_lock(&device->lock); + + __flush_debug_queue(device, NULL); + rc = __send_session_cmd(session, HFI_CMD_SYS_SESSION_ABORT); + + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_set_buffers(void *sess, + struct vidc_buffer_addr_info *buffer_info) +{ + struct hfi_cmd_session_set_buffers_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !buffer_info) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) { + /* + * Hardware doesn't care about input buffers being + * published beforehand + */ + rc = 0; + goto err_create_pkt; + } + + pkt = (struct hfi_cmd_session_set_buffers_packet *)packet; + + rc = call_hfi_pkt_op(device, session_set_buffers, + pkt, session, buffer_info); + if (rc) { + dprintk(VIDC_ERR, "set buffers: failed to create packet\n"); + goto err_create_pkt; + } + + dprintk(VIDC_INFO, "set buffers: %#x\n", buffer_info->buffer_type); + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_release_buffers(void *sess, + struct vidc_buffer_addr_info *buffer_info) +{ + struct hfi_cmd_session_release_buffer_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !buffer_info) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) { + rc = 0; + goto err_create_pkt; + } + + pkt = (struct hfi_cmd_session_release_buffer_packet *) packet; + + rc = call_hfi_pkt_op(device, session_release_buffers, + pkt, session, buffer_info); + if (rc) { + dprintk(VIDC_ERR, "release buffers: failed to create packet\n"); + goto err_create_pkt; + } + + dprintk(VIDC_INFO, "Release buffers: %#x\n", buffer_info->buffer_type); + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_load_res(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_LOAD_RESOURCES); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_release_res(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_RELEASE_RESOURCES); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_start(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_START); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_continue(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_CONTINUE); + mutex_unlock(&device->lock); + + return rc; +} + +static int venus_hfi_session_stop(void *session) +{ + struct hal_session *sess; + struct venus_hfi_device *device; + int rc = 0; + + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + + sess = session; + device = sess->device; + + mutex_lock(&device->lock); + rc = __send_session_cmd(sess, HFI_CMD_SESSION_STOP); + mutex_unlock(&device->lock); + + return rc; +} + +static int __session_etb(struct hal_session *session, + struct vidc_frame_data *input_frame, bool relaxed) +{ + int rc = 0; + struct venus_hfi_device *device = session->device; + + if (session->is_decoder) { + struct hfi_cmd_session_empty_buffer_compressed_packet pkt; + + rc = call_hfi_pkt_op(device, session_etb_decoder, + &pkt, session, input_frame); + if (rc) { + dprintk(VIDC_ERR, + "Session etb decoder: failed to create pkt\n"); + goto err_create_pkt; + } + + if (!relaxed) + rc = __iface_cmdq_write(session->device, &pkt); + else + rc = __iface_cmdq_write_relaxed(session->device, + &pkt, NULL); + if (rc) + goto err_create_pkt; + } else { + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet + pkt; + + rc = call_hfi_pkt_op(device, session_etb_encoder, + &pkt, session, input_frame); + if (rc) { + dprintk(VIDC_ERR, + "Session etb encoder: failed to create pkt\n"); + goto err_create_pkt; + } + + if (!relaxed) + rc = __iface_cmdq_write(session->device, &pkt); + else + rc = __iface_cmdq_write_relaxed(session->device, + &pkt, NULL); + if (rc) + goto err_create_pkt; + } + +err_create_pkt: + return rc; +} + +static int venus_hfi_session_etb(void *sess, + struct vidc_frame_data *input_frame) +{ + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !input_frame) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + rc = __session_etb(session, input_frame, false); + mutex_unlock(&device->lock); + return rc; +} + +static int __session_ftb(struct hal_session *session, + struct vidc_frame_data *output_frame, bool relaxed) +{ + int rc = 0; + struct venus_hfi_device *device = session->device; + struct hfi_cmd_session_fill_buffer_packet pkt; + + rc = call_hfi_pkt_op(device, session_ftb, + &pkt, session, output_frame); + if (rc) { + dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n"); + goto err_create_pkt; + } + + if (!relaxed) + rc = __iface_cmdq_write(session->device, &pkt); + else + rc = __iface_cmdq_write_relaxed(session->device, + &pkt, NULL); + +err_create_pkt: + return rc; +} + +static int venus_hfi_session_ftb(void *sess, + struct vidc_frame_data *output_frame) +{ + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !output_frame) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + rc = __session_ftb(session, output_frame, false); + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_process_batch(void *sess, + int num_etbs, struct vidc_frame_data etbs[], + int num_ftbs, struct vidc_frame_data ftbs[]) +{ + int rc = 0, c = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + struct hfi_cmd_session_sync_process_packet pkt; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: Invalid Params\n", __func__); + return -EINVAL; + } + + device = session->device; + + mutex_lock(&device->lock); + for (c = 0; c < num_ftbs; ++c) { + rc = __session_ftb(session, &ftbs[c], true); + if (rc) { + dprintk(VIDC_ERR, "Failed to queue batched ftb: %d\n", + rc); + goto err_etbs_and_ftbs; + } + } + + for (c = 0; c < num_etbs; ++c) { + rc = __session_etb(session, &etbs[c], true); + if (rc) { + dprintk(VIDC_ERR, "Failed to queue batched etb: %d\n", + rc); + goto err_etbs_and_ftbs; + } + } + + rc = call_hfi_pkt_op(device, session_sync_process, &pkt, session); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sync packet\n"); + goto err_etbs_and_ftbs; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; + +err_etbs_and_ftbs: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_parse_seq_hdr(void *sess, + struct vidc_seq_hdr *seq_hdr) +{ + struct hfi_cmd_session_parse_sequence_header_packet *pkt; + int rc = 0; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !seq_hdr) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + pkt = (struct hfi_cmd_session_parse_sequence_header_packet *)packet; + rc = call_hfi_pkt_op(device, session_parse_seq_header, + pkt, session, seq_hdr); + if (rc) { + dprintk(VIDC_ERR, + "Session parse seq hdr: failed to create pkt\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_get_seq_hdr(void *sess, + struct vidc_seq_hdr *seq_hdr) +{ + struct hfi_cmd_session_get_sequence_header_packet *pkt; + int rc = 0; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device || !seq_hdr) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + device = session->device; + mutex_lock(&device->lock); + + pkt = (struct hfi_cmd_session_get_sequence_header_packet *)packet; + rc = call_hfi_pkt_op(device, session_get_seq_hdr, + pkt, session, seq_hdr); + if (rc) { + dprintk(VIDC_ERR, + "Session get seq hdr: failed to create pkt\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_get_buf_req(void *sess) +{ + struct hfi_cmd_session_get_property_packet pkt; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "invalid session"); + return -ENODEV; + } + + device = session->device; + mutex_lock(&device->lock); + + rc = call_hfi_pkt_op(device, session_get_buf_req, + &pkt, session); + if (rc) { + dprintk(VIDC_ERR, + "Session get buf req: failed to create pkt\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode) +{ + struct hfi_cmd_session_flush_packet pkt; + int rc = 0; + struct hal_session *session = sess; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "invalid session"); + return -ENODEV; + } + + device = session->device; + mutex_lock(&device->lock); + + rc = call_hfi_pkt_op(device, session_flush, + &pkt, session, flush_mode); + if (rc) { + dprintk(VIDC_ERR, "Session flush: failed to create pkt\n"); + goto err_create_pkt; + } + + if (__iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + mutex_unlock(&device->lock); + return rc; +} + +static int __check_core_registered(struct hal_device_data core, + phys_addr_t fw_addr, u8 *reg_addr, u32 reg_size, + phys_addr_t irq) +{ + struct venus_hfi_device *device; + struct list_head *curr, *next; + + if (core.dev_count) { + list_for_each_safe(curr, next, &core.dev_head) { + device = list_entry(curr, + struct venus_hfi_device, list); + if (device && device->hal_data->irq == irq && + (CONTAINS(device->hal_data->firmware_base, + FIRMWARE_SIZE, fw_addr) || + CONTAINS(fw_addr, FIRMWARE_SIZE, + device->hal_data->firmware_base) || + CONTAINS(device->hal_data->register_base, + reg_size, reg_addr) || + CONTAINS(reg_addr, reg_size, + device->hal_data->register_base) || + OVERLAPS(device->hal_data->register_base, + reg_size, reg_addr, reg_size) || + OVERLAPS(reg_addr, reg_size, + device->hal_data->register_base, + reg_size) || + OVERLAPS(device->hal_data->firmware_base, + FIRMWARE_SIZE, fw_addr, + FIRMWARE_SIZE) || + OVERLAPS(fw_addr, FIRMWARE_SIZE, + device->hal_data->firmware_base, + FIRMWARE_SIZE))) { + return 0; + } + dprintk(VIDC_INFO, "Device not registered\n"); + return -EINVAL; + } + } else { + dprintk(VIDC_INFO, "no device Registered\n"); + } + + return -EINVAL; +} + +static void __process_fatal_error( + struct venus_hfi_device *device) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + cmd_done.device_id = device->device_id; + device->callback(HAL_SYS_ERROR, &cmd_done); +} + +static int __prepare_pc(struct venus_hfi_device *device) +{ + int rc = 0; + struct hfi_cmd_sys_pc_prep_packet pkt; + + rc = call_hfi_pkt_op(device, sys_pc_prep, &pkt); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sys pc prep pkt\n"); + goto err_pc_prep; + } + + if (__iface_cmdq_write(device, &pkt)) + rc = -ENOTEMPTY; + if (rc) + dprintk(VIDC_ERR, "Failed to prepare venus for power off"); +err_pc_prep: + return rc; +} + +static void venus_hfi_pm_handler(struct work_struct *work) +{ + int rc = 0; + u32 wfi_status = 0, idle_status = 0, pc_ready = 0; + int count = 0; + const int max_tries = 5; + struct venus_hfi_device *device = list_first_entry( + &hal_ctxt.dev_head, struct venus_hfi_device, list); + if (!device) { + dprintk(VIDC_ERR, "%s: NULL device\n", __func__); + return; + } + + /* + * It is ok to check this variable outside the lock since + * it is being updated in this context only + */ + if (device->skip_pc_count >= VIDC_MAX_PC_SKIP_COUNT) { + dprintk(VIDC_WARN, "Failed to PC for %d times\n", + device->skip_pc_count); + device->skip_pc_count = 0; + __process_fatal_error(device); + return; + } + mutex_lock(&device->lock); + if (!device->power_enabled) { + dprintk(VIDC_DBG, "%s: Power already disabled\n", + __func__); + goto exit; + } + + rc = __core_in_valid_state(device); + if (!rc) { + dprintk(VIDC_WARN, + "Core is in bad state, Skipping power collapse\n"); + goto skip_power_off; + } + pc_ready = __read_register(device, VIDC_CPU_CS_SCIACMDARG0) & + VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY; + if (!pc_ready) { + wfi_status = __read_register(device, + VIDC_WRAPPER_CPU_STATUS); + idle_status = __read_register(device, + VIDC_CPU_CS_SCIACMDARG0); + if (!(wfi_status & BIT(0)) || + !(idle_status & BIT(30))) { + dprintk(VIDC_WARN, "Skipping PC\n"); + goto skip_power_off; + } + + rc = __prepare_pc(device); + if (rc) { + dprintk(VIDC_WARN, "Failed PC %d\n", rc); + goto skip_power_off; + } + + while (count < max_tries) { + wfi_status = __read_register(device, + VIDC_WRAPPER_CPU_STATUS); + pc_ready = __read_register(device, + VIDC_CPU_CS_SCIACMDARG0); + if ((wfi_status & BIT(0)) && (pc_ready & + VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY)) + break; + usleep_range(1000, 1500); + count++; + } + + if (count == max_tries) { + dprintk(VIDC_ERR, + "Skip PC. Core is not in right state (%#x, %#x)\n", + wfi_status, pc_ready); + goto skip_power_off; + } + } + + rc = __suspend(device); + if (rc) + dprintk(VIDC_ERR, "Failed venus power off\n"); + + /* Cancel pending delayed works if any */ + cancel_delayed_work(&venus_hfi_pm_work); + device->skip_pc_count = 0; + + mutex_unlock(&device->lock); + return; + +skip_power_off: + device->skip_pc_count++; + dprintk(VIDC_WARN, "Skip PC(%d, %#x, %#x, %#x)\n", + device->skip_pc_count, wfi_status, idle_status, pc_ready); + queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, + msecs_to_jiffies(msm_vidc_pwr_collapse_delay)); +exit: + mutex_unlock(&device->lock); +} + +static void __dump_venus_debug_registers(struct venus_hfi_device *device) +{ + u32 reg; + + dprintk(VIDC_ERR, "Dumping Venus registers...\n"); + reg = __read_register(device, VENUS_VBIF_XIN_HALT_CTRL1); + dprintk(VIDC_ERR, "VENUS_VBIF_XIN_HALT_CTRL1: 0x%x\n", reg); + + reg = __read_register(device, + VIDC_VENUS_WRAPPER_MMCC_VENUS0_POWER_STATUS); + dprintk(VIDC_ERR, + "VIDC_VENUS_WRAPPER_MMCC_VENUS0_POWER_STATUS: 0x%x\n", reg); + + reg = __read_register(device, VIDC_WRAPPER_CPU_STATUS); + dprintk(VIDC_ERR, "VIDC_WRAPPER_CPU_STATUS: 0x%x\n", reg); + + reg = __read_register(device, VIDC_CPU_CS_SCIACMDARG0); + dprintk(VIDC_ERR, "VIDC_CPU_CS_SCIACMDARG0: 0x%x\n", reg); +} + +static void __process_sys_error(struct venus_hfi_device *device) +{ + struct hfi_sfr_struct *vsfr = NULL; + + /* Once SYS_ERROR received from HW, it is safe to halt the AXI. + * With SYS_ERROR, Venus FW may have crashed and HW might be + * active and causing unnecessary transactions. Hence it is + * safe to stop all AXI transactions from venus sub-system. + */ + if (__halt_axi(device)) + dprintk(VIDC_WARN, "Failed to halt AXI after SYS_ERROR\n"); + + vsfr = (struct hfi_sfr_struct *)device->sfr.align_virtual_addr; + if (vsfr) { + void *p = memchr(vsfr->rg_data, '\0', vsfr->bufSize); + /* SFR isn't guaranteed to be NULL terminated + * since SYS_ERROR indicates that Venus is in the + * process of crashing. + */ + if (p == NULL) + vsfr->rg_data[vsfr->bufSize - 1] = '\0'; + + dprintk(VIDC_ERR, "SFR Message from FW: %s\n", + vsfr->rg_data); + } +} + +static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet) +{ + bool local_packet = false; + + if (!device) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return; + } + + if (!packet) { + packet = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_KERNEL); + if (!packet) { + dprintk(VIDC_ERR, "In %s() Fail to allocate mem\n", + __func__); + return; + } + + local_packet = true; + } + +#define SKIP_INVALID_PKT(pkt_size, payload_size, pkt_hdr_size) ({ \ + if (pkt_size < pkt_hdr_size || \ + payload_size < MIN_PAYLOAD_SIZE || \ + payload_size > \ + (pkt_size - pkt_hdr_size + sizeof(u8))) { \ + dprintk(VIDC_ERR, \ + "%s: invalid msg size - %d\n", \ + __func__, pkt->msg_size); \ + continue; \ + } \ + }) + + while (!__iface_dbgq_read(device, packet)) { + struct hfi_packet_header *pkt = + (struct hfi_packet_header *) packet; + + if (pkt->size < sizeof(struct hfi_packet_header)) { + dprintk(VIDC_ERR, "Invalid pkt size - %s\n", + __func__); + continue; + } + + if (pkt->packet_type == HFI_MSG_SYS_COV) { + struct hfi_msg_sys_coverage_packet *pkt = + (struct hfi_msg_sys_coverage_packet *) packet; + int stm_size = 0; + + SKIP_INVALID_PKT(pkt->size, + pkt->msg_size, sizeof(*pkt)); + + stm_size = stm_log_inv_ts(0, 0, + pkt->rg_msg_data, pkt->msg_size); + if (stm_size == 0) + dprintk(VIDC_ERR, + "In %s, stm_log returned size of 0\n", + __func__); + + } else if (pkt->packet_type == HFI_MSG_SYS_DEBUG) { + struct hfi_msg_sys_debug_packet *pkt = + (struct hfi_msg_sys_debug_packet *) packet; + + SKIP_INVALID_PKT(pkt->size, + pkt->msg_size, sizeof(*pkt)); + + pkt->rg_msg_data[pkt->msg_size-1] = '\0'; + dprintk(VIDC_FW, "%s", pkt->rg_msg_data); + } + } +#undef SKIP_INVALID_PKT + + if (local_packet) + kfree(packet); +} + +static struct hal_session *__get_session(struct venus_hfi_device *device, + u32 session_id) +{ + struct hal_session *temp = NULL; + + list_for_each_entry(temp, &device->sess_head, list) { + if (session_id == hash32_ptr(temp)) + return temp; + } + + return NULL; +} + +static int __response_handler(struct venus_hfi_device *device) +{ + struct msm_vidc_cb_info *packets; + int packet_count = 0; + u8 *raw_packet = NULL; + bool requeue_pm_work = true; + + if (!device || device->state != VENUS_STATE_INIT) + return 0; + + packets = device->response_pkt; + + raw_packet = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_KERNEL); + if (!raw_packet || !packets) { + dprintk(VIDC_ERR, "%s: Failed to allocate memory\n", __func__); + kfree(raw_packet); + return 0; + } + + if (device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK) { + struct hfi_sfr_struct *vsfr = (struct hfi_sfr_struct *) + device->sfr.align_virtual_addr; + struct msm_vidc_cb_info info = { + .response_type = HAL_SYS_WATCHDOG_TIMEOUT, + .response.cmd = { + .device_id = device->device_id, + } + }; + + if (vsfr) + dprintk(VIDC_ERR, "SFR Message from FW: %s\n", + vsfr->rg_data); + + __dump_venus_debug_registers(device); + dprintk(VIDC_ERR, "Received watchdog timeout\n"); + packets[packet_count++] = info; + goto exit; + } + + /* Bleed the msg queue dry of packets */ + while (!__iface_msgq_read(device, raw_packet)) { + void **session_id = NULL; + struct msm_vidc_cb_info *info = &packets[packet_count++]; + struct vidc_hal_sys_init_done sys_init_done = {0}; + int rc = 0; + + rc = hfi_process_msg_packet(device->device_id, + (struct vidc_hal_msg_pkt_hdr *)raw_packet, info); + if (rc) { + dprintk(VIDC_WARN, + "Corrupt/unknown packet found, discarding\n"); + --packet_count; + continue; + } + + /* Process the packet types that we're interested in */ + switch (info->response_type) { + case HAL_SYS_ERROR: + __dump_venus_debug_registers(device); + __process_sys_error(device); + break; + case HAL_SYS_RELEASE_RESOURCE_DONE: + dprintk(VIDC_DBG, "Received SYS_RELEASE_RESOURCE\n"); + complete(&release_resources_done); + break; + case HAL_SYS_INIT_DONE: + dprintk(VIDC_DBG, "Received SYS_INIT_DONE\n"); + /* Video driver intentionally does not unset + * IMEM on venus to simplify power collapse. + */ + if (__set_imem(device, &device->resources.imem)) + dprintk(VIDC_WARN, + "Failed to set IMEM. Performance will be impacted\n"); + sys_init_done.capabilities = + device->sys_init_capabilities; + hfi_process_sys_init_done_prop_read( + (struct hfi_msg_sys_init_done_packet *) + raw_packet, &sys_init_done); + info->response.cmd.data.sys_init_done = sys_init_done; + break; + case HAL_SESSION_LOAD_RESOURCE_DONE: + /* + * Work around for H/W bug, need to re-program these + * registers as part of a handshake agreement with the + * firmware. This strictly only needs to be done for + * decoder secure sessions, but there's no harm in doing + * so for all sessions as it's at worst a NO-OP. + */ + __set_threshold_registers(device); + break; + default: + break; + } + + /* For session-related packets, validate session */ + switch (info->response_type) { + case HAL_SESSION_LOAD_RESOURCE_DONE: + case HAL_SESSION_INIT_DONE: + case HAL_SESSION_END_DONE: + case HAL_SESSION_ABORT_DONE: + case HAL_SESSION_START_DONE: + case HAL_SESSION_STOP_DONE: + case HAL_SESSION_FLUSH_DONE: + case HAL_SESSION_SUSPEND_DONE: + case HAL_SESSION_RESUME_DONE: + case HAL_SESSION_SET_PROP_DONE: + case HAL_SESSION_GET_PROP_DONE: + case HAL_SESSION_PARSE_SEQ_HDR_DONE: + case HAL_SESSION_RELEASE_BUFFER_DONE: + case HAL_SESSION_RELEASE_RESOURCE_DONE: + case HAL_SESSION_PROPERTY_INFO: + session_id = &info->response.cmd.session_id; + break; + case HAL_SESSION_ERROR: + case HAL_SESSION_GET_SEQ_HDR_DONE: + case HAL_SESSION_ETB_DONE: + case HAL_SESSION_FTB_DONE: + session_id = &info->response.data.session_id; + break; + case HAL_SESSION_EVENT_CHANGE: + session_id = &info->response.event.session_id; + break; + case HAL_RESPONSE_UNUSED: + default: + session_id = NULL; + break; + } + + /* + * hfi_process_msg_packet provides a session_id that's a hashed + * value of struct hal_session, we need to coerce the hashed + * value back to pointer that we can use. Ideally, hfi_process\ + * _msg_packet should take care of this, but it doesn't have + * required information for it + */ + if (session_id) { + struct hal_session *session = NULL; + + if (upper_32_bits((uintptr_t)*session_id) != 0) { + dprintk(VIDC_WARN, + "Upper 32 bits of session_id != 0\n"); + WARN_ON(VIDC_DBG_WARN_ENABLE); + } + session = __get_session(device, + (u32)(uintptr_t)*session_id); + if (!session) { + dprintk(VIDC_ERR, + "Received a packet (%#x) for an unrecognized session (%pK), discarding\n", + info->response_type, + *session_id); + --packet_count; + continue; + } + + *session_id = session->session_id; + } + + if (packet_count >= max_packets) { + dprintk(VIDC_WARN, + "Too many packets in message queue to handle at once, deferring read\n"); + break; + } + /* do not read packets after sys error packet */ + if (info->response_type == HAL_SYS_ERROR) + break; + } + + if (requeue_pm_work && device->res->sw_power_collapsible) { + cancel_delayed_work(&venus_hfi_pm_work); + if (!queue_delayed_work(device->venus_pm_workq, + &venus_hfi_pm_work, + msecs_to_jiffies(msm_vidc_pwr_collapse_delay))) { + dprintk(VIDC_ERR, "PM work already scheduled\n"); + } + } + +exit: + __flush_debug_queue(device, raw_packet); + + kfree(raw_packet); + return packet_count; +} + +static void venus_hfi_core_work_handler(struct work_struct *work) +{ + struct venus_hfi_device *device = list_first_entry( + &hal_ctxt.dev_head, struct venus_hfi_device, list); + int num_responses = 0, i = 0; + + mutex_lock(&device->lock); + + dprintk(VIDC_INFO, "Handling interrupt\n"); + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - Core not in init state\n", __func__); + goto err_no_work; + } + + if (!device->callback) { + dprintk(VIDC_ERR, "No interrupt callback function: %pK\n", + device); + goto err_no_work; + } + + if (__resume(device)) { + dprintk(VIDC_ERR, "%s: Power enable failed\n", __func__); + goto err_no_work; + } + + __core_clear_interrupt(device); + num_responses = __response_handler(device); + +err_no_work: + /* We need re-enable the irq which was disabled in ISR handler */ + if (!(device->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) + enable_irq(device->hal_data->irq); + + mutex_unlock(&device->lock); + + /* + * Issue the callbacks outside of the locked contex to preserve + * re-entrancy. + */ + + for (i = 0; !IS_ERR_OR_NULL(device->response_pkt) && + i < num_responses; ++i) { + struct msm_vidc_cb_info *r = &device->response_pkt[i]; + + if (!__core_in_valid_state(device)) { + dprintk(VIDC_ERR, + "Ignore responses from %d to %d as device is in invalid state", + (i + 1), num_responses); + break; + } + device->callback(r->response_type, &r->response); + } + + /* + * XXX: Don't add any code beyond here. Reacquiring locks after release + * it above doesn't guarantee the atomicity that we're aiming for. + */ +} + +static DECLARE_WORK(venus_hfi_work, venus_hfi_core_work_handler); + +static irqreturn_t venus_hfi_isr(int irq, void *dev) +{ + struct venus_hfi_device *device = dev; + + dprintk(VIDC_INFO, "Received an interrupt %d\n", irq); + disable_irq_nosync(irq); + queue_work(device->vidc_workq, &venus_hfi_work); + return IRQ_HANDLED; +} + +static int __init_regs_and_interrupts(struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + struct hal_data *hal = NULL; + int rc = 0; + + rc = __check_core_registered(hal_ctxt, res->firmware_base, + (u8 *)(uintptr_t)res->register_base, + res->register_size, res->irq); + if (!rc) { + dprintk(VIDC_ERR, "Core present/Already added\n"); + rc = -EEXIST; + goto err_core_init; + } + + dprintk(VIDC_DBG, "HAL_DATA will be assigned now\n"); + hal = kzalloc(sizeof(struct hal_data), GFP_KERNEL); + if (!hal) { + dprintk(VIDC_ERR, "Failed to alloc\n"); + rc = -ENOMEM; + goto err_core_init; + } + + hal->irq = res->irq; + hal->firmware_base = res->firmware_base; + hal->register_base = devm_ioremap_nocache(&res->pdev->dev, + res->register_base, res->register_size); + hal->register_size = res->register_size; + if (!hal->register_base) { + dprintk(VIDC_ERR, + "could not map reg addr %pa of size %d\n", + &res->register_base, res->register_size); + goto error_irq_fail; + } + + device->hal_data = hal; + rc = request_irq(res->irq, venus_hfi_isr, IRQF_TRIGGER_HIGH, + "msm_vidc", device); + if (unlikely(rc)) { + dprintk(VIDC_ERR, "() :request_irq failed\n"); + goto error_irq_fail; + } + + disable_irq_nosync(res->irq); + dprintk(VIDC_INFO, + "firmware_base = %pa, register_base = %pa, register_size = %d\n", + &res->firmware_base, &res->register_base, + res->register_size); + return rc; + +error_irq_fail: + kfree(hal); +err_core_init: + return rc; + +} + +static inline void __deinit_clocks(struct venus_hfi_device *device) +{ + struct clock_info *cl; + + device->clk_freq = 0; + venus_hfi_for_each_clock_reverse(device, cl) { + if (cl->clk) { + clk_put(cl->clk); + cl->clk = NULL; + } + } +} + +static inline int __init_clocks(struct venus_hfi_device *device) +{ + int rc = 0; + struct clock_info *cl = NULL; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } + + venus_hfi_for_each_clock(device, cl) { + int i = 0; + + dprintk(VIDC_DBG, "%s: scalable? %d, count %d\n", + cl->name, cl->has_scaling, cl->count); + for (i = 0; i < cl->count; ++i) { + dprintk(VIDC_DBG, + "\tload = %d, freq = %d codecs supported %#x\n", + cl->load_freq_tbl[i].load, + cl->load_freq_tbl[i].freq, + cl->load_freq_tbl[i].supported_codecs); + } + } + + venus_hfi_for_each_clock(device, cl) { + if (!cl->clk) { + cl->clk = clk_get(&device->res->pdev->dev, cl->name); + if (IS_ERR_OR_NULL(cl->clk)) { + dprintk(VIDC_ERR, + "Failed to get clock: %s\n", cl->name); + rc = PTR_ERR(cl->clk) ?: -EINVAL; + cl->clk = NULL; + goto err_clk_get; + } + } + } + device->clk_freq = 0; + return 0; + +err_clk_get: + __deinit_clocks(device); + return rc; +} + + +static inline void __disable_unprepare_clks(struct venus_hfi_device *device) +{ + struct clock_info *cl; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return; + } + + venus_hfi_for_each_clock(device, cl) { + usleep_range(100, 500); + dprintk(VIDC_DBG, "Clock: %s disable and unprepare\n", + cl->name); + clk_disable_unprepare(cl->clk); + } +} + +static inline int __prepare_enable_clks(struct venus_hfi_device *device) +{ + struct clock_info *cl = NULL, *cl_fail = NULL; + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } + + venus_hfi_for_each_clock(device, cl) { + /* + * For the clocks we control, set the rate prior to preparing + * them. Since we don't really have a load at this point, scale + * it to the lowest frequency possible + */ + if (cl->has_scaling) + clk_set_rate(cl->clk, clk_round_rate(cl->clk, 0)); + + rc = clk_prepare_enable(cl->clk); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks\n"); + cl_fail = cl; + goto fail_clk_enable; + } + + dprintk(VIDC_DBG, "Clock: %s prepared and enabled\n", cl->name); + } + + __write_register(device, VIDC_WRAPPER_CLOCK_CONFIG, 0); + __write_register(device, VIDC_WRAPPER_CPU_CLOCK_CONFIG, 0); + return rc; + +fail_clk_enable: + venus_hfi_for_each_clock(device, cl) { + if (cl_fail == cl) + break; + usleep_range(100, 500); + dprintk(VIDC_ERR, "Clock: %s disable and unprepare\n", + cl->name); + clk_disable_unprepare(cl->clk); + } + + return rc; +} + +static void __deinit_bus(struct venus_hfi_device *device) +{ + struct bus_info *bus = NULL; + + if (!device) + return; + + kfree(device->bus_vote.data); + device->bus_vote = DEFAULT_BUS_VOTE; + + venus_hfi_for_each_bus_reverse(device, bus) { + msm_bus_scale_unregister(bus->client); + bus->client = NULL; + } +} + +static int __init_bus(struct venus_hfi_device *device) +{ + struct bus_info *bus = NULL; + int rc = 0; + + if (!device) + return -EINVAL; + + venus_hfi_for_each_bus(device, bus) { + bus->client = msm_bus_scale_register(bus->master, bus->slave, + bus->name, false); + if (IS_ERR_OR_NULL(bus->client)) { + rc = PTR_ERR(bus->client) ?: -EBADHANDLE; + dprintk(VIDC_ERR, "Failed to register bus %s: %d\n", + bus->name, rc); + bus->client = NULL; + goto err_add_dev; + } + } + + device->bus_vote = DEFAULT_BUS_VOTE; + return 0; + +err_add_dev: + __deinit_bus(device); + return rc; +} + +static void __deinit_regulators(struct venus_hfi_device *device) +{ + struct regulator_info *rinfo = NULL; + + venus_hfi_for_each_regulator_reverse(device, rinfo) { + if (rinfo->regulator) { + regulator_put(rinfo->regulator); + rinfo->regulator = NULL; + } + } +} + +static int __init_regulators(struct venus_hfi_device *device) +{ + int rc = 0; + struct regulator_info *rinfo = NULL; + + venus_hfi_for_each_regulator(device, rinfo) { + rinfo->regulator = regulator_get(&device->res->pdev->dev, + rinfo->name); + if (IS_ERR_OR_NULL(rinfo->regulator)) { + rc = PTR_ERR(rinfo->regulator) ?: -EBADHANDLE; + dprintk(VIDC_ERR, "Failed to get regulator: %s\n", + rinfo->name); + rinfo->regulator = NULL; + goto err_reg_get; + } + } + + return 0; + +err_reg_get: + __deinit_regulators(device); + return rc; +} + +static int __init_resources(struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + + rc = __init_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to get all regulators\n"); + return -ENODEV; + } + + rc = __init_clocks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init clocks\n"); + rc = -ENODEV; + goto err_init_clocks; + } + + rc = __init_bus(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init bus: %d\n", rc); + goto err_init_bus; + } + + device->sys_init_capabilities = + kzalloc(sizeof(struct msm_vidc_capability) + * VIDC_MAX_SESSIONS, GFP_KERNEL); + + return rc; + +err_init_bus: + __deinit_clocks(device); +err_init_clocks: + __deinit_regulators(device); + return rc; +} + +static int __early_init_resources(struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + + rc = __init_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to get all regulators\n"); + return -ENODEV; + } + + rc = __init_clocks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init clocks\n"); + rc = -ENODEV; + goto err_init_clocks; + } + + return rc; + +err_init_clocks: + __deinit_regulators(device); + return rc; +} + + +static void __deinit_resources(struct venus_hfi_device *device) +{ + __deinit_bus(device); + __deinit_clocks(device); + __deinit_regulators(device); + kfree(device->sys_init_capabilities); + device->sys_init_capabilities = NULL; +} + +static void __early_deinit_resources(struct venus_hfi_device *device) +{ + __deinit_clocks(device); + __deinit_regulators(device); +} + +static int __protect_cp_mem(struct venus_hfi_device *device) +{ + struct tzbsp_memprot memprot; + unsigned int resp = 0; + int rc = 0; + struct context_bank_info *cb; + struct scm_desc desc = {0}; + + if (!device) + return -EINVAL; + + memprot.cp_start = 0x0; + memprot.cp_size = 0x0; + memprot.cp_nonpixel_start = 0x0; + memprot.cp_nonpixel_size = 0x0; + + list_for_each_entry(cb, &device->res->context_banks, list) { + if (!strcmp(cb->name, "venus_ns")) { + desc.args[1] = memprot.cp_size = + cb->addr_range.start; + dprintk(VIDC_DBG, "%s memprot.cp_size: %#x\n", + __func__, memprot.cp_size); + } + + if (!strcmp(cb->name, "venus_sec_non_pixel")) { + desc.args[2] = memprot.cp_nonpixel_start = + cb->addr_range.start; + desc.args[3] = memprot.cp_nonpixel_size = + cb->addr_range.size; + dprintk(VIDC_DBG, + "%s memprot.cp_nonpixel_start: %#x size: %#x\n", + __func__, memprot.cp_nonpixel_start, + memprot.cp_nonpixel_size); + } + } + desc.arginfo = SCM_ARGS(4); + rc = scm_call2(SCM_SIP_FNID(SCM_SVC_MP, + TZBSP_MEM_PROTECT_VIDEO_VAR), &desc); + resp = desc.ret[0]; + if (rc) { + dprintk(VIDC_ERR, "Failed to protect memory(%d) response: %d\n", + rc, resp); + } + + trace_venus_hfi_var_done( + memprot.cp_start, memprot.cp_size, + memprot.cp_nonpixel_start, memprot.cp_nonpixel_size); + return rc; +} + +static int __disable_regulator(struct regulator_info *rinfo) +{ + int rc = 0; + + dprintk(VIDC_DBG, "Disabling regulator %s\n", rinfo->name); + + /* + * This call is needed. Driver needs to acquire the control back + * from HW in order to disable the regualtor. Else the behavior + * is unknown. + */ + + rc = __acquire_regulator(rinfo); + if (rc) { + /* This is somewhat fatal, but nothing we can do + * about it. We can't disable the regulator w/o + * getting it back under s/w control + */ + dprintk(VIDC_WARN, + "Failed to acquire control on %s\n", + rinfo->name); + + goto disable_regulator_failed; + } + + rc = regulator_disable(rinfo->regulator); + if (rc) { + dprintk(VIDC_WARN, + "Failed to disable %s: %d\n", + rinfo->name, rc); + goto disable_regulator_failed; + } + + return 0; +disable_regulator_failed: + + /* Bring attention to this issue */ + WARN_ON(VIDC_DBG_WARN_ENABLE); + return rc; +} + +static int __enable_hw_power_collapse(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!msm_vidc_fw_low_power_mode) { + dprintk(VIDC_DBG, "Not enabling hardware power collapse\n"); + return 0; + } + + rc = __hand_off_regulators(device); + if (rc) + dprintk(VIDC_WARN, + "%s : Failed to enable HW power collapse %d\n", + __func__, rc); + return rc; +} + +static int __enable_regulators(struct venus_hfi_device *device) +{ + int rc = 0, c = 0; + struct regulator_info *rinfo; + + dprintk(VIDC_DBG, "Enabling regulators\n"); + + venus_hfi_for_each_regulator(device, rinfo) { + rc = regulator_enable(rinfo->regulator); + if (rc) { + dprintk(VIDC_ERR, + "Failed to enable %s: %d\n", + rinfo->name, rc); + goto err_reg_enable_failed; + } + + dprintk(VIDC_DBG, "Enabled regulator %s\n", + rinfo->name); + c++; + } + + return 0; + +err_reg_enable_failed: + venus_hfi_for_each_regulator_reverse_continue(device, rinfo, c) + __disable_regulator(rinfo); + + return rc; +} + +static int __disable_regulators(struct venus_hfi_device *device) +{ + struct regulator_info *rinfo; + + dprintk(VIDC_DBG, "Disabling regulators\n"); + + venus_hfi_for_each_regulator_reverse(device, rinfo) + __disable_regulator(rinfo); + + return 0; + +} + +static int __venus_power_on(struct venus_hfi_device *device) +{ + int rc = 0; + + if (device->power_enabled) + return 0; + + device->power_enabled = true; + /* Vote for all hardware resources */ + rc = __vote_buses(device, device->bus_vote.data, + device->bus_vote.data_count); + if (rc) { + dprintk(VIDC_ERR, "Failed to vote buses, err: %d\n", rc); + goto fail_vote_buses; + } + + rc = __alloc_imem(device, device->res->imem_size); + if (rc) { + dprintk(VIDC_ERR, "Failed to allocate IMEM\n"); + goto fail_alloc_imem; + } + + rc = __enable_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable GDSC, err = %d\n", rc); + goto fail_enable_gdsc; + } + + rc = __prepare_enable_clks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc); + goto fail_enable_clks; + } + + rc = __scale_clocks(device, 0, NULL, 0); + if (rc) { + dprintk(VIDC_WARN, + "Failed to scale clocks, performance might be affected\n"); + rc = 0; + } + __write_register(device, VIDC_WRAPPER_INTR_MASK, + VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK); + device->intr_status = 0; + enable_irq(device->hal_data->irq); + + /* + * Hand off control of regulators to h/w _after_ enabling clocks. + * Note that the GDSC will turn off when switching from normal + * (s/w triggered) to fast (HW triggered) unless the h/w vote is + * present. Since Venus isn't up yet, the GDSC will be off briefly. + */ + if (__enable_hw_power_collapse(device)) + dprintk(VIDC_ERR, "Failed to enabled inter-frame PC\n"); + + return rc; + +fail_enable_clks: + __disable_regulators(device); +fail_enable_gdsc: + __free_imem(device); +fail_alloc_imem: + __unvote_buses(device); +fail_vote_buses: + device->power_enabled = false; + return rc; +} + +static int __venus_early_power_on(struct venus_hfi_device *device) +{ + int rc = 0; + + if (device->power_enabled) + return 0; + + device->power_enabled = true; + + rc = __enable_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable GDSC, err = %d\n", rc); + goto fail_enable_gdsc; + } + + rc = __prepare_enable_clks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc); + goto fail_enable_clks; + } + + rc = __scale_clocks(device, 0, NULL, 0); + if (rc) { + dprintk(VIDC_WARN, + "Failed to scale clocks, performance might be affected\n"); + rc = 0; + } + return rc; + +fail_enable_clks: + __disable_regulators(device); +fail_enable_gdsc: + return rc; +} + +static void __venus_power_off(struct venus_hfi_device *device, bool halt_axi) +{ + if (!device->power_enabled) + return; + + if (!(device->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) + disable_irq_nosync(device->hal_data->irq); + device->intr_status = 0; + + /* Halt the AXI to make sure there are no pending transactions. + * Clocks should be unprepared after making sure axi is halted. + */ + if (halt_axi && __halt_axi(device)) + dprintk(VIDC_WARN, "Failed to halt AXI\n"); + + __disable_unprepare_clks(device); + if (__disable_regulators(device)) + dprintk(VIDC_WARN, "Failed to disable regulators\n"); + + __free_imem(device); + + if (__unvote_buses(device)) + dprintk(VIDC_WARN, "Failed to unvote for buses\n"); + device->power_enabled = false; +} + +static void __venus_early_power_off(struct venus_hfi_device *device) +{ + __disable_unprepare_clks(device); + if (__disable_regulators(device)) + dprintk(VIDC_WARN, "Failed to disable regulators\n"); + device->power_enabled = false; +} + +static inline int __suspend(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } else if (!device->power_enabled) { + dprintk(VIDC_DBG, "Power already disabled\n"); + return 0; + } + + dprintk(VIDC_DBG, "Entering power collapse\n"); + + if (device->res->pm_qos_latency_us && + pm_qos_request_active(&device->qos)) + pm_qos_remove_request(&device->qos); + + rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); + if (rc) { + dprintk(VIDC_WARN, "Failed to suspend video core %d\n", rc); + goto err_tzbsp_suspend; + } + + __venus_power_off(device, true); + dprintk(VIDC_INFO, "Venus power collapsed\n"); + return rc; + +err_tzbsp_suspend: + return rc; +} + +static inline int __resume(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %pK\n", device); + return -EINVAL; + } else if (device->power_enabled) { + dprintk(VIDC_DBG, "Power is already enabled\n"); + goto exit; + } else if (!__core_in_valid_state(device)) { + dprintk(VIDC_DBG, "venus_hfi_device in deinit state."); + return -EINVAL; + } + + dprintk(VIDC_DBG, "Resuming from power collapse\n"); + rc = __venus_power_on(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to power on venus\n"); + goto err_venus_power_on; + } + + /* Reboot the firmware */ + rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME); + if (rc) { + dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc); + goto err_set_video_state; + } + + /* + * Re-program all of the registers that get reset as a result of + * regulator_disable() and _enable() + */ + __set_registers(device); + __setup_ucregion_memory_map(device); + /* Wait for boot completion */ + rc = __boot_firmware(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to reset venus core\n"); + goto err_reset_core; + } + + /* + * Work around for H/W bug, need to reprogram these registers once + * firmware is out reset + */ + __set_threshold_registers(device); + + if (device->res->pm_qos_latency_us) { +#ifdef CONFIG_SMP + device->qos.type = PM_QOS_REQ_AFFINE_IRQ; + device->qos.irq = device->hal_data->irq; +#endif + pm_qos_add_request(&device->qos, PM_QOS_CPU_DMA_LATENCY, + device->res->pm_qos_latency_us); + } + dprintk(VIDC_INFO, "Resumed from power collapse\n"); +exit: + device->skip_pc_count = 0; + return rc; +err_reset_core: + __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); +err_set_video_state: + __venus_power_off(device, true); +err_venus_power_on: + dprintk(VIDC_ERR, "Failed to resume from power collapse\n"); + return rc; +} + +static int __load_fw(struct venus_hfi_device *device) +{ + int rc = 0; + /* Initialize resources */ + rc = __init_resources(device, device->res); + if (rc) { + dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); + goto fail_init_res; + } + + rc = __initialize_packetization(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to initialize packetization\n"); + goto fail_init_pkt; + } + trace_msm_v4l2_vidc_fw_load_start("msm_v4l2_vidc venus_fw load start"); + + rc = __venus_power_on(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to power on venus in in load_fw\n"); + goto fail_venus_power_on; + } + + if ((!device->res->use_non_secure_pil && !device->res->firmware_base) + || device->res->use_non_secure_pil) { + if (!device->resources.fw.cookie) + device->resources.fw.cookie = + subsystem_get_with_fwname("venus", + device->res->fw_name); + + if (IS_ERR_OR_NULL(device->resources.fw.cookie)) { + dprintk(VIDC_ERR, "Failed to download firmware\n"); + device->resources.fw.cookie = NULL; + rc = -ENOMEM; + goto fail_load_fw; + } + } + + if (!device->res->use_non_secure_pil && !device->res->firmware_base) { + rc = __protect_cp_mem(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to protect memory\n"); + goto fail_protect_mem; + } + } + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end"); + return rc; +fail_protect_mem: + if (device->resources.fw.cookie) + subsystem_put(device->resources.fw.cookie); + device->resources.fw.cookie = NULL; +fail_load_fw: + __venus_power_off(device, true); +fail_venus_power_on: +fail_init_pkt: + __deinit_resources(device); +fail_init_res: + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end"); + return rc; +} + +static void __unload_fw(struct venus_hfi_device *device) +{ + if (!device->resources.fw.cookie) + return; + + cancel_delayed_work(&venus_hfi_pm_work); + if (device->state != VENUS_STATE_DEINIT) + flush_workqueue(device->venus_pm_workq); + + __vote_buses(device, NULL, 0); + subsystem_put(device->resources.fw.cookie); + __interface_queues_release(device); + __venus_power_off(device, false); + device->resources.fw.cookie = NULL; + __deinit_resources(device); +} + +static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info) +{ + int i = 0, j = 0; + struct venus_hfi_device *device = dev; + size_t smem_block_size = 0; + u8 *smem_table_ptr; + char version[VENUS_VERSION_LENGTH] = ""; + const u32 smem_image_index_venus = 14 * 128; + + if (!device || !fw_info) { + dprintk(VIDC_ERR, + "%s Invalid parameter: device = %pK fw_info = %pK\n", + __func__, device, fw_info); + return -EINVAL; + } + + mutex_lock(&device->lock); + + smem_table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMAGE_VERSION_TABLE, + &smem_block_size); + if (smem_table_ptr && + ((smem_image_index_venus + + VENUS_VERSION_LENGTH) <= smem_block_size)) + memcpy(version, + smem_table_ptr + smem_image_index_venus, + VENUS_VERSION_LENGTH); + + while (version[i++] != 'V' && i < VENUS_VERSION_LENGTH) + ; + + if (i == VENUS_VERSION_LENGTH - 1) { + dprintk(VIDC_WARN, "Venus version string is not proper\n"); + fw_info->version[0] = '\0'; + goto fail_version_string; + } + + for (i--; i < VENUS_VERSION_LENGTH && j < VENUS_VERSION_LENGTH - 1; i++) + fw_info->version[j++] = version[i]; + fw_info->version[j] = '\0'; + +fail_version_string: + dprintk(VIDC_DBG, "F/W version retrieved : %s\n", fw_info->version); + fw_info->base_addr = device->hal_data->firmware_base; + fw_info->register_base = device->res->register_base; + fw_info->register_size = device->hal_data->register_size; + fw_info->irq = device->hal_data->irq; + + mutex_unlock(&device->lock); + return 0; +} + +static int venus_hfi_get_core_capabilities(void *dev) +{ + struct venus_hfi_device *device = dev; + int rc = 0; + + if (!device) + return -EINVAL; + + mutex_lock(&device->lock); + + rc = HAL_VIDEO_ENCODER_ROTATION_CAPABILITY | + HAL_VIDEO_ENCODER_SCALING_CAPABILITY | + HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY | + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY; + + mutex_unlock(&device->lock); + + return rc; +} + +static int __initialize_packetization(struct venus_hfi_device *device) +{ + int rc = 0; + const char *hfi_version; + + if (!device || !device->res) { + dprintk(VIDC_ERR, "%s - invalid param\n", __func__); + return -EINVAL; + } + + hfi_version = device->res->hfi_version; + + if (!hfi_version) { + device->packetization_type = HFI_PACKETIZATION_LEGACY; + } else if (!strcmp(hfi_version, "3xx")) { + device->packetization_type = HFI_PACKETIZATION_3XX; + } else { + dprintk(VIDC_ERR, "Unsupported hfi version\n"); + return -EINVAL; + } + + device->pkt_ops = hfi_get_pkt_ops_handle(device->packetization_type); + if (!device->pkt_ops) { + rc = -EINVAL; + dprintk(VIDC_ERR, "Failed to get pkt_ops handle\n"); + } + + return rc; +} + +static struct venus_hfi_device *__add_device(u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + struct venus_hfi_device *hdevice = NULL; + int rc = 0; + + if (!res || !callback) { + dprintk(VIDC_ERR, "Invalid Parameters\n"); + return NULL; + } + + dprintk(VIDC_INFO, "entered , device_id: %d\n", device_id); + + hdevice = kzalloc(sizeof(struct venus_hfi_device), GFP_KERNEL); + if (!hdevice) { + dprintk(VIDC_ERR, "failed to allocate new device\n"); + goto exit; + } + + hdevice->response_pkt = kmalloc_array(max_packets, + sizeof(*hdevice->response_pkt), GFP_KERNEL); + if (!hdevice->response_pkt) { + dprintk(VIDC_ERR, "failed to allocate response_pkt\n"); + goto err_cleanup; + } + + rc = __init_regs_and_interrupts(hdevice, res); + if (rc) + goto err_cleanup; + + hdevice->res = res; + hdevice->device_id = device_id; + hdevice->callback = callback; + + hdevice->vidc_workq = create_singlethread_workqueue( + "msm_vidc_workerq_venus"); + if (!hdevice->vidc_workq) { + dprintk(VIDC_ERR, ": create vidc workq failed\n"); + goto err_cleanup; + } + + hdevice->venus_pm_workq = create_singlethread_workqueue( + "pm_workerq_venus"); + if (!hdevice->venus_pm_workq) { + dprintk(VIDC_ERR, ": create pm workq failed\n"); + goto err_cleanup; + } + + if (!hal_ctxt.dev_count) + INIT_LIST_HEAD(&hal_ctxt.dev_head); + + mutex_init(&hdevice->lock); + INIT_LIST_HEAD(&hdevice->list); + INIT_LIST_HEAD(&hdevice->sess_head); + list_add_tail(&hdevice->list, &hal_ctxt.dev_head); + hal_ctxt.dev_count++; + + return hdevice; + +err_cleanup: + if (hdevice->vidc_workq) + destroy_workqueue(hdevice->vidc_workq); + kfree(hdevice->response_pkt); + kfree(hdevice); +exit: + return NULL; +} + +static struct venus_hfi_device *__get_device(u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + if (!res || !callback) { + dprintk(VIDC_ERR, "Invalid params: %pK %pK\n", res, callback); + return NULL; + } + + return __add_device(device_id, res, callback); +} + +void venus_hfi_delete_device(void *device) +{ + struct venus_hfi_device *close, *tmp, *dev; + + if (!device) + return; + + dev = (struct venus_hfi_device *) device; + + mutex_lock(&dev->lock); + mutex_unlock(&dev->lock); + + list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) { + if (close->hal_data->irq == dev->hal_data->irq) { + hal_ctxt.dev_count--; + list_del(&close->list); + destroy_workqueue(close->vidc_workq); + destroy_workqueue(close->venus_pm_workq); + free_irq(dev->hal_data->irq, close); + iounmap(dev->hal_data->register_base); + kfree(close->hal_data); + kfree(close->response_pkt); + kfree(close); + break; + } + } +} + +static int venus_hfi_core_early_init(void *device) +{ + int rc = 0; + struct venus_hfi_device *dev = device; + + mutex_lock(&dev->lock); + rc = __early_init_resources(dev, dev->res); + rc = __venus_early_power_on(device); + mutex_unlock(&dev->lock); + return rc; +} + +static int venus_hfi_core_early_release(void *device) +{ + struct venus_hfi_device *dev = device; + + mutex_lock(&dev->lock); + __venus_early_power_off(dev); + __early_deinit_resources(dev); + mutex_unlock(&dev->lock); + return 0; +} + +static void venus_init_hfi_callbacks(struct hfi_device *hdev) +{ + hdev->core_init = venus_hfi_core_init; + hdev->core_release = venus_hfi_core_release; + hdev->core_early_init = venus_hfi_core_early_init; + hdev->core_early_release = venus_hfi_core_early_release; + hdev->core_ping = venus_hfi_core_ping; + hdev->core_trigger_ssr = venus_hfi_core_trigger_ssr; + hdev->session_init = venus_hfi_session_init; + hdev->session_end = venus_hfi_session_end; + hdev->session_abort = venus_hfi_session_abort; + hdev->session_clean = venus_hfi_session_clean; + hdev->session_set_buffers = venus_hfi_session_set_buffers; + hdev->session_release_buffers = venus_hfi_session_release_buffers; + hdev->session_load_res = venus_hfi_session_load_res; + hdev->session_release_res = venus_hfi_session_release_res; + hdev->session_start = venus_hfi_session_start; + hdev->session_continue = venus_hfi_session_continue; + hdev->session_stop = venus_hfi_session_stop; + hdev->session_etb = venus_hfi_session_etb; + hdev->session_ftb = venus_hfi_session_ftb; + hdev->session_process_batch = venus_hfi_session_process_batch; + hdev->session_parse_seq_hdr = venus_hfi_session_parse_seq_hdr; + hdev->session_get_seq_hdr = venus_hfi_session_get_seq_hdr; + hdev->session_get_buf_req = venus_hfi_session_get_buf_req; + hdev->session_flush = venus_hfi_session_flush; + hdev->session_set_property = venus_hfi_session_set_property; + hdev->session_get_property = venus_hfi_session_get_property; + hdev->scale_clocks = venus_hfi_scale_clocks; + hdev->vote_bus = venus_hfi_vote_buses; + hdev->get_fw_info = venus_hfi_get_fw_info; + hdev->get_core_capabilities = venus_hfi_get_core_capabilities; + hdev->suspend = venus_hfi_suspend; + hdev->get_core_clock_rate = venus_hfi_get_core_clock_rate; + hdev->get_default_properties = venus_hfi_get_default_properties; +} + +int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + int rc = 0; + + if (!hdev || !res || !callback) { + dprintk(VIDC_ERR, "Invalid params: %pK %pK %pK\n", + hdev, res, callback); + rc = -EINVAL; + goto err_venus_hfi_init; + } + + hdev->hfi_device_data = __get_device(device_id, res, callback); + + if (IS_ERR_OR_NULL(hdev->hfi_device_data)) { + rc = PTR_ERR(hdev->hfi_device_data) ?: -EINVAL; + goto err_venus_hfi_init; + } + + venus_init_hfi_callbacks(hdev); + +err_venus_hfi_init: + return rc; +} + diff --git a/drivers/media/platform/msm/vidc_3x/venus_hfi.h b/drivers/media/platform/msm/vidc_3x/venus_hfi.h new file mode 100644 index 000000000000..30e4a1dfdb2d --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/venus_hfi.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2015, 2018-2020 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __H_VENUS_HFI_H__ +#define __H_VENUS_HFI_H__ + +#include +#include +#include +#include +#include +#include "vmem/vmem.h" +#include "vidc_hfi_api.h" +#include "vidc_hfi_helper.h" +#include "vidc_hfi_api.h" +#include "vidc_hfi.h" +#include "msm_vidc_resources.h" +#include "hfi_packetization.h" + +#define HFI_MASK_QHDR_TX_TYPE 0xFF000000 +#define HFI_MASK_QHDR_RX_TYPE 0x00FF0000 +#define HFI_MASK_QHDR_PRI_TYPE 0x0000FF00 +#define HFI_MASK_QHDR_Q_ID_TYPE 0x000000FF +#define HFI_Q_ID_HOST_TO_CTRL_CMD_Q 0x00 +#define HFI_Q_ID_CTRL_TO_HOST_MSG_Q 0x01 +#define HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q 0x02 +#define HFI_MASK_QHDR_STATUS 0x000000FF + +#define VIDC_MAX_UNCOMPRESSED_FMT_PLANES 3 + +#define VIDC_IFACEQ_NUMQ 3 +#define VIDC_IFACEQ_CMDQ_IDX 0 +#define VIDC_IFACEQ_MSGQ_IDX 1 +#define VIDC_IFACEQ_DBGQ_IDX 2 +#define VIDC_IFACEQ_MAX_BUF_COUNT 50 +#define VIDC_IFACE_MAX_PARALLEL_CLNTS 16 +#define VIDC_IFACEQ_DFLT_QHDR 0x01010000 + +#define VIDC_MAX_NAME_LENGTH 64 +#define VIDC_MAX_PC_SKIP_COUNT 10 + +extern unsigned long __calc_bw(struct bus_info *bus, + struct msm_vidc_gov_data *vidc_data); +struct hfi_queue_table_header { + u32 qtbl_version; + u32 qtbl_size; + u32 qtbl_qhdr0_offset; + u32 qtbl_qhdr_size; + u32 qtbl_num_q; + u32 qtbl_num_active_q; +}; + +struct hfi_queue_header { + u32 qhdr_status; + u32 qhdr_start_addr; + u32 qhdr_type; + u32 qhdr_q_size; + u32 qhdr_pkt_size; + u32 qhdr_pkt_drop_cnt; + u32 qhdr_rx_wm; + u32 qhdr_tx_wm; + u32 qhdr_rx_req; + u32 qhdr_tx_req; + u32 qhdr_rx_irq_status; + u32 qhdr_tx_irq_status; + u32 qhdr_read_idx; + u32 qhdr_write_idx; +}; + +struct hfi_mem_map_table { + u32 mem_map_num_entries; + u32 mem_map_table_base_addr; +}; + +struct hfi_mem_map { + u32 virtual_addr; + u32 physical_addr; + u32 size; + u32 attr; +}; + +#define VIDC_IFACEQ_TABLE_SIZE (sizeof(struct hfi_queue_table_header) \ + + sizeof(struct hfi_queue_header) * VIDC_IFACEQ_NUMQ) + +#define VIDC_IFACEQ_QUEUE_SIZE (VIDC_IFACEQ_MAX_PKT_SIZE * \ + VIDC_IFACEQ_MAX_BUF_COUNT * VIDC_IFACE_MAX_PARALLEL_CLNTS) + +#define VIDC_IFACEQ_GET_QHDR_START_ADDR(ptr, i) \ + (void *)((ptr + sizeof(struct hfi_queue_table_header)) + \ + (i * sizeof(struct hfi_queue_header))) + +#define QDSS_SIZE 4096 +#define SFR_SIZE 4096 + +#define QUEUE_SIZE (VIDC_IFACEQ_TABLE_SIZE + \ + (VIDC_IFACEQ_QUEUE_SIZE * VIDC_IFACEQ_NUMQ)) + +#define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K) +#define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K) +#define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K) +#define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \ + ALIGNED_QDSS_SIZE, SZ_1M) + +enum vidc_hw_reg { + VIDC_HWREG_CTRL_STATUS = 0x1, + VIDC_HWREG_QTBL_INFO = 0x2, + VIDC_HWREG_QTBL_ADDR = 0x3, + VIDC_HWREG_CTRLR_RESET = 0x4, + VIDC_HWREG_IFACEQ_FWRXREQ = 0x5, + VIDC_HWREG_IFACEQ_FWTXREQ = 0x6, + VIDC_HWREG_VHI_SOFTINTEN = 0x7, + VIDC_HWREG_VHI_SOFTINTSTATUS = 0x8, + VIDC_HWREG_VHI_SOFTINTCLR = 0x9, + VIDC_HWREG_HVI_SOFTINTEN = 0xA, +}; + +struct vidc_mem_addr { + phys_addr_t align_device_addr; + u8 *align_virtual_addr; + u32 mem_size; + struct msm_smem mem_data; +}; + +struct vidc_iface_q_info { + void *q_hdr; + struct vidc_mem_addr q_array; +}; + +/* + * These are helper macros to iterate over various lists within + * venus_hfi_device->res. The intention is to cut down on a lot of boiler-plate + * code + */ + +/* Read as "for each 'thing' in a set of 'thingies'" */ +#define venus_hfi_for_each_thing(__device, __thing, __thingy) \ + venus_hfi_for_each_thing_continue(__device, __thing, __thingy, 0) + +#define venus_hfi_for_each_thing_reverse(__device, __thing, __thingy) \ + venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \ + (__device)->res->__thingy##_set.count - 1) + +/* TODO: the __from parameter technically not required since we can figure it + * out with some pointer magic (i.e. __thing - __thing##_tbl[0]). If this macro + * sees extensive use, probably worth cleaning it up but for now omitting it + * since it introduces unnecessary complexity. + */ +#define venus_hfi_for_each_thing_continue(__device, __thing, __thingy, __from) \ + for (__thing = &(__device)->res->\ + __thingy##_set.__thingy##_tbl[__from]; \ + __thing < &(__device)->res->__thingy##_set.__thingy##_tbl[0] + \ + ((__device)->res->__thingy##_set.count - __from); \ + ++__thing) + +#define venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \ + __from) \ + for (__thing = &(__device)->res->\ + __thingy##_set.__thingy##_tbl[__from]; \ + __thing >= &(__device)->res->__thingy##_set.__thingy##_tbl[0]; \ + --__thing) + +/* Regular set helpers */ +#define venus_hfi_for_each_regulator(__device, __rinfo) \ + venus_hfi_for_each_thing(__device, __rinfo, regulator) + +#define venus_hfi_for_each_regulator_reverse(__device, __rinfo) \ + venus_hfi_for_each_thing_reverse(__device, __rinfo, regulator) + +#define venus_hfi_for_each_regulator_reverse_continue(__device, __rinfo, \ + __from) \ + venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \ + regulator, __from) + +/* Clock set helpers */ +#define venus_hfi_for_each_clock(__device, __cinfo) \ + venus_hfi_for_each_thing(__device, __cinfo, clock) + +#define venus_hfi_for_each_clock_reverse(__device, __cinfo) \ + venus_hfi_for_each_thing_reverse(__device, __cinfo, clock) + +/* Bus set helpers */ +#define venus_hfi_for_each_bus(__device, __binfo) \ + venus_hfi_for_each_thing(__device, __binfo, bus) +#define venus_hfi_for_each_bus_reverse(__device, __binfo) \ + venus_hfi_for_each_thing_reverse(__device, __binfo, bus) + + +/* Internal data used in vidc_hal not exposed to msm_vidc*/ +struct hal_data { + u32 irq; + phys_addr_t firmware_base; + u8 __iomem *register_base; + u32 register_size; +}; + +struct imem { + enum imem_type type; + union { + phys_addr_t vmem; + }; +}; + +struct venus_resources { + struct msm_vidc_fw fw; + struct imem imem; +}; + +enum venus_hfi_state { + VENUS_STATE_DEINIT = 1, + VENUS_STATE_INIT, +}; + +struct venus_hfi_device { + struct list_head list; + struct list_head sess_head; + u32 intr_status; + u32 device_id; + u32 clk_freq; + u32 last_packet_type; + unsigned long clk_bitrate; + unsigned long scaled_rate; + struct msm_vidc_gov_data bus_vote; + bool power_enabled; + struct mutex lock; + msm_vidc_callback callback; + struct vidc_mem_addr iface_q_table; + struct vidc_mem_addr qdss; + struct vidc_mem_addr sfr; + struct vidc_mem_addr mem_addr; + struct vidc_iface_q_info iface_queues[VIDC_IFACEQ_NUMQ]; + struct hal_data *hal_data; + struct workqueue_struct *vidc_workq; + struct workqueue_struct *venus_pm_workq; + int spur_count; + int reg_count; + struct venus_resources resources; + struct msm_vidc_platform_resources *res; + enum venus_hfi_state state; + struct hfi_packetization_ops *pkt_ops; + enum hfi_packetization_type packetization_type; + struct msm_vidc_cb_info *response_pkt; + struct pm_qos_request qos; + unsigned int skip_pc_count; + struct msm_vidc_capability *sys_init_capabilities; +}; + +void venus_hfi_delete_device(void *device); + +int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback); +bool venus_hfi_is_session_supported(unsigned long sessions_supported, + enum vidc_vote_data_session session_type); + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi.c b/drivers/media/platform/msm/vidc_3x/vidc_hfi.c new file mode 100644 index 000000000000..67b88c886293 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include "msm_vidc_debug.h" +#include "vidc_hfi_api.h" +#include "venus_hfi.h" + +struct hfi_device *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type, + u32 device_id, struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + struct hfi_device *hdev = NULL; + int rc = 0; + + hdev = kzalloc(sizeof(struct hfi_device), GFP_KERNEL); + if (!hdev) { + dprintk(VIDC_ERR, "%s: failed to allocate hdev\n", __func__); + return NULL; + } + + switch (hfi_type) { + case VIDC_HFI_VENUS: + rc = venus_hfi_initialize(hdev, device_id, res, callback); + break; + default: + dprintk(VIDC_ERR, "Unsupported host-firmware interface\n"); + goto err_hfi_init; + } + + if (rc) { + if (rc != -EPROBE_DEFER) + dprintk(VIDC_ERR, "%s device init failed rc = %d", + __func__, rc); + goto err_hfi_init; + } + + return hdev; + +err_hfi_init: + kfree(hdev); + return ERR_PTR(rc); +} + +void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type, + struct hfi_device *hdev) +{ + if (!hdev) { + dprintk(VIDC_ERR, "%s invalid device %pK", __func__, hdev); + return; + } + + switch (hfi_type) { + case VIDC_HFI_VENUS: + venus_hfi_delete_device(hdev->hfi_device_data); + break; + default: + dprintk(VIDC_ERR, "Unsupported host-firmware interface\n"); + } + + kfree(hdev); +} + diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi.h new file mode 100644 index 000000000000..ee462e7ab9fa --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi.h @@ -0,0 +1,935 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __H_VIDC_HFI_H__ +#define __H_VIDC_HFI_H__ + +#include +#include "vidc_hfi_helper.h" +#include "vidc_hfi_api.h" + +#define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3) +#define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4) +#define HFI_EVENT_SESSION_LTRUSE_FAILED (HFI_OX_BASE + 0x5) +#define HFI_EVENT_RELEASE_BUFFER_REFERENCE (HFI_OX_BASE + 0x6) + +#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES \ + (HFI_OX_BASE + 0x1) +#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES \ + (HFI_OX_BASE + 0x2) + +#define HFI_BUFFERFLAG_EOS 0x00000001 +#define HFI_BUFFERFLAG_STARTTIME 0x00000002 +#define HFI_BUFFERFLAG_DECODEONLY 0x00000004 +#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008 +#define HFI_BUFFERFLAG_ENDOFFRAME 0x00000010 +#define HFI_BUFFERFLAG_SYNCFRAME 0x00000020 +#define HFI_BUFFERFLAG_EXTRADATA 0x00000040 +#define HFI_BUFFERFLAG_CODECCONFIG 0x00000080 +#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100 +#define HFI_BUFFERFLAG_READONLY 0x00000200 +#define HFI_BUFFERFLAG_ENDOFSUBFRAME 0x00000400 +#define HFI_BUFFERFLAG_EOSEQ 0x00200000 +#define HFI_BUFFER_FLAG_MBAFF 0x08000000 +#define HFI_BUFFERFLAG_VPE_YUV_601_709_CSC_CLAMP \ + 0x10000000 +#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000 +#define HFI_BUFFERFLAG_TEI 0x40000000 +#define HFI_BUFFERFLAG_DISCONTINUITY 0x80000000 + + +#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING \ + (HFI_OX_BASE + 0x1001) +#define HFI_ERR_SESSION_SAME_STATE_OPERATION \ + (HFI_OX_BASE + 0x1002) +#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED \ + (HFI_OX_BASE + 0x1003) +#define HFI_ERR_SESSION_START_CODE_NOT_FOUND \ + (HFI_OX_BASE + 0x1004) + +#define HFI_BUFFER_INTERNAL_SCRATCH (HFI_OX_BASE + 0x1) +#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2) +#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3) +#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4) +#define HFI_BUFFER_INTERNAL_SCRATCH_1 (HFI_OX_BASE + 0x5) +#define HFI_BUFFER_INTERNAL_SCRATCH_2 (HFI_OX_BASE + 0x6) + +#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1) +#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2) +#define HFI_BUFFER_MODE_DYNAMIC (HFI_OX_BASE + 0x3) + +#define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1) +#define HFI_FLUSH_OUTPUT (HFI_OX_BASE + 0x2) +#define HFI_FLUSH_ALL (HFI_OX_BASE + 0x4) + +#define HFI_EXTRADATA_NONE 0x00000000 +#define HFI_EXTRADATA_MB_QUANTIZATION 0x00000001 +#define HFI_EXTRADATA_INTERLACE_VIDEO 0x00000002 +#define HFI_EXTRADATA_VC1_FRAMEDISP 0x00000003 +#define HFI_EXTRADATA_VC1_SEQDISP 0x00000004 +#define HFI_EXTRADATA_TIMESTAMP 0x00000005 +#define HFI_EXTRADATA_S3D_FRAME_PACKING 0x00000006 +#define HFI_EXTRADATA_FRAME_RATE 0x00000007 +#define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008 +#define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009 +#define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000D +#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000E +#define HFI_EXTRADATA_FRAME_QP 0x0000000F +#define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010 +#define HFI_EXTRADATA_VPX_COLORSPACE 0x00000014 +#define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000 +#define HFI_EXTRADATA_NUM_CONCEALED_MB 0x7F100001 +#define HFI_EXTRADATA_INDEX 0x7F100002 +#define HFI_EXTRADATA_METADATA_LTR 0x7F100004 +#define HFI_EXTRADATA_METADATA_FILLER 0x7FE00002 + +#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000E +#define HFI_INDEX_EXTRADATA_OUTPUT_CROP 0x0700000F +#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7F100003 + +struct hfi_buffer_alloc_mode { + u32 buffer_type; + u32 buffer_mode; +}; + + +struct hfi_index_extradata_config { + int enable; + u32 index_extra_data_id; +}; + +struct hfi_extradata_header { + u32 size; + u32 version; + u32 port_index; + u32 type; + u32 data_size; + u8 rg_data[1]; +}; + +#define HFI_INTERLACE_FRAME_PROGRESSIVE 0x01 +#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST 0x02 +#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST 0x04 +#define HFI_INTERLACE_FRAME_TOPFIELDFIRST 0x08 +#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST 0x10 + +#define HFI_PROPERTY_SYS_OX_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000) + +#define HFI_PROPERTY_PARAM_OX_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000) +#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL \ + (HFI_PROPERTY_PARAM_OX_START + 0x001) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO \ + (HFI_PROPERTY_PARAM_OX_START + 0x002) +#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED \ + (HFI_PROPERTY_PARAM_OX_START + 0x003) +#define HFI_PROPERTY_PARAM_CHROMA_SITE \ +(HFI_PROPERTY_PARAM_OX_START + 0x004) +#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG \ + (HFI_PROPERTY_PARAM_OX_START + 0x005) +#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA \ + (HFI_PROPERTY_PARAM_OX_START + 0x006) +#define HFI_PROPERTY_PARAM_DIVX_FORMAT \ + (HFI_PROPERTY_PARAM_OX_START + 0x007) +#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE \ + (HFI_PROPERTY_PARAM_OX_START + 0x008) +#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA \ + (HFI_PROPERTY_PARAM_OX_START + 0x009) +#define HFI_PROPERTY_PARAM_ERR_DETECTION_CODE_EXTRADATA \ + (HFI_PROPERTY_PARAM_OX_START + 0x00A) +#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED \ + (HFI_PROPERTY_PARAM_OX_START + 0x00B) +#define HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM \ + (HFI_PROPERTY_PARAM_OX_START + 0x00C) +#define HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT \ + (HFI_PROPERTY_PARAM_OX_START + 0x00E) + +#define HFI_PROPERTY_CONFIG_OX_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x02000) +#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS \ + (HFI_PROPERTY_CONFIG_OX_START + 0x001) +#define HFI_PROPERTY_CONFIG_REALTIME \ + (HFI_PROPERTY_CONFIG_OX_START + 0x002) +#define HFI_PROPERTY_CONFIG_PRIORITY \ + (HFI_PROPERTY_CONFIG_OX_START + 0x003) +#define HFI_PROPERTY_CONFIG_BATCH_INFO \ + (HFI_PROPERTY_CONFIG_OX_START + 0x004) + +#define HFI_PROPERTY_PARAM_VDEC_OX_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000) +#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001) +#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT\ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x002) +#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x003) +#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x004) +#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x005) +#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x006) +#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x007) +#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008) +#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009) +#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A) +#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B) +#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C) +#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D) + +#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E) +#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011) +#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x012) +#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x013) +#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014) +#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015) +#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x016) +#define HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x017) +#define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018) +#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x019) +#define HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01A) +#define HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01B) +#define HFI_PROPERTY_PARAM_VDEC_VQZIP_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001C) +#define HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001D) +#define HFI_PROPERTY_PARAM_VDEC_MASTERING_DISPLAY_COLOUR_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001E) +#define HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001F) + +#define HFI_PROPERTY_CONFIG_VDEC_OX_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x4000) +#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x001) +#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x002) +#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x003) +#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY \ + (HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x004) + +#define HFI_PROPERTY_PARAM_VENC_OX_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000) +#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x001) +#define HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x002) +#define HFI_PROPERTY_PARAM_VENC_LTR_INFO \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x003) +#define HFI_PROPERTY_PARAM_VENC_MBI_DUMPING \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x005) +#define HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x006) +#define HFI_PROPERTY_PARAM_VENC_YUVSTAT_INFO_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x007) +#define HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x008) +#define HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA \ + (HFI_PROPERTY_PARAM_VENC_OX_START + 0x009) + +#define HFI_PROPERTY_CONFIG_VENC_OX_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000) +#define HFI_PROPERTY_CONFIG_VENC_FRAME_QP \ + (HFI_PROPERTY_CONFIG_VENC_OX_START + 0x001) + +#define HFI_PROPERTY_PARAM_VPE_OX_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000) +#define HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION \ + (HFI_PROPERTY_PARAM_VPE_OX_START + 0x001) + +#define HFI_PROPERTY_CONFIG_VPE_OX_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000) + +struct hfi_batch_info { + u32 input_batch_count; + u32 output_batch_count; +}; + +struct hfi_buffer_count_actual { + u32 buffer_type; + u32 buffer_count_actual; +}; + +struct hfi_buffer_size_minimum { + u32 buffer_type; + u32 buffer_size; +}; + +struct hfi_buffer_requirements { + u32 buffer_type; + u32 buffer_size; + u32 buffer_region_size; + u32 buffer_hold_count; + u32 buffer_count_min; + u32 buffer_count_actual; + u32 contiguous; + u32 buffer_alignment; +}; + +#define HFI_CHROMA_SITE_0 (HFI_OX_BASE + 0x1) +#define HFI_CHROMA_SITE_1 (HFI_OX_BASE + 0x2) +#define HFI_CHROMA_SITE_2 (HFI_OX_BASE + 0x3) +#define HFI_CHROMA_SITE_3 (HFI_OX_BASE + 0x4) +#define HFI_CHROMA_SITE_4 (HFI_OX_BASE + 0x5) +#define HFI_CHROMA_SITE_5 (HFI_OX_BASE + 0x6) + +struct hfi_data_payload { + u32 size; + u8 rg_data[1]; +}; + +struct hfi_enable_picture { + u32 picture_type; +}; + +struct hfi_display_picture_buffer_count { + int enable; + u32 count; +}; + +struct hfi_extra_data_header_config { + u32 type; + u32 buffer_type; + u32 version; + u32 port_index; + u32 client_extra_data_id; +}; + +struct hfi_interlace_format_supported { + u32 buffer_type; + u32 format; +}; + +struct hfi_buffer_alloc_mode_supported { + u32 buffer_type; + u32 num_entries; + u32 rg_data[1]; +}; + +struct hfi_mb_error_map { + u32 error_map_size; + u8 rg_error_map[1]; +}; + +struct hfi_metadata_pass_through { + int enable; + u32 size; +}; + +struct hfi_multi_view_select { + u32 view_index; +}; + +struct hfi_hybrid_hierp { + u32 layers; +}; + +#define HFI_PRIORITY_LOW 10 +#define HFI_PRIOIRTY_MEDIUM 20 +#define HFI_PRIORITY_HIGH 30 + +#define HFI_OUTPUT_ORDER_DISPLAY (HFI_OX_BASE + 0x1) +#define HFI_OUTPUT_ORDER_DECODE (HFI_OX_BASE + 0x2) + +#define HFI_RATE_CONTROL_OFF (HFI_OX_BASE + 0x1) +#define HFI_RATE_CONTROL_VBR_VFR (HFI_OX_BASE + 0x2) +#define HFI_RATE_CONTROL_VBR_CFR (HFI_OX_BASE + 0x3) +#define HFI_RATE_CONTROL_CBR_VFR (HFI_OX_BASE + 0x4) +#define HFI_RATE_CONTROL_CBR_CFR (HFI_OX_BASE + 0x5) +#define HFI_RATE_CONTROL_MBR_CFR (HFI_OX_BASE + 0x6) +#define HFI_RATE_CONTROL_MBR_VFR (HFI_OX_BASE + 0x7) + + +struct hfi_uncompressed_plane_actual_constraints_info { + u32 buffer_type; + u32 num_planes; + struct hfi_uncompressed_plane_constraints rg_plane_format[1]; +}; + +#define HFI_CMD_SYS_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x0000) +#define HFI_CMD_SYS_SESSION_ABORT (HFI_CMD_SYS_OX_START + 0x001) +#define HFI_CMD_SYS_PING (HFI_CMD_SYS_OX_START + 0x002) + +#define HFI_CMD_SESSION_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x1000) +#define HFI_CMD_SESSION_LOAD_RESOURCES (HFI_CMD_SESSION_OX_START + 0x001) +#define HFI_CMD_SESSION_START (HFI_CMD_SESSION_OX_START + 0x002) +#define HFI_CMD_SESSION_STOP (HFI_CMD_SESSION_OX_START + 0x003) +#define HFI_CMD_SESSION_EMPTY_BUFFER (HFI_CMD_SESSION_OX_START + 0x004) +#define HFI_CMD_SESSION_FILL_BUFFER (HFI_CMD_SESSION_OX_START + 0x005) +#define HFI_CMD_SESSION_SUSPEND (HFI_CMD_SESSION_OX_START + 0x006) +#define HFI_CMD_SESSION_RESUME (HFI_CMD_SESSION_OX_START + 0x007) +#define HFI_CMD_SESSION_FLUSH (HFI_CMD_SESSION_OX_START + 0x008) +#define HFI_CMD_SESSION_GET_PROPERTY (HFI_CMD_SESSION_OX_START + 0x009) +#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER \ + (HFI_CMD_SESSION_OX_START + 0x00A) +#define HFI_CMD_SESSION_RELEASE_BUFFERS \ + (HFI_CMD_SESSION_OX_START + 0x00B) +#define HFI_CMD_SESSION_RELEASE_RESOURCES \ + (HFI_CMD_SESSION_OX_START + 0x00C) +#define HFI_CMD_SESSION_CONTINUE (HFI_CMD_SESSION_OX_START + 0x00D) +#define HFI_CMD_SESSION_SYNC (HFI_CMD_SESSION_OX_START + 0x00E) + +#define HFI_MSG_SYS_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000) +#define HFI_MSG_SYS_PING_ACK (HFI_MSG_SYS_OX_START + 0x2) +#define HFI_MSG_SYS_SESSION_ABORT_DONE (HFI_MSG_SYS_OX_START + 0x4) + +#define HFI_MSG_SESSION_OX_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x1000) +#define HFI_MSG_SESSION_LOAD_RESOURCES_DONE (HFI_MSG_SESSION_OX_START + 0x1) +#define HFI_MSG_SESSION_START_DONE (HFI_MSG_SESSION_OX_START + 0x2) +#define HFI_MSG_SESSION_STOP_DONE (HFI_MSG_SESSION_OX_START + 0x3) +#define HFI_MSG_SESSION_SUSPEND_DONE (HFI_MSG_SESSION_OX_START + 0x4) +#define HFI_MSG_SESSION_RESUME_DONE (HFI_MSG_SESSION_OX_START + 0x5) +#define HFI_MSG_SESSION_FLUSH_DONE (HFI_MSG_SESSION_OX_START + 0x6) +#define HFI_MSG_SESSION_EMPTY_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x7) +#define HFI_MSG_SESSION_FILL_BUFFER_DONE (HFI_MSG_SESSION_OX_START + 0x8) +#define HFI_MSG_SESSION_PROPERTY_INFO (HFI_MSG_SESSION_OX_START + 0x9) +#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE \ + (HFI_MSG_SESSION_OX_START + 0xA) +#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE \ + (HFI_MSG_SESSION_OX_START + 0xB) +#define HFI_MSG_SESSION_RELEASE_BUFFERS_DONE \ + (HFI_MSG_SESSION_OX_START + 0xC) + +#define VIDC_IFACEQ_MAX_PKT_SIZE 1024 +#define VIDC_IFACEQ_MED_PKT_SIZE 768 +#define VIDC_IFACEQ_MIN_PKT_SIZE 8 +#define VIDC_IFACEQ_VAR_SMALL_PKT_SIZE 100 +#define VIDC_IFACEQ_VAR_LARGE_PKT_SIZE 512 +#define VIDC_IFACEQ_VAR_HUGE_PKT_SIZE (1024*12) + + +struct hfi_cmd_sys_session_abort_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_sys_ping_packet { + u32 size; + u32 packet_type; + u32 client_data; +}; + +struct hfi_cmd_session_load_resources_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_start_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_stop_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_empty_buffer_compressed_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 input_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 view_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 input_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer2; + u32 rgData[1]; +}; + +struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer3; + u32 rgData[1]; +}; + +struct hfi_cmd_session_fill_buffer_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 stream_id; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 output_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_cmd_session_flush_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 flush_type; +}; + +struct hfi_cmd_session_suspend_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_resume_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_get_property_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_session_release_buffer_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 buffer_type; + u32 buffer_size; + u32 extra_data_size; + int response_req; + u32 num_buffers; + u32 rg_buffer_info[1]; +}; + +struct hfi_cmd_session_release_resources_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_session_parse_sequence_header_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 header_len; + u32 packet_buffer; +}; + +struct hfi_msg_sys_session_abort_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_sys_idle_packet { + u32 size; + u32 packet_type; +}; + +struct hfi_msg_sys_ping_ack_packet { + u32 size; + u32 packet_type; + u32 client_data; +}; + +struct hfi_msg_sys_property_info_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_session_load_resources_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_start_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_stop_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_suspend_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_resume_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_flush_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 flush_type; +}; + +struct hfi_msg_session_empty_buffer_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 offset; + u32 filled_len; + u32 input_tag; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[1]; +}; + +struct hfi_msg_session_fill_buffer_done_compressed_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 error_type; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 input_tag; + u32 output_tag; + u32 picture_type; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[0]; +}; + +struct hfi_msg_session_fbd_uncompressed_plane0_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 stream_id; + u32 view_id; + u32 error_type; + u32 time_stamp_hi; + u32 time_stamp_lo; + u32 flags; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 frame_width; + u32 frame_height; + u32 start_x_coord; + u32 start_y_coord; + u32 input_tag; + u32 input_tag2; + u32 output_tag; + u32 picture_type; + u32 packet_buffer; + u32 extra_data_buffer; + u32 rgData[0]; +}; + +struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer2; + u32 rgData[0]; +}; + +struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet { + u32 flags; + u32 alloc_len; + u32 filled_len; + u32 offset; + u32 packet_buffer3; + u32 rgData[0]; +}; + +struct hfi_msg_session_parse_sequence_header_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_session_property_info_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_session_release_resources_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_release_buffers_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 num_buffers; + u32 rg_buffer_info[1]; +}; + +struct hfi_extradata_mb_quantization_payload { + u8 rg_mb_qp[1]; +}; + +struct hfi_extradata_vc1_pswnd { + u32 ps_wnd_h_offset; + u32 ps_wnd_v_offset; + u32 ps_wnd_width; + u32 ps_wnd_height; +}; + +struct hfi_extradata_vc1_framedisp_payload { + u32 res_pic; + u32 ref; + u32 range_map_present; + u32 range_map_y; + u32 range_map_uv; + u32 num_pan_scan_wnds; + struct hfi_extradata_vc1_pswnd rg_ps_wnd[1]; +}; + +struct hfi_extradata_vc1_seqdisp_payload { + u32 prog_seg_frm; + u32 uv_sampling_fmt; + u32 color_fmt_flag; + u32 color_primaries; + u32 transfer_char; + u32 mat_coeff; + u32 aspect_ratio; + u32 aspect_horiz; + u32 aspect_vert; +}; + +struct hfi_extradata_timestamp_payload { + u32 time_stamp_low; + u32 time_stamp_high; +}; + + +struct hfi_extradata_s3d_frame_packing_payload { + u32 fpa_id; + int cancel_flag; + u32 fpa_type; + int quin_cunx_flag; + u32 content_interprtation_type; + int spatial_flipping_flag; + int frame0_flipped_flag; + int field_views_flag; + int current_frame_isFrame0_flag; + int frame0_self_contained_flag; + int frame1_self_contained_flag; + u32 frame0_graid_pos_x; + u32 frame0_graid_pos_y; + u32 frame1_graid_pos_x; + u32 frame1_graid_pos_y; + u32 fpa_reserved_byte; + u32 fpa_repetition_period; + int fpa_extension_flag; +}; + +struct hfi_extradata_interlace_video_payload { + u32 format; +}; + +struct hfi_extradata_num_concealed_mb_payload { + u32 num_mb_concealed; +}; + +struct hfi_extradata_sliceinfo { + u32 offset_in_stream; + u32 slice_length; +}; + +struct hfi_extradata_multislice_info_payload { + u32 num_slices; + struct hfi_extradata_sliceinfo rg_slice_info[1]; +}; + +struct hfi_index_extradata_input_crop_payload { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + +struct hfi_index_extradata_output_crop_payload { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 display_width; + u32 display_height; + u32 width; + u32 height; +}; + +struct hfi_index_extradata_digital_zoom_payload { + u32 size; + u32 version; + u32 port_index; + int width; + int height; +}; + +struct hfi_index_extradata_aspect_ratio_payload { + u32 size; + u32 version; + u32 port_index; + u32 aspect_width; + u32 aspect_height; +}; +struct hfi_extradata_panscan_wndw_payload { + u32 num_window; + struct hfi_extradata_vc1_pswnd wnd[1]; +}; + +struct hfi_extradata_frame_type_payload { + u32 frame_rate; +}; + +struct hfi_extradata_recovery_point_sei_payload { + u32 flag; +}; + +struct hfi_cmd_session_continue_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hal_session { + struct list_head list; + void *session_id; + bool is_decoder; + enum hal_video_codec codec; + enum hal_domain domain; + void *device; +}; + +struct hal_device_data { + struct list_head dev_head; + int dev_count; +}; + +struct msm_vidc_fw { + void *cookie; +}; + +int hfi_process_msg_packet(u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr, + struct msm_vidc_cb_info *info); + +enum vidc_status hfi_process_sys_init_done_prop_read( + struct hfi_msg_sys_init_done_packet *pkt, + struct vidc_hal_sys_init_done *sys_init_done); + +enum vidc_status hfi_process_session_init_done_prop_read( + struct hfi_msg_sys_session_init_done_packet *pkt, + struct vidc_hal_session_init_done *session_init_done); + +#endif + diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h new file mode 100644 index 000000000000..6e3bf2eca6eb --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi_api.h @@ -0,0 +1,1551 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VIDC_HFI_API_H__ +#define __VIDC_HFI_API_H__ + +#include +#include +#include +#include +#include "msm_vidc_resources.h" + +#define CONTAINS(__a, __sz, __t) ({\ + int __rc = __t >= __a && \ + __t < __a + __sz; \ + __rc; \ +}) + +#define OVERLAPS(__t, __tsz, __a, __asz) ({\ + int __rc = __t <= __a && \ + __t + __tsz >= __a + __asz; \ + __rc; \ +}) + +#define HAL_BUFFERFLAG_EOS 0x00000001 +#define HAL_BUFFERFLAG_STARTTIME 0x00000002 +#define HAL_BUFFERFLAG_DECODEONLY 0x00000004 +#define HAL_BUFFERFLAG_DATACORRUPT 0x00000008 +#define HAL_BUFFERFLAG_ENDOFFRAME 0x00000010 +#define HAL_BUFFERFLAG_SYNCFRAME 0x00000020 +#define HAL_BUFFERFLAG_EXTRADATA 0x00000040 +#define HAL_BUFFERFLAG_CODECCONFIG 0x00000080 +#define HAL_BUFFERFLAG_TIMESTAMPINVALID 0x00000100 +#define HAL_BUFFERFLAG_READONLY 0x00000200 +#define HAL_BUFFERFLAG_ENDOFSUBFRAME 0x00000400 +#define HAL_BUFFERFLAG_EOSEQ 0x00200000 +#define HAL_BUFFERFLAG_MBAFF 0x08000000 +#define HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP 0x10000000 +#define HAL_BUFFERFLAG_DROP_FRAME 0x20000000 +#define HAL_BUFFERFLAG_TS_DISCONTINUITY 0x40000000 +#define HAL_BUFFERFLAG_TS_ERROR 0x80000000 + + + +#define HAL_DEBUG_MSG_LOW 0x00000001 +#define HAL_DEBUG_MSG_MEDIUM 0x00000002 +#define HAL_DEBUG_MSG_HIGH 0x00000004 +#define HAL_DEBUG_MSG_ERROR 0x00000008 +#define HAL_DEBUG_MSG_FATAL 0x00000010 +#define MAX_PROFILE_COUNT 16 + +#define HAL_MAX_MATRIX_COEFFS 9 +#define HAL_MAX_BIAS_COEFFS 3 +#define HAL_MAX_LIMIT_COEFFS 6 +#define VENUS_VERSION_LENGTH 128 + +/* 16 encoder and 16 decoder sessions */ +#define VIDC_MAX_SESSIONS 32 +#define VIDC_MAX_DECODE_SESSIONS 16 +#define VIDC_MAX_ENCODE_SESSIONS 16 + + +enum vidc_status { + VIDC_ERR_NONE = 0x0, + VIDC_ERR_FAIL = 0x80000000, + VIDC_ERR_ALLOC_FAIL, + VIDC_ERR_ILLEGAL_OP, + VIDC_ERR_BAD_PARAM, + VIDC_ERR_BAD_HANDLE, + VIDC_ERR_NOT_SUPPORTED, + VIDC_ERR_BAD_STATE, + VIDC_ERR_MAX_CLIENTS, + VIDC_ERR_IFRAME_EXPECTED, + VIDC_ERR_HW_FATAL, + VIDC_ERR_BITSTREAM_ERR, + VIDC_ERR_INDEX_NOMORE, + VIDC_ERR_SEQHDR_PARSE_FAIL, + VIDC_ERR_INSUFFICIENT_BUFFER, + VIDC_ERR_BAD_POWER_STATE, + VIDC_ERR_NO_VALID_SESSION, + VIDC_ERR_TIMEOUT, + VIDC_ERR_CMDQFULL, + VIDC_ERR_START_CODE_NOT_FOUND, + VIDC_ERR_CLIENT_PRESENT = 0x90000001, + VIDC_ERR_CLIENT_FATAL, + VIDC_ERR_CMD_QUEUE_FULL, + VIDC_ERR_UNUSED = 0x10000000 +}; + +enum hal_extradata_id { + HAL_EXTRADATA_NONE, + HAL_EXTRADATA_MB_QUANTIZATION, + HAL_EXTRADATA_INTERLACE_VIDEO, + HAL_EXTRADATA_VC1_FRAMEDISP, + HAL_EXTRADATA_VC1_SEQDISP, + HAL_EXTRADATA_TIMESTAMP, + HAL_EXTRADATA_S3D_FRAME_PACKING, + HAL_EXTRADATA_FRAME_RATE, + HAL_EXTRADATA_PANSCAN_WINDOW, + HAL_EXTRADATA_RECOVERY_POINT_SEI, + HAL_EXTRADATA_MULTISLICE_INFO, + HAL_EXTRADATA_INDEX, + HAL_EXTRADATA_NUM_CONCEALED_MB, + HAL_EXTRADATA_METADATA_FILLER, + HAL_EXTRADATA_ASPECT_RATIO, + HAL_EXTRADATA_MPEG2_SEQDISP, + HAL_EXTRADATA_STREAM_USERDATA, + HAL_EXTRADATA_DEC_FRAME_QP, + HAL_EXTRADATA_ENC_FRAME_QP, + HAL_EXTRADATA_FRAME_BITS_INFO, + HAL_EXTRADATA_INPUT_CROP, + HAL_EXTRADATA_DIGITAL_ZOOM, + HAL_EXTRADATA_LTR_INFO, + HAL_EXTRADATA_METADATA_MBI, + HAL_EXTRADATA_VQZIP_SEI, + HAL_EXTRADATA_YUV_STATS, + HAL_EXTRADATA_ROI_QP, + HAL_EXTRADATA_OUTPUT_CROP, + HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI, + HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI, + HAL_EXTRADATA_VUI_DISPLAY_INFO, + HAL_EXTRADATA_VPX_COLORSPACE, + HAL_EXTRADATA_PQ_INFO, +}; + +enum hal_property { + HAL_CONFIG_FRAME_RATE = 0x04000001, + HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, + HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO, + HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO, + HAL_PARAM_EXTRA_DATA_HEADER_CONFIG, + HAL_PARAM_INDEX_EXTRADATA, + HAL_PARAM_FRAME_SIZE, + HAL_CONFIG_REALTIME, + HAL_PARAM_BUFFER_COUNT_ACTUAL, + HAL_PARAM_BUFFER_SIZE_MINIMUM, + HAL_PARAM_NAL_STREAM_FORMAT_SELECT, + HAL_PARAM_VDEC_OUTPUT_ORDER, + HAL_PARAM_VDEC_PICTURE_TYPE_DECODE, + HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO, + HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER, + HAL_PARAM_VDEC_MULTI_STREAM, + HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT, + HAL_PARAM_DIVX_FORMAT, + HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING, + HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER, + HAL_CONFIG_VDEC_MB_ERROR_MAP, + HAL_CONFIG_VENC_REQUEST_IFRAME, + HAL_PARAM_VENC_MPEG4_SHORT_HEADER, + HAL_PARAM_VENC_MPEG4_AC_PREDICTION, + HAL_CONFIG_VENC_TARGET_BITRATE, + HAL_PARAM_PROFILE_LEVEL_CURRENT, + HAL_PARAM_VENC_H264_ENTROPY_CONTROL, + HAL_PARAM_VENC_RATE_CONTROL, + HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION, + HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION, + HAL_PARAM_VENC_H264_DEBLOCK_CONTROL, + HAL_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF, + HAL_PARAM_VENC_SESSION_QP, + HAL_PARAM_VENC_SESSION_QP_RANGE, + HAL_CONFIG_VENC_INTRA_PERIOD, + HAL_CONFIG_VENC_IDR_PERIOD, + HAL_CONFIG_VPE_OPERATIONS, + HAL_PARAM_VENC_INTRA_REFRESH, + HAL_PARAM_VENC_MULTI_SLICE_CONTROL, + HAL_CONFIG_VPE_DEINTERLACE, + HAL_SYS_DEBUG_CONFIG, + HAL_CONFIG_BUFFER_REQUIREMENTS, + HAL_CONFIG_PRIORITY, + HAL_CONFIG_BATCH_INFO, + HAL_PARAM_METADATA_PASS_THROUGH, + HAL_SYS_IDLE_INDICATOR, + HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED, + HAL_PARAM_INTERLACE_FORMAT_SUPPORTED, + HAL_PARAM_CHROMA_SITE, + HAL_PARAM_PROPERTIES_SUPPORTED, + HAL_PARAM_PROFILE_LEVEL_SUPPORTED, + HAL_PARAM_CAPABILITY_SUPPORTED, + HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED, + HAL_PARAM_MULTI_VIEW_FORMAT, + HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE, + HAL_PARAM_CODEC_SUPPORTED, + HAL_PARAM_VDEC_MULTI_VIEW_SELECT, + HAL_PARAM_VDEC_MB_QUANTIZATION, + HAL_PARAM_VDEC_NUM_CONCEALED_MB, + HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING, + HAL_PARAM_VENC_SLICE_DELIVERY_MODE, + HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING, + HAL_CONFIG_BUFFER_COUNT_ACTUAL, + HAL_CONFIG_VDEC_MULTI_STREAM, + HAL_PARAM_VENC_MULTI_SLICE_INFO, + HAL_CONFIG_VENC_TIMESTAMP_SCALE, + HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER, + HAL_PARAM_VDEC_SYNC_FRAME_DECODE, + HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL, + HAL_CONFIG_VENC_MAX_BITRATE, + HAL_PARAM_VENC_H264_VUI_TIMING_INFO, + HAL_PARAM_VENC_GENERATE_AUDNAL, + HAL_PARAM_VENC_MAX_NUM_B_FRAMES, + HAL_PARAM_BUFFER_ALLOC_MODE, + HAL_PARAM_VDEC_FRAME_ASSEMBLY, + HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC, + HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY, + HAL_PARAM_VDEC_CONCEAL_COLOR, + HAL_PARAM_VDEC_SCS_THRESHOLD, + HAL_PARAM_GET_BUFFER_REQUIREMENTS, + HAL_PARAM_MVC_BUFFER_LAYOUT, + HAL_PARAM_VENC_LTRMODE, + HAL_CONFIG_VENC_MARKLTRFRAME, + HAL_CONFIG_VENC_USELTRFRAME, + HAL_CONFIG_VENC_LTRPERIOD, + HAL_CONFIG_VENC_HIER_P_NUM_FRAMES, + HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS, + HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP, + HAL_PARAM_VENC_ENABLE_INITIAL_QP, + HAL_PARAM_VENC_SEARCH_RANGE, + HAL_PARAM_VPE_COLOR_SPACE_CONVERSION, + HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE, + HAL_PARAM_VENC_H264_NAL_SVC_EXT, + HAL_CONFIG_VENC_PERF_MODE, + HAL_PARAM_VENC_HIER_B_MAX_ENH_LAYERS, + HAL_PARAM_VDEC_NON_SECURE_OUTPUT2, + HAL_PARAM_VENC_HIER_P_HYBRID_MODE, + HAL_PARAM_VENC_MBI_STATISTICS_MODE, + HAL_PARAM_SYNC_BASED_INTERRUPT, + HAL_CONFIG_VENC_FRAME_QP, + HAL_CONFIG_VENC_BASELAYER_PRIORITYID, + HAL_PARAM_VENC_VQZIP_SEI, + HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO, + HAL_CONFIG_VDEC_ENTROPY, + HAL_PARAM_VENC_BITRATE_TYPE, + HAL_PARAM_VENC_H264_PIC_ORDER_CNT, + HAL_PARAM_VENC_LOW_LATENCY, + HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, + HAL_CONFIG_VENC_BLUR_RESOLUTION, + HAL_PARAM_VENC_VIDEO_SIGNAL_INFO, + HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED, + HAL_PARAM_VENC_H264_TRANSFORM_8x8, + HAL_PARAM_VENC_IFRAMESIZE_TYPE, +}; + +enum hal_domain { + HAL_VIDEO_DOMAIN_VPE, + HAL_VIDEO_DOMAIN_ENCODER, + HAL_VIDEO_DOMAIN_DECODER, + HAL_UNUSED_DOMAIN = 0x10000000, +}; + +enum multi_stream { + HAL_VIDEO_DECODER_NONE = 0x00000000, + HAL_VIDEO_DECODER_PRIMARY = 0x00000001, + HAL_VIDEO_DECODER_SECONDARY = 0x00000002, + HAL_VIDEO_DECODER_BOTH_OUTPUTS = 0x00000004, + HAL_VIDEO_UNUSED_OUTPUTS = 0x10000000, +}; + +enum hal_core_capabilities { + HAL_VIDEO_ENCODER_ROTATION_CAPABILITY = 0x00000001, + HAL_VIDEO_ENCODER_SCALING_CAPABILITY = 0x00000002, + HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY = 0x00000004, + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY = 0x00000008, + HAL_VIDEO_UNUSED_CAPABILITY = 0x10000000, +}; + +enum hal_default_properties { + HAL_VIDEO_DYNAMIC_BUF_MODE = 0x00000001, + HAL_VIDEO_CONTINUE_DATA_TRANSFER = 0x00000002, +}; + +enum hal_video_codec { + HAL_VIDEO_CODEC_UNKNOWN = 0x00000000, + HAL_VIDEO_CODEC_MVC = 0x00000001, + HAL_VIDEO_CODEC_H264 = 0x00000002, + HAL_VIDEO_CODEC_H263 = 0x00000004, + HAL_VIDEO_CODEC_MPEG1 = 0x00000008, + HAL_VIDEO_CODEC_MPEG2 = 0x00000010, + HAL_VIDEO_CODEC_MPEG4 = 0x00000020, + HAL_VIDEO_CODEC_DIVX_311 = 0x00000040, + HAL_VIDEO_CODEC_DIVX = 0x00000080, + HAL_VIDEO_CODEC_VC1 = 0x00000100, + HAL_VIDEO_CODEC_SPARK = 0x00000200, + HAL_VIDEO_CODEC_VP6 = 0x00000400, + HAL_VIDEO_CODEC_VP7 = 0x00000800, + HAL_VIDEO_CODEC_VP8 = 0x00001000, + HAL_VIDEO_CODEC_HEVC = 0x00002000, + HAL_VIDEO_CODEC_VP9 = 0x00004000, + HAL_VIDEO_CODEC_HEVC_HYBRID = 0x80000000, + HAL_UNUSED_CODEC = 0x10000000, +}; + +enum hal_h263_profile { + HAL_H263_PROFILE_BASELINE = 0x00000001, + HAL_H263_PROFILE_H320CODING = 0x00000002, + HAL_H263_PROFILE_BACKWARDCOMPATIBLE = 0x00000004, + HAL_H263_PROFILE_ISWV2 = 0x00000008, + HAL_H263_PROFILE_ISWV3 = 0x00000010, + HAL_H263_PROFILE_HIGHCOMPRESSION = 0x00000020, + HAL_H263_PROFILE_INTERNET = 0x00000040, + HAL_H263_PROFILE_INTERLACE = 0x00000080, + HAL_H263_PROFILE_HIGHLATENCY = 0x00000100, + HAL_UNUSED_H263_PROFILE = 0x10000000, +}; + +enum hal_h263_level { + HAL_H263_LEVEL_10 = 0x00000001, + HAL_H263_LEVEL_20 = 0x00000002, + HAL_H263_LEVEL_30 = 0x00000004, + HAL_H263_LEVEL_40 = 0x00000008, + HAL_H263_LEVEL_45 = 0x00000010, + HAL_H263_LEVEL_50 = 0x00000020, + HAL_H263_LEVEL_60 = 0x00000040, + HAL_H263_LEVEL_70 = 0x00000080, + HAL_UNUSED_H263_LEVEL = 0x10000000, +}; + +enum hal_mpeg2_profile { + HAL_MPEG2_PROFILE_SIMPLE = 0x00000001, + HAL_MPEG2_PROFILE_MAIN = 0x00000002, + HAL_MPEG2_PROFILE_422 = 0x00000004, + HAL_MPEG2_PROFILE_SNR = 0x00000008, + HAL_MPEG2_PROFILE_SPATIAL = 0x00000010, + HAL_MPEG2_PROFILE_HIGH = 0x00000020, + HAL_UNUSED_MPEG2_PROFILE = 0x10000000, +}; + +enum hal_mpeg2_level { + HAL_MPEG2_LEVEL_LL = 0x00000001, + HAL_MPEG2_LEVEL_ML = 0x00000002, + HAL_MPEG2_LEVEL_H14 = 0x00000004, + HAL_MPEG2_LEVEL_HL = 0x00000008, + HAL_UNUSED_MEPG2_LEVEL = 0x10000000, +}; + +enum hal_mpeg4_profile { + HAL_MPEG4_PROFILE_SIMPLE = 0x00000001, + HAL_MPEG4_PROFILE_ADVANCEDSIMPLE = 0x00000002, + HAL_MPEG4_PROFILE_CORE = 0x00000004, + HAL_MPEG4_PROFILE_MAIN = 0x00000008, + HAL_MPEG4_PROFILE_NBIT = 0x00000010, + HAL_MPEG4_PROFILE_SCALABLETEXTURE = 0x00000020, + HAL_MPEG4_PROFILE_SIMPLEFACE = 0x00000040, + HAL_MPEG4_PROFILE_SIMPLEFBA = 0x00000080, + HAL_MPEG4_PROFILE_BASICANIMATED = 0x00000100, + HAL_MPEG4_PROFILE_HYBRID = 0x00000200, + HAL_MPEG4_PROFILE_ADVANCEDREALTIME = 0x00000400, + HAL_MPEG4_PROFILE_CORESCALABLE = 0x00000800, + HAL_MPEG4_PROFILE_ADVANCEDCODING = 0x00001000, + HAL_MPEG4_PROFILE_ADVANCEDCORE = 0x00002000, + HAL_MPEG4_PROFILE_ADVANCEDSCALABLE = 0x00004000, + HAL_MPEG4_PROFILE_SIMPLESCALABLE = 0x00008000, + HAL_UNUSED_MPEG4_PROFILE = 0x10000000, +}; + +enum hal_mpeg4_level { + HAL_MPEG4_LEVEL_0 = 0x00000001, + HAL_MPEG4_LEVEL_0b = 0x00000002, + HAL_MPEG4_LEVEL_1 = 0x00000004, + HAL_MPEG4_LEVEL_2 = 0x00000008, + HAL_MPEG4_LEVEL_3 = 0x00000010, + HAL_MPEG4_LEVEL_4 = 0x00000020, + HAL_MPEG4_LEVEL_4a = 0x00000040, + HAL_MPEG4_LEVEL_5 = 0x00000080, + HAL_MPEG4_LEVEL_VENDOR_START_UNUSED = 0x7F000000, + HAL_MPEG4_LEVEL_6 = 0x7F000001, + HAL_MPEG4_LEVEL_7 = 0x7F000002, + HAL_MPEG4_LEVEL_8 = 0x7F000003, + HAL_MPEG4_LEVEL_9 = 0x7F000004, + HAL_MPEG4_LEVEL_3b = 0x7F000005, + HAL_UNUSED_MPEG4_LEVEL = 0x10000000, +}; + +enum hal_h264_profile { + HAL_H264_PROFILE_BASELINE = 0x00000001, + HAL_H264_PROFILE_MAIN = 0x00000002, + HAL_H264_PROFILE_HIGH = 0x00000004, + HAL_H264_PROFILE_EXTENDED = 0x00000008, + HAL_H264_PROFILE_HIGH10 = 0x00000010, + HAL_H264_PROFILE_HIGH422 = 0x00000020, + HAL_H264_PROFILE_HIGH444 = 0x00000040, + HAL_H264_PROFILE_CONSTRAINED_BASE = 0x00000080, + HAL_H264_PROFILE_CONSTRAINED_HIGH = 0x00000100, + HAL_UNUSED_H264_PROFILE = 0x10000000, +}; + +enum hal_h264_level { + HAL_H264_LEVEL_1 = 0x00000001, + HAL_H264_LEVEL_1b = 0x00000002, + HAL_H264_LEVEL_11 = 0x00000004, + HAL_H264_LEVEL_12 = 0x00000008, + HAL_H264_LEVEL_13 = 0x00000010, + HAL_H264_LEVEL_2 = 0x00000020, + HAL_H264_LEVEL_21 = 0x00000040, + HAL_H264_LEVEL_22 = 0x00000080, + HAL_H264_LEVEL_3 = 0x00000100, + HAL_H264_LEVEL_31 = 0x00000200, + HAL_H264_LEVEL_32 = 0x00000400, + HAL_H264_LEVEL_4 = 0x00000800, + HAL_H264_LEVEL_41 = 0x00001000, + HAL_H264_LEVEL_42 = 0x00002000, + HAL_H264_LEVEL_5 = 0x00004000, + HAL_H264_LEVEL_51 = 0x00008000, + HAL_H264_LEVEL_52 = 0x00010000, + HAL_UNUSED_H264_LEVEL = 0x10000000, +}; + +enum hal_hevc_profile { + HAL_HEVC_PROFILE_MAIN = 0x00000001, + HAL_HEVC_PROFILE_MAIN10 = 0x00000002, + HAL_HEVC_PROFILE_MAIN_STILL_PIC = 0x00000004, + HAL_UNUSED_HEVC_PROFILE = 0x10000000, +}; + +enum hal_hevc_level { + HAL_HEVC_MAIN_TIER_LEVEL_1 = 0x10000001, + HAL_HEVC_MAIN_TIER_LEVEL_2 = 0x10000002, + HAL_HEVC_MAIN_TIER_LEVEL_2_1 = 0x10000004, + HAL_HEVC_MAIN_TIER_LEVEL_3 = 0x10000008, + HAL_HEVC_MAIN_TIER_LEVEL_3_1 = 0x10000010, + HAL_HEVC_MAIN_TIER_LEVEL_4 = 0x10000020, + HAL_HEVC_MAIN_TIER_LEVEL_4_1 = 0x10000040, + HAL_HEVC_MAIN_TIER_LEVEL_5 = 0x10000080, + HAL_HEVC_MAIN_TIER_LEVEL_5_1 = 0x10000100, + HAL_HEVC_MAIN_TIER_LEVEL_5_2 = 0x10000200, + HAL_HEVC_MAIN_TIER_LEVEL_6 = 0x10000400, + HAL_HEVC_MAIN_TIER_LEVEL_6_1 = 0x10000800, + HAL_HEVC_MAIN_TIER_LEVEL_6_2 = 0x10001000, + HAL_HEVC_HIGH_TIER_LEVEL_1 = 0x20000001, + HAL_HEVC_HIGH_TIER_LEVEL_2 = 0x20000002, + HAL_HEVC_HIGH_TIER_LEVEL_2_1 = 0x20000004, + HAL_HEVC_HIGH_TIER_LEVEL_3 = 0x20000008, + HAL_HEVC_HIGH_TIER_LEVEL_3_1 = 0x20000010, + HAL_HEVC_HIGH_TIER_LEVEL_4 = 0x20000020, + HAL_HEVC_HIGH_TIER_LEVEL_4_1 = 0x20000040, + HAL_HEVC_HIGH_TIER_LEVEL_5 = 0x20000080, + HAL_HEVC_HIGH_TIER_LEVEL_5_1 = 0x20000100, + HAL_HEVC_HIGH_TIER_LEVEL_5_2 = 0x20000200, + HAL_HEVC_HIGH_TIER_LEVEL_6 = 0x20000400, + HAL_HEVC_HIGH_TIER_LEVEL_6_1 = 0x20000800, + HAL_HEVC_HIGH_TIER_LEVEL_6_2 = 0x20001000, + HAL_UNUSED_HEVC_TIER_LEVEL = 0x80000000, +}; + +enum hal_hevc_tier { + HAL_HEVC_TIER_MAIN = 0x00000001, + HAL_HEVC_TIER_HIGH = 0x00000002, + HAL_UNUSED_HEVC_TIER = 0x10000000, +}; + +enum hal_vpx_profile { + HAL_VPX_PROFILE_SIMPLE = 0x00000001, + HAL_VPX_PROFILE_ADVANCED = 0x00000002, + HAL_VPX_PROFILE_VERSION_0 = 0x00000004, + HAL_VPX_PROFILE_VERSION_1 = 0x00000008, + HAL_VPX_PROFILE_VERSION_2 = 0x00000010, + HAL_VPX_PROFILE_VERSION_3 = 0x00000020, + HAL_VPX_PROFILE_UNUSED = 0x10000000, +}; + +enum hal_vc1_profile { + HAL_VC1_PROFILE_SIMPLE = 0x00000001, + HAL_VC1_PROFILE_MAIN = 0x00000002, + HAL_VC1_PROFILE_ADVANCED = 0x00000004, + HAL_UNUSED_VC1_PROFILE = 0x10000000, +}; + +enum hal_vc1_level { + HAL_VC1_LEVEL_LOW = 0x00000001, + HAL_VC1_LEVEL_MEDIUM = 0x00000002, + HAL_VC1_LEVEL_HIGH = 0x00000004, + HAL_VC1_LEVEL_0 = 0x00000008, + HAL_VC1_LEVEL_1 = 0x00000010, + HAL_VC1_LEVEL_2 = 0x00000020, + HAL_VC1_LEVEL_3 = 0x00000040, + HAL_VC1_LEVEL_4 = 0x00000080, + HAL_UNUSED_VC1_LEVEL = 0x10000000, +}; + +enum hal_divx_format { + HAL_DIVX_FORMAT_4, + HAL_DIVX_FORMAT_5, + HAL_DIVX_FORMAT_6, + HAL_UNUSED_DIVX_FORMAT = 0x10000000, +}; + +enum hal_divx_profile { + HAL_DIVX_PROFILE_QMOBILE = 0x00000001, + HAL_DIVX_PROFILE_MOBILE = 0x00000002, + HAL_DIVX_PROFILE_MT = 0x00000004, + HAL_DIVX_PROFILE_HT = 0x00000008, + HAL_DIVX_PROFILE_HD = 0x00000010, + HAL_UNUSED_DIVX_PROFILE = 0x10000000, +}; + +enum hal_mvc_profile { + HAL_MVC_PROFILE_STEREO_HIGH = 0x00001000, + HAL_UNUSED_MVC_PROFILE = 0x10000000, +}; + +enum hal_mvc_level { + HAL_MVC_LEVEL_1 = 0x00000001, + HAL_MVC_LEVEL_1b = 0x00000002, + HAL_MVC_LEVEL_11 = 0x00000004, + HAL_MVC_LEVEL_12 = 0x00000008, + HAL_MVC_LEVEL_13 = 0x00000010, + HAL_MVC_LEVEL_2 = 0x00000020, + HAL_MVC_LEVEL_21 = 0x00000040, + HAL_MVC_LEVEL_22 = 0x00000080, + HAL_MVC_LEVEL_3 = 0x00000100, + HAL_MVC_LEVEL_31 = 0x00000200, + HAL_MVC_LEVEL_32 = 0x00000400, + HAL_MVC_LEVEL_4 = 0x00000800, + HAL_MVC_LEVEL_41 = 0x00001000, + HAL_MVC_LEVEL_42 = 0x00002000, + HAL_MVC_LEVEL_5 = 0x00004000, + HAL_MVC_LEVEL_51 = 0x00008000, + HAL_UNUSED_MVC_LEVEL = 0x10000000, +}; + +struct hal_frame_rate { + enum hal_buffer buffer_type; + u32 frame_rate; +}; + +enum hal_uncompressed_format { + HAL_COLOR_FORMAT_MONOCHROME = 0x00000001, + HAL_COLOR_FORMAT_NV12 = 0x00000002, + HAL_COLOR_FORMAT_NV21 = 0x00000004, + HAL_COLOR_FORMAT_NV12_4x4TILE = 0x00000008, + HAL_COLOR_FORMAT_NV21_4x4TILE = 0x00000010, + HAL_COLOR_FORMAT_YUYV = 0x00000020, + HAL_COLOR_FORMAT_YVYU = 0x00000040, + HAL_COLOR_FORMAT_UYVY = 0x00000080, + HAL_COLOR_FORMAT_VYUY = 0x00000100, + HAL_COLOR_FORMAT_RGB565 = 0x00000200, + HAL_COLOR_FORMAT_BGR565 = 0x00000400, + HAL_COLOR_FORMAT_RGB888 = 0x00000800, + HAL_COLOR_FORMAT_BGR888 = 0x00001000, + HAL_COLOR_FORMAT_NV12_UBWC = 0x00002000, + HAL_COLOR_FORMAT_NV12_TP10_UBWC = 0x00004000, + HAL_COLOR_FORMAT_RGBA8888 = 0x00008000, + HAL_COLOR_FORMAT_RGBA8888_UBWC = 0x00010000, + HAL_UNUSED_COLOR = 0x10000000, +}; + +enum hal_statistics_mode_type { + HAL_STATISTICS_MODE_DEFAULT = 0x00000001, + HAL_STATISTICS_MODE_1 = 0x00000002, + HAL_STATISTICS_MODE_2 = 0x00000004, + HAL_STATISTICS_MODE_3 = 0x00000008, +}; + +enum hal_ssr_trigger_type { + SSR_ERR_FATAL = 1, + SSR_SW_DIV_BY_ZERO, + SSR_HW_WDOG_IRQ, +}; + +struct hal_uncompressed_format_select { + enum hal_buffer buffer_type; + enum hal_uncompressed_format format; +}; + +struct hal_uncompressed_plane_actual { + int actual_stride; + u32 actual_plane_buffer_height; +}; + +struct hal_uncompressed_plane_actual_info { + enum hal_buffer buffer_type; + u32 num_planes; + struct hal_uncompressed_plane_actual rg_plane_format[1]; +}; + +struct hal_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hal_uncompressed_plane_actual_constraints_info { + enum hal_buffer buffer_type; + u32 num_planes; + struct hal_uncompressed_plane_constraints rg_plane_format[1]; +}; + +struct hal_extra_data_header_config { + u32 type; + enum hal_buffer buffer_type; + u32 version; + u32 port_index; + u32 client_extradata_id; +}; + +struct hal_frame_size { + enum hal_buffer buffer_type; + u32 width; + u32 height; +}; + +struct hal_enable { + bool enable; +}; + +struct hal_buffer_count_actual { + enum hal_buffer buffer_type; + u32 buffer_count_actual; +}; + +struct hal_buffer_size_minimum { + enum hal_buffer buffer_type; + u32 buffer_size; +}; + +struct hal_buffer_display_hold_count_actual { + enum hal_buffer buffer_type; + u32 hold_count; +}; + +enum hal_nal_stream_format { + HAL_NAL_FORMAT_STARTCODES = 0x00000001, + HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER = 0x00000002, + HAL_NAL_FORMAT_ONE_BYTE_LENGTH = 0x00000004, + HAL_NAL_FORMAT_TWO_BYTE_LENGTH = 0x00000008, + HAL_NAL_FORMAT_FOUR_BYTE_LENGTH = 0x00000010, +}; + +enum hal_output_order { + HAL_OUTPUT_ORDER_DISPLAY, + HAL_OUTPUT_ORDER_DECODE, + HAL_UNUSED_OUTPUT = 0x10000000, +}; + +enum hal_picture { + HAL_PICTURE_I = 0x01, + HAL_PICTURE_P = 0x02, + HAL_PICTURE_B = 0x04, + HAL_PICTURE_IDR = 0x08, + HAL_PICTURE_CRA = 0x10, + HAL_FRAME_NOTCODED = 0x7F002000, + HAL_FRAME_YUV = 0x7F004000, + HAL_UNUSED_PICT = 0x10000000, +}; + +struct hal_extradata_enable { + u32 enable; + enum hal_extradata_id index; +}; + +struct hal_enable_picture { + u32 picture_type; +}; + +struct hal_multi_stream { + enum hal_buffer buffer_type; + u32 enable; + u32 width; + u32 height; +}; + +struct hal_display_picture_buffer_count { + u32 enable; + u32 count; +}; + +struct hal_mb_error_map { + u32 error_map_size; + u8 rg_error_map[1]; +}; + +struct hal_request_iframe { + u32 enable; +}; + +struct hal_bitrate { + u32 bit_rate; + u32 layer_id; +}; + +struct hal_profile_level { + u32 profile; + u32 level; +}; + +struct hal_profile_level_supported { + u32 profile_count; + struct hal_profile_level profile_level[MAX_PROFILE_COUNT]; +}; + +enum hal_h264_entropy { + HAL_H264_ENTROPY_CAVLC = 1, + HAL_H264_ENTROPY_CABAC = 2, + HAL_UNUSED_ENTROPY = 0x10000000, +}; + +enum hal_h264_cabac_model { + HAL_H264_CABAC_MODEL_0 = 1, + HAL_H264_CABAC_MODEL_1 = 2, + HAL_H264_CABAC_MODEL_2 = 4, + HAL_UNUSED_CABAC = 0x10000000, +}; + +struct hal_h264_entropy_control { + enum hal_h264_entropy entropy_mode; + enum hal_h264_cabac_model cabac_model; +}; + +enum hal_rate_control { + HAL_RATE_CONTROL_OFF, + HAL_RATE_CONTROL_VBR_VFR, + HAL_RATE_CONTROL_VBR_CFR, + HAL_RATE_CONTROL_CBR_VFR, + HAL_RATE_CONTROL_CBR_CFR, + HAL_RATE_CONTROL_MBR_CFR, + HAL_RATE_CONTROL_MBR_VFR, + HAL_UNUSED_RC = 0x10000000, +}; + +struct hal_mpeg4_time_resolution { + u32 time_increment_resolution; +}; + +struct hal_mpeg4_header_extension { + u32 header_extension; +}; + +enum hal_h264_db_mode { + HAL_H264_DB_MODE_DISABLE, + HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY, + HAL_H264_DB_MODE_ALL_BOUNDARY, + HAL_UNUSED_H264_DB = 0x10000000, +}; + +struct hal_h264_db_control { + enum hal_h264_db_mode mode; + int slice_alpha_offset; + int slice_beta_offset; +}; + +struct hal_temporal_spatial_tradeoff { + u32 ts_factor; +}; + +struct hal_quantization { + u32 qpi; + u32 qpp; + u32 qpb; + u32 layer_id; +}; + +struct hal_initial_quantization { + u32 qpi; + u32 qpp; + u32 qpb; + u32 init_qp_enable; +}; + +struct hal_quantization_range { + u32 min_qp; + u32 max_qp; + u32 layer_id; +}; + +struct hal_intra_period { + u32 pframes; + u32 bframes; +}; + +struct hal_idr_period { + u32 idr_period; +}; + +enum hal_rotate { + HAL_ROTATE_NONE, + HAL_ROTATE_90, + HAL_ROTATE_180, + HAL_ROTATE_270, + HAL_UNUSED_ROTATE = 0x10000000, +}; + +enum hal_flip { + HAL_FLIP_NONE, + HAL_FLIP_HORIZONTAL, + HAL_FLIP_VERTICAL, + HAL_UNUSED_FLIP = 0x10000000, +}; + +struct hal_operations { + enum hal_rotate rotate; + enum hal_flip flip; +}; + +enum hal_intra_refresh_mode { + HAL_INTRA_REFRESH_NONE, + HAL_INTRA_REFRESH_CYCLIC, + HAL_INTRA_REFRESH_ADAPTIVE, + HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE, + HAL_INTRA_REFRESH_RANDOM, + HAL_UNUSED_INTRA = 0x10000000, +}; + +struct hal_intra_refresh { + enum hal_intra_refresh_mode mode; + u32 air_mbs; + u32 air_ref; + u32 cir_mbs; +}; + +enum hal_multi_slice { + HAL_MULTI_SLICE_OFF, + HAL_MULTI_SLICE_BY_MB_COUNT, + HAL_MULTI_SLICE_BY_BYTE_COUNT, + HAL_MULTI_SLICE_GOB, + HAL_UNUSED_SLICE = 0x10000000, +}; + +struct hal_multi_slice_control { + enum hal_multi_slice multi_slice; + u32 slice_size; +}; + +struct hal_debug_config { + u32 debug_config; +}; + +struct hal_buffer_requirements { + enum hal_buffer buffer_type; + u32 buffer_size; + u32 buffer_region_size; + u32 buffer_hold_count; + u32 buffer_count_min; + u32 buffer_count_actual; + u32 contiguous; + u32 buffer_alignment; +}; + +enum hal_priority {/* Priority increases with number */ + HAL_PRIORITY_LOW = 10, + HAL_PRIOIRTY_MEDIUM = 20, + HAL_PRIORITY_HIGH = 30, + HAL_UNUSED_PRIORITY = 0x10000000, +}; + +struct hal_batch_info { + u32 input_batch_count; + u32 output_batch_count; +}; + +struct hal_metadata_pass_through { + u32 enable; + u32 size; +}; + +struct hal_uncompressed_format_supported { + enum hal_buffer buffer_type; + u32 format_entries; + u32 rg_format_info[1]; +}; + +enum hal_interlace_format { + HAL_INTERLACE_FRAME_PROGRESSIVE = 0x01, + HAL_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST = 0x02, + HAL_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04, + HAL_INTERLACE_FRAME_TOPFIELDFIRST = 0x08, + HAL_INTERLACE_FRAME_BOTTOMFIELDFIRST = 0x10, + HAL_UNUSED_INTERLACE = 0x10000000, +}; + +struct hal_interlace_format_supported { + enum hal_buffer buffer_type; + enum hal_interlace_format format; +}; + +enum hal_chroma_site { + HAL_CHROMA_SITE_0, + HAL_CHROMA_SITE_1, + HAL_UNUSED_CHROMA = 0x10000000, +}; + +struct hal_properties_supported { + u32 num_properties; + u32 rg_properties[1]; +}; + +enum hal_capability { + HAL_CAPABILITY_FRAME_WIDTH = 0x1, + HAL_CAPABILITY_FRAME_HEIGHT, + HAL_CAPABILITY_MBS_PER_FRAME, + HAL_CAPABILITY_MBS_PER_SECOND, + HAL_CAPABILITY_FRAMERATE, + HAL_CAPABILITY_SCALE_X, + HAL_CAPABILITY_SCALE_Y, + HAL_CAPABILITY_BITRATE, + HAL_CAPABILITY_BFRAME, + HAL_CAPABILITY_PEAKBITRATE, + HAL_CAPABILITY_HIER_P_NUM_ENH_LAYERS, + HAL_CAPABILITY_ENC_LTR_COUNT, + HAL_CAPABILITY_SECURE_OUTPUT2_THRESHOLD, + HAL_CAPABILITY_HIER_B_NUM_ENH_LAYERS, + HAL_CAPABILITY_LCU_SIZE, + HAL_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, + HAL_CAPABILITY_MBS_PER_SECOND_POWER_SAVE, + HAL_UNUSED_CAPABILITY = 0x10000000, +}; + +struct hal_capability_supported { + enum hal_capability capability_type; + u32 min; + u32 max; + u32 step_size; +}; + +struct hal_capability_supported_info { + u32 num_capabilities; + struct hal_capability_supported rg_data[1]; +}; + +struct hal_nal_stream_format_supported { + u32 nal_stream_format_supported; +}; + +struct hal_nal_stream_format_select { + u32 nal_stream_format_select; +}; + +struct hal_multi_view_format { + u32 views; + u32 rg_view_order[1]; +}; + +enum hal_buffer_layout_type { + HAL_BUFFER_LAYOUT_TOP_BOTTOM, + HAL_BUFFER_LAYOUT_SEQ, + HAL_UNUSED_BUFFER_LAYOUT = 0x10000000, +}; + +struct hal_mvc_buffer_layout { + enum hal_buffer_layout_type layout_type; + u32 bright_view_first; + u32 ngap; +}; + +struct hal_seq_header_info { + u32 nax_header_len; +}; + +struct hal_aspect_ratio { + u32 aspect_width; + u32 aspect_height; +}; + +struct hal_codec_supported { + u32 decoder_codec_supported; + u32 encoder_codec_supported; +}; + +struct hal_multi_view_select { + u32 view_index; +}; + +struct hal_timestamp_scale { + u32 time_stamp_scale; +}; + + +struct hal_h264_vui_timing_info { + u32 enable; + u32 fixed_frame_rate; + u32 time_scale; +}; + +struct hal_h264_vui_bitstream_restrc { + u32 enable; +}; + +struct hal_preserve_text_quality { + u32 enable; +}; + +struct hal_vc1e_perf_cfg_type { + struct { + u32 x_subsampled; + u32 y_subsampled; + } i_frame, p_frame, b_frame; +}; + +struct hal_vpe_color_space_conversion { + u32 csc_matrix[HAL_MAX_MATRIX_COEFFS]; + u32 csc_bias[HAL_MAX_BIAS_COEFFS]; + u32 csc_limit[HAL_MAX_LIMIT_COEFFS]; +}; + +struct hal_video_signal_info { + u32 color_space; + u32 transfer_chars; + u32 matrix_coeffs; + bool full_range; +}; + +enum hal_iframesize_type { + HAL_IFRAMESIZE_TYPE_DEFAULT, + HAL_IFRAMESIZE_TYPE_MEDIUM, + HAL_IFRAMESIZE_TYPE_HUGE, + HAL_IFRAMESIZE_TYPE_UNLIMITED, +}; + +enum vidc_resource_id { + VIDC_RESOURCE_NONE, + VIDC_RESOURCE_OCMEM, + VIDC_RESOURCE_VMEM, + VIDC_UNUSED_RESOURCE = 0x10000000, +}; + +struct vidc_resource_hdr { + enum vidc_resource_id resource_id; + void *resource_handle; + u32 size; +}; + +struct vidc_buffer_addr_info { + enum hal_buffer buffer_type; + u32 buffer_size; + u32 num_buffers; + phys_addr_t align_device_addr; + phys_addr_t extradata_addr; + u32 extradata_size; + u32 response_required; +}; + +/* Needs to be exactly the same as hfi_buffer_info */ +struct hal_buffer_info { + u32 buffer_addr; + u32 extra_data_addr; +}; + +struct vidc_frame_plane_config { + u32 left; + u32 top; + u32 width; + u32 height; + u32 stride; + u32 scan_lines; +}; + +struct vidc_uncompressed_frame_config { + struct vidc_frame_plane_config luma_plane; + struct vidc_frame_plane_config chroma_plane; +}; + +struct vidc_frame_data { + enum hal_buffer buffer_type; + phys_addr_t device_addr; + phys_addr_t extradata_addr; + int64_t timestamp; + u32 flags; + u32 offset; + u32 alloc_len; + u32 filled_len; + u32 mark_target; + u32 mark_data; + u32 clnt_data; + u32 extradata_size; +}; + +struct vidc_seq_hdr { + phys_addr_t seq_hdr; + u32 seq_hdr_len; +}; + +struct hal_fw_info { + char version[VENUS_VERSION_LENGTH]; + phys_addr_t base_addr; + int register_base; + int register_size; + int irq; +}; + +enum hal_flush { + HAL_FLUSH_INPUT, + HAL_FLUSH_OUTPUT, + HAL_FLUSH_ALL, + HAL_UNUSED_FLUSH = 0x10000000, +}; + +enum hal_event_type { + HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES, + HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES, + HAL_EVENT_RELEASE_BUFFER_REFERENCE, + HAL_UNUSED_SEQCHG = 0x10000000, +}; + +enum buffer_mode_type { + HAL_BUFFER_MODE_STATIC = 0x001, + HAL_BUFFER_MODE_RING = 0x010, + HAL_BUFFER_MODE_DYNAMIC = 0x100, +}; + +struct hal_buffer_alloc_mode { + enum hal_buffer buffer_type; + enum buffer_mode_type buffer_mode; +}; + +enum ltr_mode { + HAL_LTR_MODE_DISABLE, + HAL_LTR_MODE_MANUAL, + HAL_LTR_MODE_PERIODIC, +}; + +struct hal_ltr_mode { + enum ltr_mode mode; + u32 count; + u32 trust_mode; +}; + +struct hal_ltr_use { + u32 ref_ltr; + u32 use_constraint; + u32 frames; +}; + +struct hal_ltr_mark { + u32 mark_frame; +}; + +enum hal_perf_mode { + HAL_PERF_MODE_POWER_SAVE, + HAL_PERF_MODE_POWER_MAX_QUALITY, +}; + +struct hal_hybrid_hierp { + u32 layers; +}; + +struct hal_scs_threshold { + u32 threshold_value; +}; + +struct buffer_requirements { + struct hal_buffer_requirements buffer[HAL_BUFFER_MAX]; +}; + +union hal_get_property { + struct hal_frame_rate frame_rate; + struct hal_uncompressed_format_select format_select; + struct hal_uncompressed_plane_actual plane_actual; + struct hal_uncompressed_plane_actual_info plane_actual_info; + struct hal_uncompressed_plane_constraints plane_constraints; + struct hal_uncompressed_plane_actual_constraints_info + plane_constraints_info; + struct hal_extra_data_header_config extra_data_header_config; + struct hal_frame_size frame_size; + struct hal_enable enable; + struct hal_buffer_count_actual buffer_count_actual; + struct hal_extradata_enable extradata_enable; + struct hal_enable_picture enable_picture; + struct hal_multi_stream multi_stream; + struct hal_display_picture_buffer_count display_picture_buffer_count; + struct hal_mb_error_map mb_error_map; + struct hal_request_iframe request_iframe; + struct hal_bitrate bitrate; + struct hal_profile_level profile_level; + struct hal_profile_level_supported profile_level_supported; + struct hal_mpeg4_time_resolution mpeg4_time_resolution; + struct hal_mpeg4_header_extension mpeg4_header_extension; + struct hal_h264_db_control h264_db_control; + struct hal_temporal_spatial_tradeoff temporal_spatial_tradeoff; + struct hal_quantization quantization; + struct hal_quantization_range quantization_range; + struct hal_intra_period intra_period; + struct hal_idr_period idr_period; + struct hal_operations operations; + struct hal_intra_refresh intra_refresh; + struct hal_multi_slice_control multi_slice_control; + struct hal_debug_config debug_config; + struct hal_batch_info batch_info; + struct hal_metadata_pass_through metadata_pass_through; + struct hal_uncompressed_format_supported uncompressed_format_supported; + struct hal_interlace_format_supported interlace_format_supported; + struct hal_properties_supported properties_supported; + struct hal_capability_supported capability_supported; + struct hal_capability_supported_info capability_supported_info; + struct hal_nal_stream_format_supported nal_stream_format_supported; + struct hal_nal_stream_format_select nal_stream_format_select; + struct hal_multi_view_format multi_view_format; + struct hal_seq_header_info seq_header_info; + struct hal_codec_supported codec_supported; + struct hal_multi_view_select multi_view_select; + struct hal_timestamp_scale timestamp_scale; + struct hal_h264_vui_timing_info h264_vui_timing_info; + struct hal_h264_vui_bitstream_restrc h264_vui_bitstream_restrc; + struct hal_preserve_text_quality preserve_text_quality; + struct hal_buffer_info buffer_info; + struct hal_buffer_alloc_mode buffer_alloc_mode; + struct buffer_requirements buf_req; + enum hal_h264_entropy h264_entropy; +}; + +/* HAL Response */ +#define IS_HAL_SYS_CMD(cmd) ((cmd) >= HAL_SYS_INIT_DONE && \ + (cmd) <= HAL_SYS_ERROR) +#define IS_HAL_SESSION_CMD(cmd) ((cmd) >= HAL_SESSION_EVENT_CHANGE && \ + (cmd) <= HAL_SESSION_ERROR) +enum hal_command_response { + /* SYSTEM COMMANDS_DONE*/ + HAL_SYS_INIT_DONE, + HAL_SYS_SET_RESOURCE_DONE, + HAL_SYS_RELEASE_RESOURCE_DONE, + HAL_SYS_PING_ACK_DONE, + HAL_SYS_PC_PREP_DONE, + HAL_SYS_IDLE, + HAL_SYS_DEBUG, + HAL_SYS_WATCHDOG_TIMEOUT, + HAL_SYS_ERROR, + /* SESSION COMMANDS_DONE */ + HAL_SESSION_EVENT_CHANGE, + HAL_SESSION_LOAD_RESOURCE_DONE, + HAL_SESSION_INIT_DONE, + HAL_SESSION_END_DONE, + HAL_SESSION_ABORT_DONE, + HAL_SESSION_START_DONE, + HAL_SESSION_STOP_DONE, + HAL_SESSION_ETB_DONE, + HAL_SESSION_FTB_DONE, + HAL_SESSION_FLUSH_DONE, + HAL_SESSION_SUSPEND_DONE, + HAL_SESSION_RESUME_DONE, + HAL_SESSION_SET_PROP_DONE, + HAL_SESSION_GET_PROP_DONE, + HAL_SESSION_PARSE_SEQ_HDR_DONE, + HAL_SESSION_GET_SEQ_HDR_DONE, + HAL_SESSION_RELEASE_BUFFER_DONE, + HAL_SESSION_RELEASE_RESOURCE_DONE, + HAL_SESSION_PROPERTY_INFO, + HAL_SESSION_ERROR, + HAL_RESPONSE_UNUSED = 0x10000000, +}; + +struct vidc_hal_ebd { + u32 timestamp_hi; + u32 timestamp_lo; + u32 flags; + enum vidc_status status; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 offset; + u32 alloc_len; + u32 filled_len; + enum hal_picture picture_type; + phys_addr_t packet_buffer; + phys_addr_t extra_data_buffer; +}; + +struct vidc_hal_fbd { + u32 stream_id; + u32 view_id; + u32 timestamp_hi; + u32 timestamp_lo; + u32 flags1; + u32 mark_target; + u32 mark_data; + u32 stats; + u32 alloc_len1; + u32 filled_len1; + u32 offset1; + u32 frame_width; + u32 frame_height; + u32 start_x_coord; + u32 start_y_coord; + u32 input_tag; + u32 input_tag1; + enum hal_picture picture_type; + phys_addr_t packet_buffer1; + phys_addr_t extra_data_buffer; + u32 flags2; + u32 alloc_len2; + u32 filled_len2; + u32 offset2; + phys_addr_t packet_buffer2; + u32 flags3; + u32 alloc_len3; + u32 filled_len3; + u32 offset3; + phys_addr_t packet_buffer3; + enum hal_buffer buffer_type; +}; + +struct msm_vidc_capability { + enum hal_domain domain; + enum hal_video_codec codec; + struct hal_capability_supported width; + struct hal_capability_supported height; + struct hal_capability_supported mbs_per_frame; + struct hal_capability_supported mbs_per_sec; + struct hal_capability_supported frame_rate; + struct hal_capability_supported scale_x; + struct hal_capability_supported scale_y; + struct hal_capability_supported bitrate; + struct hal_capability_supported bframe; + struct hal_capability_supported peakbitrate; + struct hal_capability_supported hier_p; + struct hal_capability_supported ltr_count; + struct hal_capability_supported secure_output2_threshold; + struct hal_capability_supported hier_b; + struct hal_capability_supported lcu_size; + struct hal_capability_supported hier_p_hybrid; + struct hal_capability_supported mbs_per_sec_power_save; + struct hal_profile_level_supported profile_level; + struct hal_uncompressed_format_supported uncomp_format; + struct hal_interlace_format_supported HAL_format; + struct hal_nal_stream_format_supported nal_stream_format; + struct hal_intra_refresh intra_refresh; + enum buffer_mode_type alloc_mode_out; + enum buffer_mode_type alloc_mode_in; + u32 pixelprocess_capabilities; +}; + +struct vidc_hal_sys_init_done { + u32 dec_codec_supported; + u32 enc_codec_supported; + u32 codec_count; + struct msm_vidc_capability *capabilities; + u32 max_sessions_supported; +}; + +struct vidc_hal_session_init_done { + struct msm_vidc_capability capability; +}; + +struct msm_vidc_cb_cmd_done { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 size; + union { + struct vidc_resource_hdr resource_hdr; + struct vidc_buffer_addr_info buffer_addr_info; + struct vidc_frame_plane_config frame_plane_config; + struct vidc_uncompressed_frame_config uncompressed_frame_config; + struct vidc_frame_data frame_data; + struct vidc_seq_hdr seq_hdr; + struct vidc_hal_ebd ebd; + struct vidc_hal_fbd fbd; + struct vidc_hal_sys_init_done sys_init_done; + struct vidc_hal_session_init_done session_init_done; + struct hal_buffer_info buffer_info; + union hal_get_property property; + enum hal_flush flush_type; + } data; +}; + +struct msm_vidc_cb_event { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 height; + u32 width; + enum msm_vidc_pixel_depth bit_depth; + u32 hal_event_type; + phys_addr_t packet_buffer; + phys_addr_t extra_data_buffer; + u32 pic_struct; + u32 colour_space; +}; + +struct msm_vidc_cb_data_done { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 size; + u32 clnt_data; + union { + struct vidc_hal_ebd input_done; + struct vidc_hal_fbd output_done; + }; +}; + +struct msm_vidc_cb_info { + enum hal_command_response response_type; + union { + struct msm_vidc_cb_cmd_done cmd; + struct msm_vidc_cb_event event; + struct msm_vidc_cb_data_done data; + } response; +}; + +enum msm_vidc_hfi_type { + VIDC_HFI_VENUS, +}; + +enum msm_vidc_thermal_level { + VIDC_THERMAL_NORMAL = 0, + VIDC_THERMAL_LOW, + VIDC_THERMAL_HIGH, + VIDC_THERMAL_CRITICAL +}; + +enum vidc_vote_data_session { + VIDC_BUS_VOTE_DATA_SESSION_INVALID = 0, + /* No declarations exist. Values generated by VIDC_VOTE_DATA_SESSION_VAL + * describe the enumerations e.g.: + * + * enum vidc_bus_vote_data_session_type h264_decoder_session = + * VIDC_VOTE_DATA_SESSION_VAL(HAL_VIDEO_CODEC_H264, + * HAL_VIDEO_DOMAIN_DECODER); + */ +}; + +/* Careful modifying VIDC_VOTE_DATA_SESSION_VAL(). + * + * This macro assigns two bits to each codec: the lower bit denoting the codec + * type, and the higher bit denoting session type. + */ +static inline enum vidc_vote_data_session VIDC_VOTE_DATA_SESSION_VAL( + enum hal_video_codec c, enum hal_domain d) { + if (d != HAL_VIDEO_DOMAIN_ENCODER && d != HAL_VIDEO_DOMAIN_DECODER) + return VIDC_BUS_VOTE_DATA_SESSION_INVALID; + + return (1 << ilog2(c) * 2) | ((d - 1) << (ilog2(c) * 2 + 1)); +} + +struct msm_vidc_gov_data { + struct vidc_bus_vote_data *data; + u32 data_count; + int imem_size; +}; + +enum msm_vidc_power_mode { + VIDC_POWER_NORMAL = 0, + VIDC_POWER_LOW, + VIDC_POWER_TURBO +}; + + +struct vidc_bus_vote_data { + enum hal_domain domain; + enum hal_video_codec codec; + enum hal_uncompressed_format color_formats[2]; + int num_formats; /* 1 = DPB-OPB unified; 2 = split */ + int height, width, fps; + enum msm_vidc_power_mode power_mode; + struct imem_ab_table *imem_ab_tbl; + u32 imem_ab_tbl_size; + unsigned long core_freq; +}; + + +struct vidc_clk_scale_data { + enum vidc_vote_data_session session[VIDC_MAX_SESSIONS]; + enum msm_vidc_power_mode power_mode[VIDC_MAX_SESSIONS]; + u32 load[VIDC_MAX_SESSIONS]; + int num_sessions; +}; + +struct hal_index_extradata_input_crop_payload { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + +struct hal_cmd_sys_get_property_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +#define call_hfi_op(q, op, args...) \ + (((q) && (q)->op) ? ((q)->op(args)) : 0) + +struct hfi_device { + void *hfi_device_data; + + /*Add function pointers for all the hfi functions below*/ + int (*core_init)(void *device); + int (*core_early_init)(void *device); + int (*core_release)(void *device); + int (*core_early_release)(void *device); + int (*core_ping)(void *device); + int (*core_trigger_ssr)(void *device, enum hal_ssr_trigger_type); + int (*session_init)(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type, + void **new_session); + int (*session_end)(void *session); + int (*session_abort)(void *session); + int (*session_set_buffers)(void *sess, + struct vidc_buffer_addr_info *buffer_info); + int (*session_release_buffers)(void *sess, + struct vidc_buffer_addr_info *buffer_info); + int (*session_load_res)(void *sess); + int (*session_release_res)(void *sess); + int (*session_start)(void *sess); + int (*session_continue)(void *sess); + int (*session_stop)(void *sess); + int (*session_etb)(void *sess, struct vidc_frame_data *input_frame); + int (*session_ftb)(void *sess, struct vidc_frame_data *output_frame); + int (*session_process_batch)(void *sess, + int num_etbs, struct vidc_frame_data etbs[], + int num_ftbs, struct vidc_frame_data ftbs[]); + int (*session_parse_seq_hdr)(void *sess, + struct vidc_seq_hdr *seq_hdr); + int (*session_get_seq_hdr)(void *sess, + struct vidc_seq_hdr *seq_hdr); + int (*session_get_buf_req)(void *sess); + int (*session_flush)(void *sess, enum hal_flush flush_mode); + int (*session_set_property)(void *sess, enum hal_property ptype, + void *pdata); + int (*session_get_property)(void *sess, enum hal_property ptype); + int (*scale_clocks)(void *dev, int load, + struct vidc_clk_scale_data *data, + unsigned long instant_bitrate); + int (*vote_bus)(void *dev, struct vidc_bus_vote_data *data, + int num_data); + int (*get_fw_info)(void *dev, struct hal_fw_info *fw_info); + int (*session_clean)(void *sess); + int (*get_core_capabilities)(void *dev); + int (*suspend)(void *dev); + unsigned long (*get_core_clock_rate)(void *dev, bool actual_rate); + enum hal_default_properties (*get_default_properties)(void *dev); +}; + +typedef void (*hfi_cmd_response_callback) (enum hal_command_response cmd, + void *data); +typedef void (*msm_vidc_callback) (u32 response, void *callback); + +struct hfi_device *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type, + u32 device_id, struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback); +void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type, + struct hfi_device *hdev); +u32 vidc_get_hfi_domain(enum hal_domain hal_domain); +u32 vidc_get_hfi_codec(enum hal_video_codec hal_codec); +enum hal_domain vidc_get_hal_domain(u32 hfi_domain); +enum hal_video_codec vidc_get_hal_codec(u32 hfi_codec); + +#endif /*__VIDC_HFI_API_H__ */ diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h new file mode 100644 index 000000000000..547cac27658d --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi_helper.h @@ -0,0 +1,1185 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __H_VIDC_HFI_HELPER_H__ +#define __H_VIDC_HFI_HELPER_H__ + +#define HFI_COMMON_BASE (0) +#define HFI_OX_BASE (0x01000000) + +#define HFI_VIDEO_DOMAIN_ENCODER (HFI_COMMON_BASE + 0x1) +#define HFI_VIDEO_DOMAIN_DECODER (HFI_COMMON_BASE + 0x2) +#define HFI_VIDEO_DOMAIN_VPE (HFI_COMMON_BASE + 0x4) +#define HFI_VIDEO_DOMAIN_MBI (HFI_COMMON_BASE + 0x8) + +#define HFI_DOMAIN_BASE_COMMON (HFI_COMMON_BASE + 0) +#define HFI_DOMAIN_BASE_VDEC (HFI_COMMON_BASE + 0x01000000) +#define HFI_DOMAIN_BASE_VENC (HFI_COMMON_BASE + 0x02000000) +#define HFI_DOMAIN_BASE_VPE (HFI_COMMON_BASE + 0x03000000) + +#define HFI_VIDEO_ARCH_OX (HFI_COMMON_BASE + 0x1) + +#define HFI_ARCH_COMMON_OFFSET (0) +#define HFI_ARCH_OX_OFFSET (0x00200000) + +#define HFI_CMD_START_OFFSET (0x00010000) +#define HFI_MSG_START_OFFSET (0x00020000) + +#define HFI_ERR_NONE HFI_COMMON_BASE +#define HFI_ERR_SYS_FATAL (HFI_COMMON_BASE + 0x1) +#define HFI_ERR_SYS_INVALID_PARAMETER (HFI_COMMON_BASE + 0x2) +#define HFI_ERR_SYS_VERSION_MISMATCH (HFI_COMMON_BASE + 0x3) +#define HFI_ERR_SYS_INSUFFICIENT_RESOURCES (HFI_COMMON_BASE + 0x4) +#define HFI_ERR_SYS_MAX_SESSIONS_REACHED (HFI_COMMON_BASE + 0x5) +#define HFI_ERR_SYS_UNSUPPORTED_CODEC (HFI_COMMON_BASE + 0x6) +#define HFI_ERR_SYS_SESSION_IN_USE (HFI_COMMON_BASE + 0x7) +#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE (HFI_COMMON_BASE + 0x8) +#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN (HFI_COMMON_BASE + 0x9) + +#define HFI_ERR_SESSION_FATAL (HFI_COMMON_BASE + 0x1001) +#define HFI_ERR_SESSION_INVALID_PARAMETER (HFI_COMMON_BASE + 0x1002) +#define HFI_ERR_SESSION_BAD_POINTER (HFI_COMMON_BASE + 0x1003) +#define HFI_ERR_SESSION_INVALID_SESSION_ID (HFI_COMMON_BASE + 0x1004) +#define HFI_ERR_SESSION_INVALID_STREAM_ID (HFI_COMMON_BASE + 0x1005) +#define HFI_ERR_SESSION_INCORRECT_STATE_OPERATION \ + (HFI_COMMON_BASE + 0x1006) +#define HFI_ERR_SESSION_UNSUPPORTED_PROPERTY (HFI_COMMON_BASE + 0x1007) + +#define HFI_ERR_SESSION_UNSUPPORTED_SETTING (HFI_COMMON_BASE + 0x1008) + +#define HFI_ERR_SESSION_INSUFFICIENT_RESOURCES (HFI_COMMON_BASE + 0x1009) + +#define HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED \ + (HFI_COMMON_BASE + 0x100A) + +#define HFI_ERR_SESSION_STREAM_CORRUPT (HFI_COMMON_BASE + 0x100B) +#define HFI_ERR_SESSION_ENC_OVERFLOW (HFI_COMMON_BASE + 0x100C) +#define HFI_ERR_SESSION_UNSUPPORTED_STREAM (HFI_COMMON_BASE + 0x100D) +#define HFI_ERR_SESSION_CMDSIZE (HFI_COMMON_BASE + 0x100E) +#define HFI_ERR_SESSION_UNSUPPORT_CMD (HFI_COMMON_BASE + 0x100F) +#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE (HFI_COMMON_BASE + 0x1010) +#define HFI_ERR_SESSION_BUFFERCOUNT_TOOSMALL (HFI_COMMON_BASE + 0x1011) +#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR (HFI_COMMON_BASE + 0x1012) +#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED (HFI_COMMON_BASE + 0x1013) + +#define HFI_EVENT_SYS_ERROR (HFI_COMMON_BASE + 0x1) +#define HFI_EVENT_SESSION_ERROR (HFI_COMMON_BASE + 0x2) + +#define HFI_VIDEO_CODEC_H264 0x00000002 +#define HFI_VIDEO_CODEC_H263 0x00000004 +#define HFI_VIDEO_CODEC_MPEG1 0x00000008 +#define HFI_VIDEO_CODEC_MPEG2 0x00000010 +#define HFI_VIDEO_CODEC_MPEG4 0x00000020 +#define HFI_VIDEO_CODEC_DIVX_311 0x00000040 +#define HFI_VIDEO_CODEC_DIVX 0x00000080 +#define HFI_VIDEO_CODEC_VC1 0x00000100 +#define HFI_VIDEO_CODEC_SPARK 0x00000200 +#define HFI_VIDEO_CODEC_VP8 0x00001000 +#define HFI_VIDEO_CODEC_HEVC 0x00002000 +#define HFI_VIDEO_CODEC_VP9 0x00004000 +#define HFI_VIDEO_CODEC_HEVC_HYBRID 0x80000000 + +#define HFI_H264_PROFILE_BASELINE 0x00000001 +#define HFI_H264_PROFILE_MAIN 0x00000002 +#define HFI_H264_PROFILE_HIGH 0x00000004 +#define HFI_H264_PROFILE_STEREO_HIGH 0x00000008 +#define HFI_H264_PROFILE_MULTIVIEW_HIGH 0x00000010 +#define HFI_H264_PROFILE_CONSTRAINED_BASE 0x00000020 +#define HFI_H264_PROFILE_CONSTRAINED_HIGH 0x00000040 + +#define HFI_H264_LEVEL_1 0x00000001 +#define HFI_H264_LEVEL_1b 0x00000002 +#define HFI_H264_LEVEL_11 0x00000004 +#define HFI_H264_LEVEL_12 0x00000008 +#define HFI_H264_LEVEL_13 0x00000010 +#define HFI_H264_LEVEL_2 0x00000020 +#define HFI_H264_LEVEL_21 0x00000040 +#define HFI_H264_LEVEL_22 0x00000080 +#define HFI_H264_LEVEL_3 0x00000100 +#define HFI_H264_LEVEL_31 0x00000200 +#define HFI_H264_LEVEL_32 0x00000400 +#define HFI_H264_LEVEL_4 0x00000800 +#define HFI_H264_LEVEL_41 0x00001000 +#define HFI_H264_LEVEL_42 0x00002000 +#define HFI_H264_LEVEL_5 0x00004000 +#define HFI_H264_LEVEL_51 0x00008000 +#define HFI_H264_LEVEL_52 0x00010000 + +#define HFI_H263_PROFILE_BASELINE 0x00000001 + +#define HFI_H263_LEVEL_10 0x00000001 +#define HFI_H263_LEVEL_20 0x00000002 +#define HFI_H263_LEVEL_30 0x00000004 +#define HFI_H263_LEVEL_40 0x00000008 +#define HFI_H263_LEVEL_45 0x00000010 +#define HFI_H263_LEVEL_50 0x00000020 +#define HFI_H263_LEVEL_60 0x00000040 +#define HFI_H263_LEVEL_70 0x00000080 + +#define HFI_MPEG2_PROFILE_SIMPLE 0x00000001 +#define HFI_MPEG2_PROFILE_MAIN 0x00000002 +#define HFI_MPEG2_PROFILE_422 0x00000004 +#define HFI_MPEG2_PROFILE_SNR 0x00000008 +#define HFI_MPEG2_PROFILE_SPATIAL 0x00000010 +#define HFI_MPEG2_PROFILE_HIGH 0x00000020 + +#define HFI_MPEG2_LEVEL_LL 0x00000001 +#define HFI_MPEG2_LEVEL_ML 0x00000002 +#define HFI_MPEG2_LEVEL_H14 0x00000004 +#define HFI_MPEG2_LEVEL_HL 0x00000008 + +#define HFI_MPEG4_PROFILE_SIMPLE 0x00000001 +#define HFI_MPEG4_PROFILE_ADVANCEDSIMPLE 0x00000002 + +#define HFI_MPEG4_LEVEL_0 0x00000001 +#define HFI_MPEG4_LEVEL_0b 0x00000002 +#define HFI_MPEG4_LEVEL_1 0x00000004 +#define HFI_MPEG4_LEVEL_2 0x00000008 +#define HFI_MPEG4_LEVEL_3 0x00000010 +#define HFI_MPEG4_LEVEL_4 0x00000020 +#define HFI_MPEG4_LEVEL_4a 0x00000040 +#define HFI_MPEG4_LEVEL_5 0x00000080 +#define HFI_MPEG4_LEVEL_6 0x00000100 +#define HFI_MPEG4_LEVEL_7 0x00000200 +#define HFI_MPEG4_LEVEL_8 0x00000400 +#define HFI_MPEG4_LEVEL_9 0x00000800 +#define HFI_MPEG4_LEVEL_3b 0x00001000 + +#define HFI_VC1_PROFILE_SIMPLE 0x00000001 +#define HFI_VC1_PROFILE_MAIN 0x00000002 +#define HFI_VC1_PROFILE_ADVANCED 0x00000004 + +#define HFI_VC1_LEVEL_LOW 0x00000001 +#define HFI_VC1_LEVEL_MEDIUM 0x00000002 +#define HFI_VC1_LEVEL_HIGH 0x00000004 +#define HFI_VC1_LEVEL_0 0x00000008 +#define HFI_VC1_LEVEL_1 0x00000010 +#define HFI_VC1_LEVEL_2 0x00000020 +#define HFI_VC1_LEVEL_3 0x00000040 +#define HFI_VC1_LEVEL_4 0x00000080 + +#define HFI_VPX_PROFILE_SIMPLE 0x00000001 +#define HFI_VPX_PROFILE_ADVANCED 0x00000002 +#define HFI_VPX_PROFILE_VERSION_0 0x00000004 +#define HFI_VPX_PROFILE_VERSION_1 0x00000008 +#define HFI_VPX_PROFILE_VERSION_2 0x00000010 +#define HFI_VPX_PROFILE_VERSION_3 0x00000020 + +#define HFI_DIVX_FORMAT_4 (HFI_COMMON_BASE + 0x1) +#define HFI_DIVX_FORMAT_5 (HFI_COMMON_BASE + 0x2) +#define HFI_DIVX_FORMAT_6 (HFI_COMMON_BASE + 0x3) + +#define HFI_DIVX_PROFILE_QMOBILE 0x00000001 +#define HFI_DIVX_PROFILE_MOBILE 0x00000002 +#define HFI_DIVX_PROFILE_MT 0x00000004 +#define HFI_DIVX_PROFILE_HT 0x00000008 +#define HFI_DIVX_PROFILE_HD 0x00000010 + +#define HFI_HEVC_PROFILE_MAIN 0x00000001 +#define HFI_HEVC_PROFILE_MAIN10 0x00000002 +#define HFI_HEVC_PROFILE_MAIN_STILL_PIC 0x00000004 + +#define HFI_HEVC_LEVEL_1 0x00000001 +#define HFI_HEVC_LEVEL_2 0x00000002 +#define HFI_HEVC_LEVEL_21 0x00000004 +#define HFI_HEVC_LEVEL_3 0x00000008 +#define HFI_HEVC_LEVEL_31 0x00000010 +#define HFI_HEVC_LEVEL_4 0x00000020 +#define HFI_HEVC_LEVEL_41 0x00000040 +#define HFI_HEVC_LEVEL_5 0x00000080 +#define HFI_HEVC_LEVEL_51 0x00000100 +#define HFI_HEVC_LEVEL_52 0x00000200 +#define HFI_HEVC_LEVEL_6 0x00000400 +#define HFI_HEVC_LEVEL_61 0x00000800 +#define HFI_HEVC_LEVEL_62 0x00001000 + +#define HFI_HEVC_TIER_MAIN 0x1 +#define HFI_HEVC_TIER_HIGH0 0x2 + +#define HFI_BUFFER_INPUT (HFI_COMMON_BASE + 0x1) +#define HFI_BUFFER_OUTPUT (HFI_COMMON_BASE + 0x2) +#define HFI_BUFFER_OUTPUT2 (HFI_COMMON_BASE + 0x3) +#define HFI_BUFFER_INTERNAL_PERSIST (HFI_COMMON_BASE + 0x4) +#define HFI_BUFFER_INTERNAL_PERSIST_1 (HFI_COMMON_BASE + 0x5) + +#define HFI_BITDEPTH_8 (HFI_COMMON_BASE + 0x0) +#define HFI_BITDEPTH_9 (HFI_COMMON_BASE + 0x1) +#define HFI_BITDEPTH_10 (HFI_COMMON_BASE + 0x2) + +#define HFI_VENC_PERFMODE_MAX_QUALITY 0x1 +#define HFI_VENC_PERFMODE_POWER_SAVE 0x2 + +struct hfi_buffer_info { + u32 buffer_addr; + u32 extra_data_addr; +}; + +#define HFI_PROPERTY_SYS_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000) +#define HFI_PROPERTY_SYS_DEBUG_CONFIG \ + (HFI_PROPERTY_SYS_COMMON_START + 0x001) +#define HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO \ + (HFI_PROPERTY_SYS_COMMON_START + 0x002) +#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ \ + (HFI_PROPERTY_SYS_COMMON_START + 0x003) +#define HFI_PROPERTY_SYS_IDLE_INDICATOR \ + (HFI_PROPERTY_SYS_COMMON_START + 0x004) +#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL \ + (HFI_PROPERTY_SYS_COMMON_START + 0x005) +#define HFI_PROPERTY_SYS_IMAGE_VERSION \ + (HFI_PROPERTY_SYS_COMMON_START + 0x006) +#define HFI_PROPERTY_SYS_CONFIG_COVERAGE \ + (HFI_PROPERTY_SYS_COMMON_START + 0x007) + +#define HFI_PROPERTY_PARAM_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000) +#define HFI_PROPERTY_PARAM_FRAME_SIZE \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x002) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x003) +#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x004) +#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x005) +#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x006) +#define HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x007) +#define HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x008) +#define HFI_PROPERTY_PARAM_CODEC_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x009) +#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00A) +#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00B) +#define HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00C) +#define HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00D) +#define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00E) +#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x00F) +#define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED \ + (HFI_PROPERTY_PARAM_COMMON_START + 0x010) + +#define HFI_PROPERTY_CONFIG_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000) +#define HFI_PROPERTY_CONFIG_FRAME_RATE \ + (HFI_PROPERTY_CONFIG_COMMON_START + 0x001) + +#define HFI_PROPERTY_PARAM_VDEC_COMMON_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x3000) +#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x002) +#define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x003) +#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x007) +#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x009) +#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE \ + (HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x00A) + + +#define HFI_PROPERTY_CONFIG_VDEC_COMMON_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x4000) + +#define HFI_PROPERTY_PARAM_VENC_COMMON_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x5000) +#define HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x001) +#define HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x002) +#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x003) +#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x004) +#define HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x005) +#define HFI_PROPERTY_PARAM_VENC_SESSION_QP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x006) +#define HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x007) +#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x008) +#define HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x009) +#define HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00A) +#define HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00B) +#define HFI_PROPERTY_PARAM_VENC_OPEN_GOP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00C) +#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00D) +#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00E) +#define HFI_PROPERTY_PARAM_VENC_VBV_HRD_BUF_SIZE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00F) +#define HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x010) +#define HFI_PROPERTY_PARAM_VENC_ADVANCED \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x012) +#define HFI_PROPERTY_PARAM_VENC_H264_SPS_ID \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014) +#define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x015) +#define HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x016) +#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017) +#define HFI_PROPERTY_PARAM_VENC_NUMREF \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018) +#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019) +#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B) +#define HFI_PROPERTY_PARAM_VENC_LTRMODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01C) +#define HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D) +#define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E) +#define HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F) +#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020) +#define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x021) +#define HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x022) +#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023) +#define HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x025) +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026) +#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x027) +#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x028) +#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x029) +#define HFI_PROPERTY_PARAM_VENC_CONSTRAINED_INTRA_PRED \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02B) +#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02C) +#define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02F) +#define HFI_PROPERTY_PARAM_VENC_BITRATE_TYPE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x031) +#define HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x033) +#define HFI_PROPERTY_PARAM_VENC_IFRAMESIZE \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x034) + +#define HFI_PROPERTY_CONFIG_VENC_COMMON_START \ + (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000) +#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x001) +#define HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x002) +#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x003) +#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x004) +#define HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x005) +#define HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x007) + +#define HFI_PROPERTY_PARAM_VPE_COMMON_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000) +#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x008) +#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x009) +#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A) +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B) +#define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C) +#define HFI_PROPERTY_CONFIG_VENC_PERF_MODE \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00E) +#define HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00F) + +#define HFI_PROPERTY_CONFIG_VPE_COMMON_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000) +#define HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE \ + (HFI_PROPERTY_CONFIG_COMMON_START + 0x010) +#define HFI_PROPERTY_CONFIG_VPE_DEINTERLACE \ + (HFI_PROPERTY_CONFIG_VPE_COMMON_START + 0x001) +#define HFI_PROPERTY_CONFIG_VPE_OPERATIONS \ + (HFI_PROPERTY_CONFIG_VPE_COMMON_START + 0x002) + +struct hfi_pic_struct { + u32 progressive_only; +}; + +struct hfi_bitrate { + u32 bit_rate; + u32 layer_id; +}; + +struct hfi_colour_space { + u32 colour_space; +}; + +#define HFI_CAPABILITY_FRAME_WIDTH (HFI_COMMON_BASE + 0x1) +#define HFI_CAPABILITY_FRAME_HEIGHT (HFI_COMMON_BASE + 0x2) +#define HFI_CAPABILITY_MBS_PER_FRAME (HFI_COMMON_BASE + 0x3) +#define HFI_CAPABILITY_MBS_PER_SECOND (HFI_COMMON_BASE + 0x4) +#define HFI_CAPABILITY_FRAMERATE (HFI_COMMON_BASE + 0x5) +#define HFI_CAPABILITY_SCALE_X (HFI_COMMON_BASE + 0x6) +#define HFI_CAPABILITY_SCALE_Y (HFI_COMMON_BASE + 0x7) +#define HFI_CAPABILITY_BITRATE (HFI_COMMON_BASE + 0x8) +#define HFI_CAPABILITY_BFRAME (HFI_COMMON_BASE + 0x9) +#define HFI_CAPABILITY_PEAKBITRATE (HFI_COMMON_BASE + 0xa) +#define HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x10) +#define HFI_CAPABILITY_ENC_LTR_COUNT (HFI_COMMON_BASE + 0x11) +#define HFI_CAPABILITY_CP_OUTPUT2_THRESH (HFI_COMMON_BASE + 0x12) +#define HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x13) +#define HFI_CAPABILITY_LCU_SIZE (HFI_COMMON_BASE + 0x14) +#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x15) +#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE (HFI_COMMON_BASE + 0x16) + +struct hfi_capability_supported { + u32 capability_type; + u32 min; + u32 max; + u32 step_size; +}; + +struct hfi_capability_supported_info { + u32 num_capabilities; + struct hfi_capability_supported rg_data[1]; +}; + +#define HFI_DEBUG_MSG_LOW 0x00000001 +#define HFI_DEBUG_MSG_MEDIUM 0x00000002 +#define HFI_DEBUG_MSG_HIGH 0x00000004 +#define HFI_DEBUG_MSG_ERROR 0x00000008 +#define HFI_DEBUG_MSG_FATAL 0x00000010 +#define HFI_DEBUG_MSG_PERF 0x00000020 + +#define HFI_DEBUG_MODE_QUEUE 0x00000001 +#define HFI_DEBUG_MODE_QDSS 0x00000002 + +struct hfi_debug_config { + u32 debug_config; + u32 debug_mode; +}; + +struct hfi_enable { + u32 enable; +}; + +#define HFI_H264_DB_MODE_DISABLE (HFI_COMMON_BASE + 0x1) +#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY \ + (HFI_COMMON_BASE + 0x2) +#define HFI_H264_DB_MODE_ALL_BOUNDARY (HFI_COMMON_BASE + 0x3) + +struct hfi_h264_db_control { + u32 mode; + u32 slice_alpha_offset; + u32 slice_beta_offset; +}; + +#define HFI_H264_ENTROPY_CAVLC (HFI_COMMON_BASE + 0x1) +#define HFI_H264_ENTROPY_CABAC (HFI_COMMON_BASE + 0x2) + +#define HFI_H264_CABAC_MODEL_0 (HFI_COMMON_BASE + 0x1) +#define HFI_H264_CABAC_MODEL_1 (HFI_COMMON_BASE + 0x2) +#define HFI_H264_CABAC_MODEL_2 (HFI_COMMON_BASE + 0x3) + +struct hfi_h264_entropy_control { + u32 entropy_mode; + u32 cabac_model; +}; + +struct hfi_frame_rate { + u32 buffer_type; + u32 frame_rate; +}; + +#define HFI_INTRA_REFRESH_NONE (HFI_COMMON_BASE + 0x1) +#define HFI_INTRA_REFRESH_CYCLIC (HFI_COMMON_BASE + 0x2) +#define HFI_INTRA_REFRESH_ADAPTIVE (HFI_COMMON_BASE + 0x3) +#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE (HFI_COMMON_BASE + 0x4) +#define HFI_INTRA_REFRESH_RANDOM (HFI_COMMON_BASE + 0x5) + +struct hfi_intra_refresh { + u32 mode; + u32 air_mbs; + u32 air_ref; + u32 cir_mbs; +}; + +struct hfi_3x_intra_refresh { + u32 mode; + u32 mbs; +}; + +struct hfi_idr_period { + u32 idr_period; +}; + +struct hfi_operations_type { + u32 rotation; + u32 flip; +}; + +struct hfi_max_num_b_frames { + u32 max_num_b_frames; +}; + +struct hfi_vc1e_perf_cfg_type { + u32 search_range_x_subsampled[3]; + u32 search_range_y_subsampled[3]; +}; + +struct hfi_conceal_color { + u32 conceal_color; +}; + +struct hfi_intra_period { + u32 pframes; + u32 bframes; +}; + +struct hfi_mpeg4_header_extension { + u32 header_extension; +}; + +struct hfi_mpeg4_time_resolution { + u32 time_increment_resolution; +}; + +struct hfi_multi_stream { + u32 buffer_type; + u32 enable; + u32 width; + u32 height; +}; + +struct hfi_3x_multi_stream { + u32 buffer_type; + u32 enable; +}; + +struct hfi_multi_view_format { + u32 views; + u32 rg_view_order[1]; +}; + +#define HFI_MULTI_SLICE_OFF (HFI_COMMON_BASE + 0x1) +#define HFI_MULTI_SLICE_BY_MB_COUNT (HFI_COMMON_BASE + 0x2) +#define HFI_MULTI_SLICE_BY_BYTE_COUNT (HFI_COMMON_BASE + 0x3) +#define HFI_MULTI_SLICE_GOB (HFI_COMMON_BASE + 0x4) + +struct hfi_multi_slice_control { + u32 multi_slice; + u32 slice_size; +}; + +#define HFI_NAL_FORMAT_STARTCODES 0x00000001 +#define HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER 0x00000002 +#define HFI_NAL_FORMAT_ONE_BYTE_LENGTH 0x00000004 +#define HFI_NAL_FORMAT_TWO_BYTE_LENGTH 0x00000008 +#define HFI_NAL_FORMAT_FOUR_BYTE_LENGTH 0x00000010 + +struct hfi_nal_stream_format_supported { + u32 nal_stream_format_supported; +}; + +struct hfi_nal_stream_format_select { + u32 nal_stream_format_select; +}; +#define HFI_PICTURE_TYPE_I 0x01 +#define HFI_PICTURE_TYPE_P 0x02 +#define HFI_PICTURE_TYPE_B 0x04 +#define HFI_PICTURE_TYPE_IDR 0x08 +#define HFI_PICTURE_TYPE_CRA 0x10 + +struct hfi_profile_level { + u32 profile; + u32 level; +}; + +struct hfi_profile_level_supported { + u32 profile_count; + struct hfi_profile_level rg_profile_level[1]; +}; + +struct hfi_quality_vs_speed { + u32 quality_vs_speed; +}; + +struct hfi_quantization { + u32 qp_i; + u32 qp_p; + u32 qp_b; + u32 layer_id; +}; + +struct hfi_initial_quantization { + u32 qp_i; + u32 qp_p; + u32 qp_b; + u32 init_qp_enable; +}; + +struct hfi_quantization_range { + u32 min_qp; + u32 max_qp; + u32 layer_id; +}; + +#define HFI_LTR_MODE_DISABLE 0x0 +#define HFI_LTR_MODE_MANUAL 0x1 +#define HFI_LTR_MODE_PERIODIC 0x2 + +struct hfi_ltr_mode { + u32 ltr_mode; + u32 ltr_count; + u32 trust_mode; +}; + +struct hfi_ltr_use { + u32 ref_ltr; + u32 use_constrnt; + u32 frames; +}; + +struct hfi_ltr_mark { + u32 mark_frame; +}; + +struct hfi_frame_size { + u32 buffer_type; + u32 width; + u32 height; +}; + +struct hfi_video_signal_metadata { + u32 enable; + u32 video_format; + u32 video_full_range; + u32 color_description; + u32 color_primaries; + u32 transfer_characteristics; + u32 matrix_coeffs; +}; + +struct hfi_h264_vui_timing_info { + u32 enable; + u32 fixed_frame_rate; + u32 time_scale; +}; + +struct hfi_bit_depth { + u32 buffer_type; + u32 bit_depth; +}; + +struct hfi_picture_type { + u32 picture_type; +}; + +/* Base Offset for UBWC color formats */ +#define HFI_COLOR_FORMAT_UBWC_BASE (0x8000) +/* Base Offset for 10-bit color formats */ +#define HFI_COLOR_FORMAT_10_BIT_BASE (0x4000) + +#define HFI_COLOR_FORMAT_MONOCHROME (HFI_COMMON_BASE + 0x1) +#define HFI_COLOR_FORMAT_NV12 (HFI_COMMON_BASE + 0x2) +#define HFI_COLOR_FORMAT_NV21 (HFI_COMMON_BASE + 0x3) +#define HFI_COLOR_FORMAT_NV12_4x4TILE (HFI_COMMON_BASE + 0x4) +#define HFI_COLOR_FORMAT_NV21_4x4TILE (HFI_COMMON_BASE + 0x5) +#define HFI_COLOR_FORMAT_YUYV (HFI_COMMON_BASE + 0x6) +#define HFI_COLOR_FORMAT_YVYU (HFI_COMMON_BASE + 0x7) +#define HFI_COLOR_FORMAT_UYVY (HFI_COMMON_BASE + 0x8) +#define HFI_COLOR_FORMAT_VYUY (HFI_COMMON_BASE + 0x9) +#define HFI_COLOR_FORMAT_RGB565 (HFI_COMMON_BASE + 0xA) +#define HFI_COLOR_FORMAT_BGR565 (HFI_COMMON_BASE + 0xB) +#define HFI_COLOR_FORMAT_RGB888 (HFI_COMMON_BASE + 0xC) +#define HFI_COLOR_FORMAT_BGR888 (HFI_COMMON_BASE + 0xD) +#define HFI_COLOR_FORMAT_YUV444 (HFI_COMMON_BASE + 0xE) +#define HFI_COLOR_FORMAT_RGBA8888 (HFI_COMMON_BASE + 0x10) + +#define HFI_COLOR_FORMAT_YUV420_TP10 \ + (HFI_COLOR_FORMAT_10_BIT_BASE + HFI_COLOR_FORMAT_NV12) + +#define HFI_COLOR_FORMAT_NV12_UBWC \ + (HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_NV12) + +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC \ + (HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_YUV420_TP10) + +#define HFI_COLOR_FORMAT_RGBA8888_UBWC \ + (HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_RGBA8888) + +#define HFI_MAX_MATRIX_COEFFS 9 +#define HFI_MAX_BIAS_COEFFS 3 +#define HFI_MAX_LIMIT_COEFFS 6 + +#define HFI_STATISTICS_MODE_DEFAULT 0x10 +#define HFI_STATISTICS_MODE_1 0x11 +#define HFI_STATISTICS_MODE_2 0x12 +#define HFI_STATISTICS_MODE_3 0x13 + +struct hfi_uncompressed_format_select { + u32 buffer_type; + u32 format; +}; + +struct hfi_uncompressed_format_supported { + u32 buffer_type; + u32 format_entries; + u32 rg_format_info[1]; +}; + +struct hfi_uncompressed_plane_actual { + u32 actual_stride; + u32 actual_plane_buffer_height; +}; + +struct hfi_uncompressed_plane_actual_info { + u32 buffer_type; + u32 num_planes; + struct hfi_uncompressed_plane_actual rg_plane_format[1]; +}; + +struct hfi_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hfi_uncompressed_plane_info { + u32 format; + u32 num_planes; + struct hfi_uncompressed_plane_constraints rg_plane_format[1]; +}; + +struct hfi_codec_supported { + u32 decoder_codec_supported; + u32 encoder_codec_supported; +}; + +struct hfi_properties_supported { + u32 num_properties; + u32 rg_properties[1]; +}; + +struct hfi_max_sessions_supported { + u32 max_sessions; +}; + +struct hfi_vpe_color_space_conversion { + u32 csc_matrix[HFI_MAX_MATRIX_COEFFS]; + u32 csc_bias[HFI_MAX_BIAS_COEFFS]; + u32 csc_limit[HFI_MAX_LIMIT_COEFFS]; +}; + +struct hfi_scs_threshold { + u32 threshold_value; +}; + +#define HFI_ROTATE_NONE (HFI_COMMON_BASE + 0x1) +#define HFI_ROTATE_90 (HFI_COMMON_BASE + 0x2) +#define HFI_ROTATE_180 (HFI_COMMON_BASE + 0x3) +#define HFI_ROTATE_270 (HFI_COMMON_BASE + 0x4) + +#define HFI_FLIP_NONE (HFI_COMMON_BASE + 0x1) +#define HFI_FLIP_HORIZONTAL (HFI_COMMON_BASE + 0x2) +#define HFI_FLIP_VERTICAL (HFI_COMMON_BASE + 0x3) + +struct hfi_operations { + u32 rotate; + u32 flip; +}; + +#define HFI_RESOURCE_OCMEM 0x00000001 + +struct hfi_resource_ocmem { + u32 size; + u32 mem; +}; + +struct hfi_resource_ocmem_requirement { + u32 session_domain; + u32 width; + u32 height; + u32 size; +}; + +struct hfi_resource_ocmem_requirement_info { + u32 num_entries; + struct hfi_resource_ocmem_requirement rg_requirements[1]; +}; + +struct hfi_property_sys_image_version_info_type { + u32 string_size; + u8 str_image_version[1]; +}; + +struct hfi_venc_config_advanced { + u8 pipe2d; + u8 hw_mode; + u8 low_delay_enforce; + u8 worker_vppsg_delay; + u32 close_gop; + u32 h264_constrain_intra_pred; + u32 h264_transform_8x8_flag; + u32 mpeg4_qpel_enable; + u32 multi_refp_en; + u32 qmatrix_en; + u8 vpp_info_packet_mode; + u8 ref_tile_mode; + u8 bitstream_flush_mode; + u32 vppsg_vspap_fb_sync_delay; + u32 rc_initial_delay; + u32 peak_bitrate_constraint; + u32 ds_display_frame_width; + u32 ds_display_frame_height; + u32 perf_tune_param_ptr; + u32 input_x_offset; + u32 input_y_offset; + u32 input_roi_width; + u32 input_roi_height; + u32 vsp_fifo_dma_sel; + u32 h264_num_ref_frames; +}; + +struct hfi_vbv_hrd_bufsize { + u32 buffer_size; +}; + +struct hfi_codec_mask_supported { + u32 codecs; + u32 video_domains; +}; + +struct hfi_seq_header_info { + u32 max_hader_len; +}; + +struct hfi_aspect_ratio { + u32 aspect_width; + u32 aspect_height; +}; + +#define HFI_IFRAME_SIZE_DEFAULT (HFI_COMMON_BASE + 0x1) +#define HFI_IFRAME_SIZE_MEDIUM (HFI_COMMON_BASE + 0x2) +#define HFI_IFRAME_SIZE_HIGH (HFI_COMMON_BASE + 0x3) +#define HFI_IFRAME_SIZE_UNLIMITED (HFI_COMMON_BASE + 0x4) +struct hfi_iframe_size { + u32 type; +}; + +#define HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM (0) +#define HFI_MVC_BUFFER_LAYOUT_SIDEBYSIDE (1) +#define HFI_MVC_BUFFER_LAYOUT_SEQ (2) +struct hfi_mvc_buffer_layout_descp_type { + u32 layout_type; + u32 bright_view_first; + u32 ngap; +}; + + +#define HFI_CMD_SYS_COMMON_START \ +(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + HFI_CMD_START_OFFSET \ + + 0x0000) +#define HFI_CMD_SYS_INIT (HFI_CMD_SYS_COMMON_START + 0x001) +#define HFI_CMD_SYS_PC_PREP (HFI_CMD_SYS_COMMON_START + 0x002) +#define HFI_CMD_SYS_SET_RESOURCE (HFI_CMD_SYS_COMMON_START + 0x003) +#define HFI_CMD_SYS_RELEASE_RESOURCE (HFI_CMD_SYS_COMMON_START + 0x004) +#define HFI_CMD_SYS_SET_PROPERTY (HFI_CMD_SYS_COMMON_START + 0x005) +#define HFI_CMD_SYS_GET_PROPERTY (HFI_CMD_SYS_COMMON_START + 0x006) +#define HFI_CMD_SYS_SESSION_INIT (HFI_CMD_SYS_COMMON_START + 0x007) +#define HFI_CMD_SYS_SESSION_END (HFI_CMD_SYS_COMMON_START + 0x008) +#define HFI_CMD_SYS_SET_BUFFERS (HFI_CMD_SYS_COMMON_START + 0x009) +#define HFI_CMD_SYS_TEST_START (HFI_CMD_SYS_COMMON_START + 0x100) + +#define HFI_CMD_SESSION_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \ + HFI_CMD_START_OFFSET + 0x1000) +#define HFI_CMD_SESSION_SET_PROPERTY \ + (HFI_CMD_SESSION_COMMON_START + 0x001) +#define HFI_CMD_SESSION_SET_BUFFERS \ + (HFI_CMD_SESSION_COMMON_START + 0x002) +#define HFI_CMD_SESSION_GET_SEQUENCE_HEADER \ + (HFI_CMD_SESSION_COMMON_START + 0x003) + +#define HFI_MSG_SYS_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \ + HFI_MSG_START_OFFSET + 0x0000) +#define HFI_MSG_SYS_INIT_DONE (HFI_MSG_SYS_COMMON_START + 0x1) +#define HFI_MSG_SYS_PC_PREP_DONE (HFI_MSG_SYS_COMMON_START + 0x2) +#define HFI_MSG_SYS_RELEASE_RESOURCE (HFI_MSG_SYS_COMMON_START + 0x3) +#define HFI_MSG_SYS_DEBUG (HFI_MSG_SYS_COMMON_START + 0x4) +#define HFI_MSG_SYS_SESSION_INIT_DONE (HFI_MSG_SYS_COMMON_START + 0x6) +#define HFI_MSG_SYS_SESSION_END_DONE (HFI_MSG_SYS_COMMON_START + 0x7) +#define HFI_MSG_SYS_IDLE (HFI_MSG_SYS_COMMON_START + 0x8) +#define HFI_MSG_SYS_COV (HFI_MSG_SYS_COMMON_START + 0x9) +#define HFI_MSG_SYS_PROPERTY_INFO (HFI_MSG_SYS_COMMON_START + 0xA) +#define HFI_MSG_SESSION_SYNC_DONE (HFI_MSG_SESSION_OX_START + 0xD) + +#define HFI_MSG_SESSION_COMMON_START \ + (HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + \ + HFI_MSG_START_OFFSET + 0x1000) +#define HFI_MSG_EVENT_NOTIFY (HFI_MSG_SESSION_COMMON_START + 0x1) +#define HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE \ + (HFI_MSG_SESSION_COMMON_START + 0x2) + +#define HFI_CMD_SYS_TEST_SSR (HFI_CMD_SYS_TEST_START + 0x1) +#define HFI_TEST_SSR_SW_ERR_FATAL 0x1 +#define HFI_TEST_SSR_SW_DIV_BY_ZERO 0x2 +#define HFI_TEST_SSR_HW_WDOG_IRQ 0x3 + +struct vidc_hal_cmd_pkt_hdr { + u32 size; + u32 packet_type; +}; + +struct vidc_hal_msg_pkt_hdr { + u32 size; + u32 packet; +}; + +struct vidc_hal_session_cmd_pkt { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_packet_header { + u32 size; + u32 packet_type; +}; + +struct hfi_cmd_sys_init_packet { + u32 size; + u32 packet_type; + u32 arch_type; +}; + +struct hfi_cmd_sys_pc_prep_packet { + u32 size; + u32 packet_type; +}; + +struct hfi_cmd_sys_set_resource_packet { + u32 size; + u32 packet_type; + u32 resource_handle; + u32 resource_type; + u32 rg_resource_data[1]; +}; + +struct hfi_cmd_sys_release_resource_packet { + u32 size; + u32 packet_type; + u32 resource_type; + u32 resource_handle; +}; + +struct hfi_cmd_sys_set_property_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_sys_get_property_packet { + u32 size; + u32 packet_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_cmd_sys_session_init_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 session_domain; + u32 session_codec; +}; + +struct hfi_cmd_sys_session_end_packet { + u32 size; + u32 packet_type; + u32 session_id; +}; + +struct hfi_cmd_sys_set_buffers_packet { + u32 size; + u32 packet_type; + u32 buffer_type; + u32 buffer_size; + u32 num_buffers; + u32 rg_buffer_addr[1]; +}; + +struct hfi_cmd_session_set_property_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 num_properties; + u32 rg_property_data[0]; +}; + +struct hfi_cmd_session_set_buffers_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 buffer_type; + u32 buffer_size; + u32 extra_data_size; + u32 min_buffer_size; + u32 num_buffers; + u32 rg_buffer_info[1]; +}; + +struct hfi_cmd_session_get_sequence_header_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 buffer_len; + u32 packet_buffer; +}; + +struct hfi_cmd_session_sync_process_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 sync_id; + u32 rg_data[1]; +}; + +struct hfi_msg_event_notify_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 event_id; + u32 event_data1; + u32 event_data2; + u32 rg_ext_event_data[1]; +}; + +struct hfi_msg_release_buffer_ref_event_packet { + u32 packet_buffer; + u32 extra_data_buffer; + u32 output_tag; +}; + +struct hfi_msg_sys_init_done_packet { + u32 size; + u32 packet_type; + u32 error_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_sys_pc_prep_done_packet { + u32 size; + u32 packet_type; + u32 error_type; +}; + +struct hfi_msg_sys_release_resource_done_packet { + u32 size; + u32 packet_type; + u32 resource_handle; + u32 error_type; +}; + +struct hfi_msg_sys_session_init_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 num_properties; + u32 rg_property_data[1]; +}; + +struct hfi_msg_sys_session_end_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; +}; + +struct hfi_msg_session_get_sequence_header_done_packet { + u32 size; + u32 packet_type; + u32 session_id; + u32 error_type; + u32 header_len; + u32 sequence_header; +}; + +struct hfi_msg_sys_debug_packet { + u32 size; + u32 packet_type; + u32 msg_type; + u32 msg_size; + u32 time_stamp_hi; + u32 time_stamp_lo; + u8 rg_msg_data[1]; +}; + +struct hfi_msg_sys_coverage_packet { + u32 size; + u32 packet_type; + u32 msg_size; + u32 time_stamp_hi; + u32 time_stamp_lo; + u8 rg_msg_data[1]; +}; + +enum HFI_VENUS_QTBL_STATUS { + HFI_VENUS_QTBL_DISABLED = 0x00, + HFI_VENUS_QTBL_ENABLED = 0x01, + HFI_VENUS_QTBL_INITIALIZING = 0x02, + HFI_VENUS_QTBL_DEINITIALIZING = 0x03 +}; + +enum HFI_VENUS_CTRL_INIT_STATUS { + HFI_VENUS_CTRL_NOT_INIT = 0x0, + HFI_VENUS_CTRL_READY = 0x1, + HFI_VENUS_CTRL_ERROR_FATAL = 0x2 +}; + +struct hfi_sfr_struct { + u32 bufSize; + u8 rg_data[1]; +}; + +struct hfi_cmd_sys_test_ssr_packet { + u32 size; + u32 packet_type; + u32 trigger_type; +}; +#endif diff --git a/drivers/media/platform/msm/vidc_3x/vidc_hfi_io.h b/drivers/media/platform/msm/vidc_3x/vidc_hfi_io.h new file mode 100644 index 000000000000..104a78ae9777 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vidc_hfi_io.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VIDC_HFI_IO_H__ +#define __VIDC_HFI_IO_H__ + +#include + +#define VENUS_VCODEC_SS_CLOCK_HALT 0x0000000C +#define VENUS_VPP_CORE_SW_RESET 0x00042004 +#define VENUS_VPP_CTRL_CTRL_RESET 0x00041008 + +#define VIDC_VBIF_BASE_OFFS 0x00080000 +#define VIDC_VBIF_VERSION (VIDC_VBIF_BASE_OFFS + 0x00) +#define VIDC_VENUS_VBIF_DDR_OUT_MAX_BURST \ + (VIDC_VBIF_BASE_OFFS + 0xD8) +#define VIDC_VENUS_VBIF_OCMEM_OUT_MAX_BURST \ + (VIDC_VBIF_BASE_OFFS + 0xDC) +#define VIDC_VENUS_VBIF_ROUND_ROBIN_QOS_ARB \ + (VIDC_VBIF_BASE_OFFS + 0x124) + +#define VIDC_CPU_BASE_OFFS 0x000C0000 +#define VIDC_CPU_CS_BASE_OFFS (VIDC_CPU_BASE_OFFS + 0x00012000) +#define VIDC_CPU_IC_BASE_OFFS (VIDC_CPU_BASE_OFFS + 0x0001F000) + +#define VIDC_CPU_CS_REMAP_OFFS (VIDC_CPU_CS_BASE_OFFS + 0x00) +#define VIDC_CPU_CS_TIMER_CONTROL (VIDC_CPU_CS_BASE_OFFS + 0x04) +#define VIDC_CPU_CS_A2HSOFTINTEN (VIDC_CPU_CS_BASE_OFFS + 0x10) +#define VIDC_CPU_CS_A2HSOFTINTENCLR (VIDC_CPU_CS_BASE_OFFS + 0x14) +#define VIDC_CPU_CS_A2HSOFTINT (VIDC_CPU_CS_BASE_OFFS + 0x18) +#define VIDC_CPU_CS_A2HSOFTINTCLR (VIDC_CPU_CS_BASE_OFFS + 0x1C) +#define VIDC_CPU_CS_SCIACMD (VIDC_CPU_CS_BASE_OFFS + 0x48) + +/* HFI_CTRL_STATUS */ +#define VIDC_CPU_CS_SCIACMDARG0 (VIDC_CPU_CS_BASE_OFFS + 0x4C) +#define VIDC_CPU_CS_SCIACMDARG0_BMSK 0xff +#define VIDC_CPU_CS_SCIACMDARG0_SHFT 0x0 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK 0xfe +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_SHFT 0x1 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_BMSK 0x1 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_SHFT 0x0 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY 0x100 +#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK 0x40000000 + +/* HFI_QTBL_INFO */ +#define VIDC_CPU_CS_SCIACMDARG1 (VIDC_CPU_CS_BASE_OFFS + 0x50) + +/* HFI_QTBL_ADDR */ +#define VIDC_CPU_CS_SCIACMDARG2 (VIDC_CPU_CS_BASE_OFFS + 0x54) + +/* HFI_VERSION_INFO */ +#define VIDC_CPU_CS_SCIACMDARG3 (VIDC_CPU_CS_BASE_OFFS + 0x58) +#define VIDC_CPU_IC_IRQSTATUS (VIDC_CPU_IC_BASE_OFFS + 0x00) +#define VIDC_CPU_IC_FIQSTATUS (VIDC_CPU_IC_BASE_OFFS + 0x04) +#define VIDC_CPU_IC_RAWINTR (VIDC_CPU_IC_BASE_OFFS + 0x08) +#define VIDC_CPU_IC_INTSELECT (VIDC_CPU_IC_BASE_OFFS + 0x0C) +#define VIDC_CPU_IC_INTENABLE (VIDC_CPU_IC_BASE_OFFS + 0x10) +#define VIDC_CPU_IC_INTENACLEAR (VIDC_CPU_IC_BASE_OFFS + 0x14) +#define VIDC_CPU_IC_SOFTINT (VIDC_CPU_IC_BASE_OFFS + 0x18) +#define VIDC_CPU_IC_SOFTINT_H2A_BMSK 0x8000 +#define VIDC_CPU_IC_SOFTINT_H2A_SHFT 0xF +#define VIDC_CPU_IC_SOFTINTCLEAR (VIDC_CPU_IC_BASE_OFFS + 0x1C) + +/*--------------------------------------------------------------------------- + * MODULE: vidc_wrapper + *-------------------------------------------------------------------------- + */ +#define VIDC_WRAPPER_BASE_OFFS 0x000E0000 + +#define VIDC_WRAPPER_HW_VERSION (VIDC_WRAPPER_BASE_OFFS + 0x00) +#define VIDC_WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000 +#define VIDC_WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28 +#define VIDC_WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xFFF0000 +#define VIDC_WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16 +#define VIDC_WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xFFFF +#define VIDC_WRAPPER_CLOCK_CONFIG (VIDC_WRAPPER_BASE_OFFS + 0x04) + +#define VIDC_WRAPPER_INTR_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x0C) +#define VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK 0x10 +#define VIDC_WRAPPER_INTR_STATUS_A2HWD_SHFT 0x4 +#define VIDC_WRAPPER_INTR_STATUS_A2H_BMSK 0x4 +#define VIDC_WRAPPER_INTR_STATUS_A2H_SHFT 0x2 + +#define VIDC_WRAPPER_INTR_MASK (VIDC_WRAPPER_BASE_OFFS + 0x10) +#define VIDC_WRAPPER_INTR_MASK_A2HWD_BMSK 0x10 +#define VIDC_WRAPPER_INTR_MASK_A2HWD_SHFT 0x4 +#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK 0x8 +#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_SHFT 0x3 +#define VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK 0x4 +#define VIDC_WRAPPER_INTR_MASK_A2HCPU_SHFT 0x2 + +#define VIDC_WRAPPER_INTR_CLEAR (VIDC_WRAPPER_BASE_OFFS + 0x14) +#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK 0x10 +#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_SHFT 0x4 +#define VIDC_WRAPPER_INTR_CLEAR_A2H_BMSK 0x4 +#define VIDC_WRAPPER_INTR_CLEAR_A2H_SHFT 0x2 + +#define VIDC_WRAPPER_VBIF_XIN_SW_RESET (VIDC_WRAPPER_BASE_OFFS + 0x18) +#define VIDC_WRAPPER_VBIF_XIN_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x1C) +#define VIDC_WRAPPER_CPU_CLOCK_CONFIG (VIDC_WRAPPER_BASE_OFFS + 0x2000) +#define VIDC_WRAPPER_VBIF_XIN_CPU_SW_RESET \ + (VIDC_WRAPPER_BASE_OFFS + 0x2004) +#define VIDC_WRAPPER_AXI_HALT (VIDC_WRAPPER_BASE_OFFS + 0x2008) +#define VIDC_WRAPPER_AXI_HALT_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x200C) +#define VIDC_WRAPPER_CPU_CGC_DIS (VIDC_WRAPPER_BASE_OFFS + 0x2010) +#define VIDC_WRAPPER_CPU_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x2014) +#define VIDC_VENUS_VBIF_CLK_ON (VIDC_VBIF_BASE_OFFS + 0x4) +#define VIDC_VBIF_IN_RD_LIM_CONF0 (VIDC_VBIF_BASE_OFFS + 0xB0) +#define VIDC_VBIF_IN_RD_LIM_CONF1 (VIDC_VBIF_BASE_OFFS + 0xB4) +#define VIDC_VBIF_IN_RD_LIM_CONF2 (VIDC_VBIF_BASE_OFFS + 0xB8) +#define VIDC_VBIF_IN_RD_LIM_CONF3 (VIDC_VBIF_BASE_OFFS + 0xBC) +#define VIDC_VBIF_IN_WR_LIM_CONF0 (VIDC_VBIF_BASE_OFFS + 0xC0) +#define VIDC_VBIF_IN_WR_LIM_CONF1 (VIDC_VBIF_BASE_OFFS + 0xC4) +#define VIDC_VBIF_IN_WR_LIM_CONF2 (VIDC_VBIF_BASE_OFFS + 0xC8) +#define VIDC_VBIF_IN_WR_LIM_CONF3 (VIDC_VBIF_BASE_OFFS + 0xCC) +#define VIDC_VBIF_OUT_RD_LIM_CONF0 (VIDC_VBIF_BASE_OFFS + 0xD0) +#define VIDC_VBIF_OUT_WR_LIM_CONF0 (VIDC_VBIF_BASE_OFFS + 0xD4) +#define VIDC_VBIF_DDR_OUT_MAX_BURST (VIDC_VBIF_BASE_OFFS + 0xD8) +#define VIDC_VBIF_OCMEM_OUT_MAX_BURST (VIDC_VBIF_BASE_OFFS + 0xDC) +#define VIDC_VBIF_DDR_ARB_CONF0 (VIDC_VBIF_BASE_OFFS + 0xF4) +#define VIDC_VBIF_DDR_ARB_CONF1 (VIDC_VBIF_BASE_OFFS + 0xF8) +#define VIDC_VBIF_ROUND_ROBIN_QOS_ARB (VIDC_VBIF_BASE_OFFS + 0x124) +#define VIDC_VBIF_OUT_AXI_AOOO_EN (VIDC_VBIF_BASE_OFFS + 0x178) +#define VIDC_VBIF_OUT_AXI_AOOO (VIDC_VBIF_BASE_OFFS + 0x17C) +#define VIDC_VBIF_ARB_CTL (VIDC_VBIF_BASE_OFFS + 0xF0) +#define VIDC_VBIF_OUT_AXI_AMEMTYPE_CONF0 (VIDC_VBIF_BASE_OFFS + 0x160) +#define VIDC_VBIF_OUT_AXI_AMEMTYPE_CONF1 (VIDC_VBIF_BASE_OFFS + 0x164) +#define VIDC_VBIF_ADDR_TRANS_EN (VIDC_VBIF_BASE_OFFS + 0xC00) +#define VIDC_VBIF_AT_OLD_BASE (VIDC_VBIF_BASE_OFFS + 0xC04) +#define VIDC_VBIF_AT_OLD_HIGH (VIDC_VBIF_BASE_OFFS + 0xC08) +#define VIDC_VBIF_AT_NEW_BASE (VIDC_VBIF_BASE_OFFS + 0xC10) +#define VIDC_VBIF_AT_NEW_HIGH (VIDC_VBIF_BASE_OFFS + 0xC18) +#define VENUS_VBIF_XIN_HALT_CTRL1 (VIDC_VBIF_BASE_OFFS + 0x204) +#define VENUS_VBIF_AXI_HALT_CTRL0 (VIDC_VBIF_BASE_OFFS + 0x208) +#define VENUS_VBIF_AXI_HALT_CTRL1 (VIDC_VBIF_BASE_OFFS + 0x20C) + +#define VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0) +#define VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0) +#define VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US 500000 + +#define VIDC_VENUS0_WRAPPER_VBIF_REQ_PRIORITY \ + (VIDC_WRAPPER_BASE_OFFS + 0x20) +#define VIDC_VENUS0_WRAPPER_VBIF_PRIORITY_LEVEL \ + (VIDC_WRAPPER_BASE_OFFS + 0x24) +#define VIDC_VENUS_WRAPPER_MMCC_VENUS0_POWER_STATUS \ + (VIDC_WRAPPER_BASE_OFFS + 0x44) + +#define VIDC_CTRL_INIT 0x000D2048 +#define VIDC_CTRL_INIT_RESERVED_BITS31_1__M 0xFFFFFFFE +#define VIDC_CTRL_INIT_RESERVED_BITS31_1__S 1 +#define VIDC_CTRL_INIT_CTRL__M 0x00000001 +#define VIDC_CTRL_INIT_CTRL__S 0 + +#define VIDC_CTRL_STATUS 0x000D204C +#define VIDC_CTRL_STATUS_RESERVED_BITS31_8__M 0xFFFFFF00 +#define VIDC_CTRL_STATUS_RESERVED_BITS31_8__S 8 +#define VIDC_CTRL_ERROR_STATUS__M 0x000000FE +#define VIDC_CTRL_ERROR_STATUS__S 1 +#define VIDC_CTRL_INIT_STATUS__M 0x00000001 +#define VIDC_CTRL_INIT_STATUS__S 0 + +#define VIDC_QTBL_INFO 0x000D2050 +#define VIDC_QTBL_HOSTID__M 0xFF000000 +#define VIDC_QTBL_HOSTID__S 24 +#define VIDC_QTBL_INFO_RESERVED_BITS23_8__M 0x00FFFF00 +#define VIDC_QTBL_INFO_RESERVED_BITS23_8__S 8 +#define VIDC_QTBL_STATUS__M 0x000000FF +#define VIDC_QTBL_STATUS__S 0 + +#define VIDC_QTBL_ADDR 0x000D2054 + +#define VIDC_VERSION_INFO 0x000D2058 +#define VIDC_VERSION_INFO_MAJOR__M 0xF0000000 +#define VIDC_VERSION_INFO_MAJOR__S 28 +#define VIDC_VERSION_INFO_MINOR__M 0x0FFFFFE0 +#define VIDC_VERSION_INFO_MINOR__S 5 +#define VIDC_VERSION_INFO_BRANCH__M 0x0000001F +#define VIDC_VERSION_INFO_BRANCH__S 0 + +#define VIDC_SFR_ADDR 0x000D205C +#define VIDC_MMAP_ADDR 0x000D2060 +#define VIDC_UC_REGION_ADDR 0x000D2064 +#define VIDC_UC_REGION_SIZE 0x000D2068 + +#endif diff --git a/drivers/media/platform/msm/vidc_3x/vmem/Kconfig b/drivers/media/platform/msm/vidc_3x/vmem/Kconfig new file mode 100644 index 000000000000..6f2651e1a7df --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vmem/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig MSM_VIDC_VMEM + bool "Qualcomm Technologies Inc MSM VMEM driver" + depends on ARCH_MSM && MSM_VIDC_V4L2 diff --git a/drivers/media/platform/msm/vidc_3x/vmem/Makefile b/drivers/media/platform/msm/vidc_3x/vmem/Makefile new file mode 100644 index 000000000000..d6e2244fa777 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vmem/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MSM_VIDC_VMEM) := vmem.o \ + vmem_debugfs.o diff --git a/drivers/media/platform/msm/vidc_3x/vmem/vmem.c b/drivers/media/platform/msm/vidc_3x/vmem/vmem.c new file mode 100644 index 000000000000..333a80f4e176 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vmem/vmem.c @@ -0,0 +1,699 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2016, 2018-2019 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vmem.h" +#include "vmem_debugfs.h" + +/* Registers */ +#define OCIMEM_BASE(v) ((uint8_t *)(v)->reg.base) +#define OCIMEM_HW_VERSION(v) (OCIMEM_BASE(v) + 0x00) +#define OCIMEM_HW_PROFILE(v) (OCIMEM_BASE(v) + 0x04) +#define OCIMEM_GEN_CTL(v) (OCIMEM_BASE(v) + 0x08) +#define OCIMEM_GEN_STAT(v) (OCIMEM_BASE(v) + 0x0C) +#define OCIMEM_INTC_CLR(v) (OCIMEM_BASE(v) + 0x10) +#define OCIMEM_INTC_MASK(v) (OCIMEM_BASE(v) + 0x14) +#define OCIMEM_INTC_STAT(v) (OCIMEM_BASE(v) + 0x18) +#define OCIMEM_OSW_STATUS(v) (OCIMEM_BASE(v) + 0x1C) +#define OCIMEM_PSCGC_TIMERS(v) (OCIMEM_BASE(v) + 0x34) +#define OCIMEM_PSCGC_STAT(v) (OCIMEM_BASE(v) + 0x38) +#define OCIMEM_PSCGC_M0_M7_CTL(v) (OCIMEM_BASE(v) + 0x3C) +#define OCIMEM_ERR_ADDRESS(v) (OCIMEM_BASE(v) + 0x60) +#define OCIMEM_AXI_ERR_SYNDROME(v) (OCIMEM_BASE(v) + 0x64) +#define OCIMEM_DEBUG_CTL(v) (OCIMEM_BASE(v) + 0x68) + +/* + * Helper macro to help out with masks and shifts for values packed into + * registers. + */ +#define DECLARE_TYPE(__type, __end, __start) \ + static const unsigned int __type##_BITS = (__end) - (__start) + 1; \ + static const unsigned int __type##_SHIFT = (__start); \ + static const unsigned int __type##_MASK = GENMASK((__end), (__start)); \ + static inline unsigned int __type(uint32_t val) \ + { \ + return (val & __type##_MASK) >> __type##_SHIFT; \ + } \ + static inline uint32_t __type##_UPDATE(unsigned int val) \ + { \ + return (val << __type##_SHIFT) & __type##_MASK; \ + } + +/* Register masks */ +/* OCIMEM_PSCGC_M0_M7_CTL */ +DECLARE_TYPE(BANK0_STATE, 3, 0); +DECLARE_TYPE(BANK1_STATE, 7, 4); +DECLARE_TYPE(BANK2_STATE, 11, 8); +DECLARE_TYPE(BANK3_STATE, 15, 12); +/* OCIMEM_PSCGC_TIMERS */ +DECLARE_TYPE(TIMERS_WAKEUP, 3, 0); +DECLARE_TYPE(TIMERS_SLEEP, 11, 8); +/* OCIMEM_HW_VERSION */ +DECLARE_TYPE(VERSION_STEP, 15, 0); +DECLARE_TYPE(VERSION_MINOR, 27, 16); +DECLARE_TYPE(VERSION_MAJOR, 31, 28); +/* OCIMEM_HW_PROFILE */ +DECLARE_TYPE(PROFILE_BANKS, 16, 12); +/* OCIMEM_AXI_ERR_SYNDROME */ +DECLARE_TYPE(ERR_SYN_ATID, 14, 8); +DECLARE_TYPE(ERR_SYN_AMID, 23, 16); +DECLARE_TYPE(ERR_SYN_APID, 28, 24); +DECLARE_TYPE(ERR_SYN_ABID, 31, 29); +/* OCIMEM_INTC_MASK */ +DECLARE_TYPE(AXI_ERR_INT, 0, 0); + +/* Internal stuff */ +#define MAX_BANKS 4 + +enum bank_state { + BANK_STATE_NORM_PASSTHRU = 0, + BANK_STATE_NORM_FORCE_CORE_ON = 2, + BANK_STATE_NORM_FORCE_PERIPH_ON = 1, + BANK_STATE_NORM_FORCE_ALL_ON = 3, + BANK_STATE_SLEEP_RET = 6, + BANK_STATE_SLEEP_RET_PERIPH_ON = 7, + BANK_STATE_SLEEP_NO_RET = 4, +}; + +struct vmem { + int irq; + int num_banks; + int bank_size; + struct { + struct resource *resource; + void __iomem *base; + } reg, mem; + struct regulator *vdd; + struct { + const char *name; + struct clk *clk; + } *clocks; + int num_clocks; + struct { + struct msm_bus_scale_pdata *pdata; + uint32_t priv; + } bus; + atomic_t alloc_count; + struct dentry *debugfs_root; +}; + +static struct vmem *vmem; + +static inline u32 __readl(void * __iomem addr) +{ + u32 value = 0; + + pr_debug("read %pK\n", addr); + value = readl_relaxed(addr); + pr_debug("-> %08x\n", value); + + return value; +} + +static inline void __writel(u32 val, void * __iomem addr) +{ + pr_debug("write %08x -> %pK\n", val, addr); + writel_relaxed(val, addr); + /* + * Commit all writes via a mem barrier, as subsequent __readl() + * will depend on the state that's set via __writel(). + */ + mb(); +} + +static inline void __wait_timer(struct vmem *v, bool wakeup) +{ + uint32_t ticks = 0; + unsigned int (*timer)(uint32_t) = wakeup ? + TIMERS_WAKEUP : TIMERS_SLEEP; + + ticks = timer(__readl(OCIMEM_PSCGC_TIMERS(v))); + + /* Sleep for `ticks` nanoseconds as per h/w spec */ + ndelay(ticks); +} + +static inline void __wait_wakeup(struct vmem *v) +{ + return __wait_timer(v, true); +} + +static inline void __wait_sleep(struct vmem *v) +{ + return __wait_timer(v, false); +} + +static inline int __power_on(struct vmem *v) +{ + int rc = 0, c = 0; + + rc = msm_bus_scale_client_update_request(v->bus.priv, 1); + if (rc) { + pr_err("Failed to vote for buses (%d)\n", rc); + goto exit; + } + pr_debug("Voted for buses\n"); + + rc = regulator_enable(v->vdd); + if (rc) { + pr_err("Failed to power on gdsc (%d)\n", rc); + goto unvote_bus; + } + pr_debug("Enabled regulator vdd\n"); + + for (c = 0; c < v->num_clocks; ++c) { + rc = clk_prepare_enable(v->clocks[c].clk); + if (rc) { + pr_err("Failed to enable %s clock (%d)\n", + v->clocks[c].name, rc); + goto disable_clocks; + } + + pr_debug("Enabled clock %s\n", v->clocks[c].name); + } + + return 0; +disable_clocks: + for (--c; c >= 0; c--) + clk_disable_unprepare(v->clocks[c].clk); + regulator_disable(v->vdd); +unvote_bus: + msm_bus_scale_client_update_request(v->bus.priv, 0); +exit: + return rc; +} + +static inline int __power_off(struct vmem *v) +{ + int c = 0; + + for (c = 0; c < v->num_clocks; ++c) { + clk_disable_unprepare(v->clocks[c].clk); + pr_debug("Disabled clock %s\n", v->clocks[c].name); + } + + regulator_disable(v->vdd); + pr_debug("Disabled regulator vdd\n"); + + msm_bus_scale_client_update_request(v->bus.priv, 0); + pr_debug("Unvoted for buses\n"); + + return 0; +} + +static inline enum bank_state __bank_get_state(struct vmem *v, + unsigned int bank) +{ + unsigned int (*func[MAX_BANKS])(uint32_t) = { + BANK0_STATE, BANK1_STATE, BANK2_STATE, BANK3_STATE + }; + + WARN_ON(bank >= ARRAY_SIZE(func)); + return func[bank](__readl(OCIMEM_PSCGC_M0_M7_CTL(v))); +} + +static inline void __bank_set_state(struct vmem *v, unsigned int bank, + enum bank_state state) +{ + uint32_t bank_state = 0; + struct { + uint32_t (*update)(unsigned int b); + uint32_t mask; + } banks[MAX_BANKS] = { + {BANK0_STATE_UPDATE, BANK0_STATE_MASK}, + {BANK1_STATE_UPDATE, BANK1_STATE_MASK}, + {BANK2_STATE_UPDATE, BANK2_STATE_MASK}, + {BANK3_STATE_UPDATE, BANK3_STATE_MASK}, + }; + + WARN_ON(bank >= ARRAY_SIZE(banks)); + + bank_state = __readl(OCIMEM_PSCGC_M0_M7_CTL(v)); + bank_state &= ~banks[bank].mask; + bank_state |= banks[bank].update(state); + + __writel(bank_state, OCIMEM_PSCGC_M0_M7_CTL(v)); +} + +static inline void __toggle_interrupts(struct vmem *v, bool enable) +{ + uint32_t ints = __readl(OCIMEM_INTC_MASK(v)), + mask = AXI_ERR_INT_MASK, + update = AXI_ERR_INT_UPDATE(!enable); + + ints &= ~mask; + ints |= update; + + __writel(ints, OCIMEM_INTC_MASK(v)); +} + +static void __enable_interrupts(struct vmem *v) +{ + pr_debug("Enabling interrupts\n"); + enable_irq(v->irq); + __toggle_interrupts(v, true); +} + +static void __disable_interrupts(struct vmem *v) +{ + pr_debug("Disabling interrupts\n"); + __toggle_interrupts(v, false); + disable_irq_nosync(v->irq); +} + +/** + * vmem_allocate: - Allocates memory from VMEM. Allocations have a few + * restrictions: only allocations of the entire VMEM memory are allowed, and + * , as a result, only single outstanding allocations are allowed. + * + * @size: amount of bytes to allocate + * @addr: A pointer to phys_addr_t where the physical address of the memory + * allocated is stored. + * + * Return: 0 in case of successful allocation (i.e. *addr != NULL). -ENOTSUPP, + * if platform doesn't support VMEM. -EEXIST, if there are outstanding VMEM + * allocations. -ENOMEM, if platform can't support allocation of `size` bytes. + * -EAGAIN, if `size` does not allocate the entire VMEM region. -EIO in case of + * internal errors. + */ +int vmem_allocate(size_t size, phys_addr_t *addr) +{ + int rc = 0, c = 0; + resource_size_t max_size = 0; + + if (!vmem) { + pr_err("No vmem, try rebooting your device\n"); + rc = -ENOTSUPP; + goto exit; + } + if (!size) { + pr_err("%s Invalid size %ld\n", __func__, size); + rc = -EINVAL; + goto exit; + } + + max_size = resource_size(vmem->mem.resource); + + if (atomic_read(&vmem->alloc_count)) { + pr_err("Only single allocations allowed for vmem\n"); + rc = -EEXIST; + goto exit; + } else if (size > max_size) { + pr_err("Out of memory, have max %pa\n", &max_size); + rc = -ENOMEM; + goto exit; + } else if (size != max_size) { + pr_err("Only support allocations of size %pa\n", &max_size); + rc = -EAGAIN; + goto exit; + } + + rc = __power_on(vmem); + if (rc) { + pr_err("Failed power on (%d)\n", rc); + goto exit; + } + + WARN_ON(vmem->num_banks != DIV_ROUND_UP(size, vmem->bank_size)); + + /* Turn on the necessary banks */ + for (c = 0; c < vmem->num_banks; ++c) { + __bank_set_state(vmem, c, BANK_STATE_NORM_FORCE_CORE_ON); + __wait_wakeup(vmem); + } + + /* Enable interrupts to detect faults */ + __enable_interrupts(vmem); + + atomic_inc(&vmem->alloc_count); + *addr = (phys_addr_t)vmem->mem.resource->start; + return 0; +exit: + return rc; +} + +/** + * vmem_free: - Frees the memory allocated via vmem_allocate. Undefined + * behaviour if to_free is a not a pointer returned via vmem_allocate + */ +void vmem_free(phys_addr_t to_free) +{ + int c = 0; + + if (!to_free || !vmem) + return; + + WARN_ON(atomic_read(&vmem->alloc_count) == 0); + + for (c = 0; c < vmem->num_banks; ++c) { + enum bank_state curr_state = __bank_get_state(vmem, c); + + if (curr_state != BANK_STATE_NORM_FORCE_CORE_ON) { + pr_warn("When freeing, expected bank state to be %d, was instead %d\n", + BANK_STATE_NORM_FORCE_CORE_ON, + curr_state); + } + + __bank_set_state(vmem, c, BANK_STATE_SLEEP_NO_RET); + } + + __disable_interrupts(vmem); + __power_off(vmem); + atomic_dec(&vmem->alloc_count); +} + +struct vmem_interrupt_cookie { + struct vmem *vmem; + struct work_struct work; +}; + +static void __irq_helper(struct work_struct *work) +{ + struct vmem_interrupt_cookie *cookie = container_of(work, + struct vmem_interrupt_cookie, work); + struct vmem *v = cookie->vmem; + unsigned int stat, gen_stat, pscgc_stat, err_addr_abs, + err_addr_rel, err_syn; + + stat = __readl(OCIMEM_INTC_STAT(v)); + gen_stat = __readl(OCIMEM_GEN_CTL(v)); + pscgc_stat = __readl(OCIMEM_PSCGC_STAT(v)); + + err_addr_abs = __readl(OCIMEM_ERR_ADDRESS(v)); + err_addr_rel = v->mem.resource->start - err_addr_abs; + + err_syn = __readl(OCIMEM_AXI_ERR_SYNDROME(v)); + + pr_crit("Detected a fault on VMEM:\n"); + pr_debug("\tinterrupt status: %x\n", stat); + pr_debug("\tgeneral status: %x\n", gen_stat); + pr_debug("\tmemory status: %x\n", pscgc_stat); + pr_debug("\tfault address: %x (absolute), %x (relative)\n", + err_addr_abs, err_addr_rel); + pr_debug("\tfault bank: %x\n", err_addr_rel / v->bank_size); + pr_debug("\tfault core: %u (mid), %u (pid), %u (bid)\n", + ERR_SYN_AMID(err_syn), ERR_SYN_APID(err_syn), + ERR_SYN_ABID(err_syn)); + + /* Clear the interrupt */ + __writel(0, OCIMEM_INTC_CLR(v)); + + __enable_interrupts(v); +} + +static struct vmem_interrupt_cookie interrupt_cookie; + +static irqreturn_t __irq_handler(int irq, void *cookie) +{ + struct vmem *v = cookie; + irqreturn_t status = __readl(OCIMEM_INTC_STAT(vmem)) ? + IRQ_HANDLED : IRQ_NONE; + + if (status != IRQ_NONE) { + /* Mask further interrupts while handling this one */ + __disable_interrupts(v); + + interrupt_cookie.vmem = v; + INIT_WORK(&interrupt_cookie.work, __irq_helper); + schedule_work(&interrupt_cookie.work); + } + + return status; +} + +static inline int __init_resources(struct vmem *v, + struct platform_device *pdev) +{ + int rc = 0, c = 0; + + v->irq = platform_get_irq(pdev, 0); + if (v->irq < 0) { + rc = v->irq; + pr_err("Failed to get irq (%d)\n", rc); + v->irq = 0; + goto exit; + } + + /* Registers and memory */ + v->reg.resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "reg-base"); + if (!v->reg.resource) { + pr_err("Failed to find register base\n"); + rc = -ENOENT; + goto exit; + } + + v->reg.base = devm_ioremap_resource(&pdev->dev, v->reg.resource); + if (IS_ERR_OR_NULL(v->reg.base)) { + rc = PTR_ERR(v->reg.base) ?: -EIO; + pr_err("Failed to map register base into kernel (%d)\n", rc); + v->reg.base = NULL; + goto exit; + } + + pr_debug("Register range: %pa -> %pa\n", &v->reg.resource->start, + &v->reg.resource->end); + + v->mem.resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mem-base"); + if (!v->mem.resource) { + pr_err("Failed to find memory base\n"); + rc = -ENOENT; + goto exit; + } + + v->mem.base = NULL; + pr_debug("Memory range: %pa -> %pa\n", &v->mem.resource->start, + &v->mem.resource->end); + + /* Buses, Clocks & Regulators*/ + v->num_clocks = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (v->num_clocks <= 0) { + pr_err("Can't find any clocks\n"); + goto exit; + } + + v->clocks = devm_kzalloc(&pdev->dev, sizeof(*v->clocks) * v->num_clocks, + GFP_KERNEL); + if (!v->clocks) { + rc = -ENOMEM; + goto exit; + } + + for (c = 0; c < v->num_clocks; ++c) { + const char *name = NULL; + struct clk *temp = NULL; + + of_property_read_string_index(pdev->dev.of_node, "clock-names", + c, &name); + temp = devm_clk_get(&pdev->dev, name); + if (IS_ERR_OR_NULL(temp)) { + rc = PTR_ERR(temp) ?: -ENOENT; + pr_err("Failed to find %s (%d)\n", name, rc); + goto exit; + } + + v->clocks[c].clk = temp; + v->clocks[c].name = name; + } + + v->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR_OR_NULL(v->vdd)) { + rc = PTR_ERR(v->vdd) ?: -ENOENT; + pr_err("Failed to find regulator (vdd) (%d)\n", rc); + goto exit; + } + + v->bus.pdata = msm_bus_cl_get_pdata(pdev); + if (IS_ERR_OR_NULL(v->bus.pdata)) { + rc = PTR_ERR(v->bus.pdata) ?: -ENOENT; + pr_err("Failed to find bus vectors (%d)\n", rc); + goto exit; + } + + v->bus.priv = msm_bus_scale_register_client(v->bus.pdata); + if (!v->bus.priv) { + rc = -EBADHANDLE; + pr_err("Failed to register bus client\n"); + goto free_pdata; + } + + /* Misc. */ + rc = of_property_read_u32(pdev->dev.of_node, "qcom,bank-size", + &v->bank_size); + if (rc || !v->bank_size) { + pr_err("Failed reading (or found invalid) qcom,bank-size in %s (%d)\n", + of_node_full_name(pdev->dev.of_node), rc); + rc = -ENOENT; + goto free_pdata; + } + + v->num_banks = resource_size(v->mem.resource) / v->bank_size; + + pr_debug("Found configuration with %d banks with size %d\n", + v->num_banks, v->bank_size); + + return 0; +free_pdata: + msm_bus_cl_clear_pdata(v->bus.pdata); +exit: + return rc; +} + +static inline void __uninit_resources(struct vmem *v, + struct platform_device *pdev) +{ + int c = 0; + + msm_bus_cl_clear_pdata(v->bus.pdata); + v->bus.pdata = NULL; + v->bus.priv = 0; + + for (c = 0; c < v->num_clocks; ++c) { + v->clocks[c].clk = NULL; + v->clocks[c].name = NULL; + } + + v->vdd = NULL; +} + +static int vmem_probe(struct platform_device *pdev) +{ + uint32_t version = 0, num_banks = 0, rc = 0; + struct vmem *v = NULL; + + if (vmem) { + pr_err("Only one instance of %s allowed\n", pdev->name); + return -EEXIST; + } + + v = devm_kzalloc(&pdev->dev, sizeof(*v), GFP_KERNEL); + if (!v) + return -ENOMEM; + + rc = __init_resources(v, pdev); + if (rc) { + pr_err("Failed to read resources\n"); + goto exit; + } + + /* + * For now, only support up to 4 banks. It's unrealistic that VMEM has + * more banks than that (even in the future). + */ + if (v->num_banks > MAX_BANKS) { + pr_err("Number of banks (%d) exceeds what's supported (%d)\n", + v->num_banks, MAX_BANKS); + rc = -ENOTSUPP; + goto exit; + } + + /* Cross check the platform resources with what's available on chip */ + rc = __power_on(v); + if (rc) { + pr_err("Failed to power on (%d)\n", rc); + goto exit; + } + + version = __readl(OCIMEM_HW_VERSION(v)); + pr_debug("v%d.%d.%d\n", VERSION_MAJOR(version), VERSION_MINOR(version), + VERSION_STEP(version)); + + num_banks = PROFILE_BANKS(__readl(OCIMEM_HW_PROFILE(v))); + pr_debug("Found %d banks on chip\n", num_banks); + if (v->num_banks != num_banks) { + pr_err("Platform configuration of %d banks differs from what's available on chip (%d)\n", + v->num_banks, num_banks); + rc = -EINVAL; + goto disable_clocks; + } + + rc = devm_request_irq(&pdev->dev, v->irq, __irq_handler, + IRQF_TRIGGER_HIGH, "vmem", v); + if (rc) { + pr_err("Failed to setup irq (%d)\n", rc); + goto disable_clocks; + } + + __disable_interrupts(v); + + /* Everything good so far, set up the global context and debug hooks */ + pr_info("Up and running with %d banks of memory from %pR\n", + v->num_banks, &v->mem.resource); + v->debugfs_root = vmem_debugfs_init(pdev); + platform_set_drvdata(pdev, v); + vmem = v; + +disable_clocks: + __power_off(v); +exit: + return rc; +} + +static int vmem_remove(struct platform_device *pdev) +{ + struct vmem *v = platform_get_drvdata(pdev); + + WARN_ON(v != vmem); + + __uninit_resources(v, pdev); + vmem_debugfs_deinit(v->debugfs_root); + vmem = NULL; + + return 0; +} + +static const struct of_device_id vmem_of_match[] = { + {.compatible = "qcom,msm-vmem"}, + {} +}; + +MODULE_DEVICE_TABLE(of, vmem_of_match); + +static struct platform_driver vmem_driver = { + .probe = vmem_probe, + .remove = vmem_remove, + .driver = { + .name = "msm_vidc_vmem", + .of_match_table = vmem_of_match, + }, +}; + +static int __init vmem_init(void) +{ + return platform_driver_register(&vmem_driver); +} + +static void __exit vmem_exit(void) +{ + platform_driver_unregister(&vmem_driver); +} + +module_init(vmem_init); +module_exit(vmem_exit); diff --git a/drivers/media/platform/msm/vidc_3x/vmem/vmem.h b/drivers/media/platform/msm/vidc_3x/vmem/vmem.h new file mode 100644 index 000000000000..092fb6c468ef --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vmem/vmem.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VMEM_H__ +#define __VMEM_H__ + +#ifdef CONFIG_MSM_VIDC_VMEM + +int vmem_allocate(size_t size, phys_addr_t *addr); +void vmem_free(phys_addr_t to_free); + +#else + +static inline int vmem_allocate(size_t size, phys_addr_t *addr) +{ + return -ENODEV; +} + +static inline void vmem_free(phys_addr_t to_free) +{ +} + +#endif + +#endif /* __VMEM_H__ */ diff --git a/drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.c b/drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.c new file mode 100644 index 000000000000..ca39b21242d1 --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include "vmem.h" + +struct vmem_debugfs_cookie { + phys_addr_t addr; + size_t size; +}; + +static int __vmem_alloc_get(void *priv, u64 *val) +{ + struct vmem_debugfs_cookie *cookie = priv; + + *val = cookie->size; + return 0; +} + +static int __vmem_alloc_set(void *priv, u64 val) +{ + struct vmem_debugfs_cookie *cookie = priv; + int rc = 0; + + switch (val) { + case 0: /* free */ + vmem_free(cookie->addr); + cookie->size = 0; + break; + default: + rc = vmem_allocate(val, &cookie->addr); + cookie->size = val; + break; + } + + return rc; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_vmem_alloc, __vmem_alloc_get, + __vmem_alloc_set, "%llu"); + +struct dentry *vmem_debugfs_init(struct platform_device *pdev) +{ + struct vmem_debugfs_cookie *alloc_cookie = NULL; + struct dentry *debugfs_root = NULL; + + alloc_cookie = devm_kzalloc(&pdev->dev, sizeof(*alloc_cookie), + GFP_KERNEL); + if (!alloc_cookie) + goto exit; + + debugfs_root = debugfs_create_dir("vmem", NULL); + if (IS_ERR_OR_NULL(debugfs_root)) { + pr_warn("Failed to create '/vmem'\n"); + debugfs_root = NULL; + goto exit; + } + + debugfs_create_file("alloc", 0600, debugfs_root, + alloc_cookie, &fops_vmem_alloc); + +exit: + return debugfs_root; +} + +void vmem_debugfs_deinit(struct dentry *debugfs_root) +{ + debugfs_remove_recursive(debugfs_root); +} + diff --git a/drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.h b/drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.h new file mode 100644 index 000000000000..f42b28d6521d --- /dev/null +++ b/drivers/media/platform/msm/vidc_3x/vmem/vmem_debugfs.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014, 2018 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VMEM_DEBUGFS_H__ +#define __VMEM_DEBUGFS_H__ + +#include + +struct dentry *vmem_debugfs_init(struct platform_device *pdev); +void vmem_debugfs_deinit(struct dentry *debugfs_root); + +#endif /* __VMEM_DEBUGFS_H__ */ diff --git a/gen_headers_arm.bp b/gen_headers_arm.bp index 31da7a32a01e..2458cba96233 100644 --- a/gen_headers_arm.bp +++ b/gen_headers_arm.bp @@ -645,6 +645,7 @@ gen_headers_out_arm = [ "media/msm_cvp_utils.h", "media/msm_media_info.h", "media/msm_sde_rotator.h", + "media/msm_vidc.h", "media/msm_vidc_private.h", "media/msm_vidc_utils.h", "media/radio-iris-commands.h", diff --git a/gen_headers_arm64.bp b/gen_headers_arm64.bp index b6bcb982845f..9f2a27d69fdd 100644 --- a/gen_headers_arm64.bp +++ b/gen_headers_arm64.bp @@ -641,6 +641,7 @@ gen_headers_out_arm64 = [ "media/msm_sde_rotator.h", "media/msm_vidc_private.h", "media/msm_vidc_utils.h", + "media/msm_vidc.h", "media/radio-iris-commands.h", "media/radio-iris.h", "media/synx.h", diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h new file mode 100644 index 000000000000..0f8a090b750b --- /dev/null +++ b/include/media/msm_vidc.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MSM_VIDC_H_ +#define _MSM_VIDC_H_ + +#include +#include +#include +#include +#include + +#define HAL_BUFFER_MAX 0xd + +enum smem_type { + SMEM_ION, +}; + +enum smem_prop { + SMEM_UNCACHED = 0x1, + SMEM_CACHED = 0x2, + SMEM_SECURE = 0x4, + SMEM_ADSP = 0x8, +}; + +/* NOTE: if you change this enum you MUST update the + * "buffer-type-tz-usage-table" for any affected target + * in arch/arm/boot/dts/.dtsi + */ +enum hal_buffer { + HAL_BUFFER_NONE = 0x0, + HAL_BUFFER_INPUT = 0x1, + HAL_BUFFER_OUTPUT = 0x2, + HAL_BUFFER_OUTPUT2 = 0x4, + HAL_BUFFER_EXTRADATA_INPUT = 0x8, + HAL_BUFFER_EXTRADATA_OUTPUT = 0x10, + HAL_BUFFER_EXTRADATA_OUTPUT2 = 0x20, + HAL_BUFFER_INTERNAL_SCRATCH = 0x40, + HAL_BUFFER_INTERNAL_SCRATCH_1 = 0x80, + HAL_BUFFER_INTERNAL_SCRATCH_2 = 0x100, + HAL_BUFFER_INTERNAL_PERSIST = 0x200, + HAL_BUFFER_INTERNAL_PERSIST_1 = 0x400, + HAL_BUFFER_INTERNAL_CMD_QUEUE = 0x800, + HAL_BUFFER_INTERNAL_RECON = 0x1000, +}; + +struct dma_mapping_info { + struct device *dev; + struct iommu_domain *domain; + struct sg_table *table; + struct dma_buf_attachment *attach; + struct dma_buf *buf; + void *cb_info; +}; + +struct msm_smem { + u32 refcount; + int fd; + void *dma_buf; + void *handle; + void *kvaddr; + u32 device_addr; + unsigned int offset; + unsigned int size; + unsigned long flags; + enum hal_buffer buffer_type; + struct dma_mapping_info mapping_info; + int mem_type; + void *smem_priv; +}; + +enum smem_cache_ops { + SMEM_CACHE_CLEAN, + SMEM_CACHE_INVALIDATE, + SMEM_CACHE_CLEAN_INVALIDATE, +}; + +enum core_id { + MSM_VIDC_CORE_VENUS = 0, + MSM_VIDC_CORE_Q6, + MSM_VIDC_CORES_MAX, +}; +enum session_type { + MSM_VIDC_ENCODER = 0, + MSM_VIDC_DECODER, + MSM_VIDC_UNKNOWN, + MSM_VIDC_MAX_DEVICES = MSM_VIDC_UNKNOWN, +}; + +union msm_v4l2_cmd { + struct v4l2_decoder_cmd dec; + struct v4l2_encoder_cmd enc; +}; + +void *msm_vidc_open(int core_id, int session_type); +int msm_vidc_close(void *instance); +int msm_vidc_suspend(int core_id); +int msm_vidc_querycap(void *instance, struct v4l2_capability *cap); +int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f); +int msm_vidc_s_fmt(void *instance, struct v4l2_format *f); +int msm_vidc_g_fmt(void *instance, struct v4l2_format *f); +int msm_vidc_release_buffers(void *instance, int buffer_type); +int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b); +int msm_vidc_s_ctrl(void *instance, struct v4l2_control *a); +int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); +int msm_vidc_g_ext_ctrl(void *instance, struct v4l2_ext_controls *a); +int msm_vidc_g_ctrl(void *instance, struct v4l2_control *a); +int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b); +int msm_vidc_release_buffer(void *instance, int buffer_type, + unsigned int buffer_index); +int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b); +int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b); +int msm_vidc_streamon(void *instance, enum v4l2_buf_type i); +int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl); +int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i); +int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd); +int msm_vidc_poll(void *instance, struct file *filp, + struct poll_table_struct *pt); +int msm_vidc_subscribe_event(void *instance, + const struct v4l2_event_subscription *sub); +int msm_vidc_unsubscribe_event(void *instance, + const struct v4l2_event_subscription *sub); +int msm_vidc_dqevent(void *instance, struct v4l2_event *event); +int msm_vidc_g_crop(void *instance, struct v4l2_crop *a); +int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize); +#endif diff --git a/include/trace/events/msm_vidc.h b/include/trace/events/msm_vidc.h new file mode 100644 index 000000000000..f3bc2de8685f --- /dev/null +++ b/include/trace/events/msm_vidc.h @@ -0,0 +1,406 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014-2017, 2019 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM msm_vidc + +#if !defined(_TRACE_MSM_VIDC_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_MSM_VIDC_H +#include +#include + +DECLARE_EVENT_CLASS(msm_v4l2_vidc, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy), + + TP_STRUCT__entry( + __field(char *, dummy) + ), + + TP_fast_assign( + __entry->dummy = dummy; + ), + + TP_printk("%s", __entry->dummy) +); + +DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_open_start, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy) +); + +DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_open_end, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy) +); + +DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_close_start, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy) +); + +DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_close_end, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy) +); + +DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_fw_load_start, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy) +); + +DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_fw_load_end, + + TP_PROTO(char *dummy), + + TP_ARGS(dummy) +); + +DECLARE_EVENT_CLASS(msm_vidc_common, + + TP_PROTO(void *instp, int old_state, int new_state), + + TP_ARGS(instp, old_state, new_state), + + TP_STRUCT__entry( + __field(void *, instp) + __field(int, old_state) + __field(int, new_state) + ), + + TP_fast_assign( + __entry->instp = instp; + __entry->old_state = old_state; + __entry->new_state = new_state; + ), + + TP_printk("Moved inst: %p from 0x%x to 0x%x", + __entry->instp, + __entry->old_state, + __entry->new_state) +); + +DEFINE_EVENT(msm_vidc_common, msm_vidc_common_state_change, + + TP_PROTO(void *instp, int old_state, int new_state), + + TP_ARGS(instp, old_state, new_state) +); + +DECLARE_EVENT_CLASS(venus_hfi_var, + + TP_PROTO(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, u32 cp_nonpixel_size), + + TP_ARGS(cp_start, cp_size, cp_nonpixel_start, cp_nonpixel_size), + + TP_STRUCT__entry( + __field(u32, cp_start) + __field(u32, cp_size) + __field(u32, cp_nonpixel_start) + __field(u32, cp_nonpixel_size) + ), + + TP_fast_assign( + __entry->cp_start = cp_start; + __entry->cp_size = cp_size; + __entry->cp_nonpixel_start = cp_nonpixel_start; + __entry->cp_nonpixel_size = cp_nonpixel_size; + ), + + TP_printk( + "TZBSP_MEM_PROTECT_VIDEO_VAR done, cp_start : 0x%x, cp_size : 0x%x, cp_nonpixel_start : 0x%x, cp_nonpixel_size : 0x%x", + __entry->cp_start, + __entry->cp_size, + __entry->cp_nonpixel_start, + __entry->cp_nonpixel_size) +); + +DEFINE_EVENT(venus_hfi_var, venus_hfi_var_done, + + TP_PROTO(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, u32 cp_nonpixel_size), + + TP_ARGS(cp_start, cp_size, cp_nonpixel_start, cp_nonpixel_size) +); + +DECLARE_EVENT_CLASS(msm_v4l2_vidc_buffer_events, + + TP_PROTO(char *event_type, u32 device_addr, int64_t timestamp, + u32 alloc_len, u32 filled_len, u32 offset), + + TP_ARGS(event_type, device_addr, timestamp, alloc_len, + filled_len, offset), + + TP_STRUCT__entry( + __field(char *, event_type) + __field(u32, device_addr) + __field(int64_t, timestamp) + __field(u32, alloc_len) + __field(u32, filled_len) + __field(u32, offset) + ), + + TP_fast_assign( + __entry->event_type = event_type; + __entry->device_addr = device_addr; + __entry->timestamp = timestamp; + __entry->alloc_len = alloc_len; + __entry->filled_len = filled_len; + __entry->offset = offset; + ), + + TP_printk( + "%s, device_addr : 0x%x, timestamp : %lld, alloc_len : 0x%x, filled_len : 0x%x, offset : 0x%x", + __entry->event_type, + __entry->device_addr, + __entry->timestamp, + __entry->alloc_len, + __entry->filled_len, + __entry->offset) +); + +DEFINE_EVENT(msm_v4l2_vidc_buffer_events, msm_v4l2_vidc_buffer_event_start, + + TP_PROTO(char *event_type, u32 device_addr, int64_t timestamp, + u32 alloc_len, u32 filled_len, u32 offset), + + TP_ARGS(event_type, device_addr, timestamp, alloc_len, + filled_len, offset) +); + +DEFINE_EVENT(msm_v4l2_vidc_buffer_events, msm_v4l2_vidc_buffer_event_end, + + TP_PROTO(char *event_type, u32 device_addr, int64_t timestamp, + u32 alloc_len, u32 filled_len, u32 offset), + + TP_ARGS(event_type, device_addr, timestamp, alloc_len, + filled_len, offset) +); + +DECLARE_EVENT_CLASS(msm_smem_buffer_ion_ops, + + TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask, + size_t size, u32 align, u32 flags, int map_kernel), + + TP_ARGS(buffer_op, buffer_type, heap_mask, size, align, + flags, map_kernel), + + TP_STRUCT__entry( + __string(buffer_op, buffer_op) + __field(u32, buffer_type) + __field(u32, heap_mask) + __field(u32, size) + __field(u32, align) + __field(u32, flags) + __field(int, map_kernel) + ), + + TP_fast_assign( + __assign_str(buffer_op, buffer_op); + __entry->buffer_type = buffer_type; + __entry->heap_mask = heap_mask; + __entry->size = size; + __entry->align = align; + __entry->flags = flags; + __entry->map_kernel = map_kernel; + ), + + TP_printk( + "%s, buffer_type : 0x%x, heap_mask : 0x%x, size : 0x%x, align : 0x%x, flags : 0x%x, map_kernel : %d", + __get_str(buffer_op), + __entry->buffer_type, + __entry->heap_mask, + __entry->size, + __entry->align, + __entry->flags, + __entry->map_kernel) +); + +DEFINE_EVENT(msm_smem_buffer_ion_ops, msm_smem_buffer_ion_op_start, + + TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask, + size_t size, u32 align, u32 flags, int map_kernel), + + TP_ARGS(buffer_op, buffer_type, heap_mask, size, align, + flags, map_kernel) +); + +DEFINE_EVENT(msm_smem_buffer_ion_ops, msm_smem_buffer_ion_op_end, + + TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask, + size_t size, u32 align, u32 flags, int map_kernel), + + TP_ARGS(buffer_op, buffer_type, heap_mask, size, align, + flags, map_kernel) +); +DECLARE_EVENT_CLASS(msm_smem_buffer_dma_ops, + + TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask, + size_t size, u32 align, u32 flags, int map_kernel), + + TP_ARGS(buffer_op, buffer_type, heap_mask, size, align, + flags, map_kernel), + + TP_STRUCT__entry( + __string(buffer_op, buffer_op) + __field(u32, buffer_type) + __field(u32, heap_mask) + __field(u32, size) + __field(u32, align) + __field(u32, flags) + __field(int, map_kernel) + ), + + TP_fast_assign( + __assign_str(buffer_op, buffer_op); + __entry->buffer_type = buffer_type; + __entry->heap_mask = heap_mask; + __entry->size = size; + __entry->align = align; + __entry->flags = flags; + __entry->map_kernel = map_kernel; + ), + + TP_printk( + "%s, buffer_type : 0x%x, heap_mask : 0x%x, size : 0x%x, align : 0x%x, flags : 0x%x, map_kernel : %d", + __get_str(buffer_op), + __entry->buffer_type, + __entry->heap_mask, + __entry->size, + __entry->align, + __entry->flags, + __entry->map_kernel) +); + +DEFINE_EVENT(msm_smem_buffer_dma_ops, msm_smem_buffer_dma_op_start, + + TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask, + size_t size, u32 align, u32 flags, int map_kernel), + + TP_ARGS(buffer_op, buffer_type, heap_mask, size, align, + flags, map_kernel) +); + +DEFINE_EVENT(msm_smem_buffer_dma_ops, msm_smem_buffer_dma_op_end, + + TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask, + size_t size, u32 align, u32 flags, int map_kernel), + + TP_ARGS(buffer_op, buffer_type, heap_mask, size, align, + flags, map_kernel) +); + +DECLARE_EVENT_CLASS(msm_smem_buffer_iommu_ops, + + TP_PROTO(char *buffer_op, int domain_num, int partition_num, + unsigned long align, unsigned long iova, + unsigned long buffer_size), + + TP_ARGS(buffer_op, domain_num, partition_num, align, iova, buffer_size), + + TP_STRUCT__entry( + __string(buffer_op, buffer_op) + __field(int, domain_num) + __field(int, partition_num) + __field(unsigned long, align) + __field(unsigned long, iova) + __field(unsigned long, buffer_size) + ), + + TP_fast_assign( + __assign_str(buffer_op, buffer_op); + __entry->domain_num = domain_num; + __entry->partition_num = partition_num; + __entry->align = align; + __entry->iova = iova; + __entry->buffer_size = buffer_size; + ), + + TP_printk( + "%s, domain : %d, partition : %d, align : %lx, iova : 0x%lx, buffer_size=%lx", + __get_str(buffer_op), + __entry->domain_num, + __entry->partition_num, + __entry->align, + __entry->iova, + __entry->buffer_size) +); + +DEFINE_EVENT(msm_smem_buffer_iommu_ops, msm_smem_buffer_iommu_op_start, + + TP_PROTO(char *buffer_op, int domain_num, int partition_num, + unsigned long align, unsigned long iova, + unsigned long buffer_size), + + TP_ARGS(buffer_op, domain_num, partition_num, align, iova, buffer_size) +); + +DEFINE_EVENT(msm_smem_buffer_iommu_ops, msm_smem_buffer_iommu_op_end, + + TP_PROTO(char *buffer_op, int domain_num, int partition_num, + unsigned long align, unsigned long iova, + unsigned long buffer_size), + + TP_ARGS(buffer_op, domain_num, partition_num, align, iova, buffer_size) +); + +DECLARE_EVENT_CLASS(msm_vidc_perf, + + TP_PROTO(const char *name, unsigned long value), + + TP_ARGS(name, value), + + TP_STRUCT__entry( + __field(const char *, name) + __field(unsigned long, value) + ), + + TP_fast_assign( + __entry->name = name; + __entry->value = value; + ), + + TP_printk("%s %lu", __entry->name, __entry->value) +); + +DEFINE_EVENT(msm_vidc_perf, msm_vidc_perf_clock_scale, + + TP_PROTO(const char *clock_name, unsigned long frequency), + + TP_ARGS(clock_name, frequency) +); + +DEFINE_EVENT(msm_vidc_perf, msm_vidc_perf_bus_vote, + + TP_PROTO(const char *governor_mode, unsigned long ab), + + TP_ARGS(governor_mode, ab) +); + +#endif + +#include diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 7369a223aa7d..b1e3eca197f8 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -387,6 +387,7 @@ enum v4l2_mpeg_video_bitrate_mode { enum v4l2_mpeg_video_header_mode { V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE = 0, V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME = 1, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME = 2, }; #define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (V4L2_CID_MPEG_BASE+217) @@ -398,6 +399,7 @@ enum v4l2_mpeg_video_multi_slice_mode { V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0, V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB = 1, V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2, + V4L2_MPEG_VIDEO_MULTI_SLICE_GOB = 3, }; #define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222) #define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223) @@ -784,7 +786,6 @@ enum v4l2_mpeg_mfc51_video_force_frame_type { /* MPEG-class control IDs specific to the msm_vidc driver */ #define V4L2_CID_MPEG_MSM_VIDC_BASE (V4L2_CTRL_CLASS_MPEG | 0x2000) - #define V4L2_MPEG_MSM_VIDC_DISABLE 0 #define V4L2_MPEG_MSM_VIDC_ENABLE 1 @@ -796,21 +797,81 @@ enum v4l2_mpeg_vidc_video_pictype_dec_mode { V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_B = 4, }; +enum v4l2_mpeg_vidc_video_pictype_dec_enable { + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_OFF = 0, + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+1) + #define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT (V4L2_CID_MPEG_MSM_VIDC_BASE+2) enum v4l2_mpeg_vidc_video_stream_format { V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES = 0, + V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_NAL_PER_BUFFER = 1, + V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_BYTE_LENGTH = 2, + V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_TWO_BYTE_LENGTH = 3, V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH = 4, }; #define V4L2_CID_MPEG_VIDC_VIDEO_DECODE_ORDER (V4L2_CID_MPEG_MSM_VIDC_BASE+3) +#define V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD (V4L2_CID_MPEG_MSM_VIDC_BASE+5) +#define V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+6) +#define V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+7) +#define V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME (V4L2_CID_MPEG_MSM_VIDC_BASE+8) + +#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL (V4L2_CID_MPEG_MSM_VIDC_BASE+9) +enum v4l2_mpeg_vidc_video_rate_control { + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF = 0, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR = 1, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR = 2, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR = 3, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR = 4, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR = 5, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR = 6, + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CQ = 7, +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR \ + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR +#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR \ + V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR + +#define V4L2_CID_MPEG_VIDC_VIDEO_ROTATION (V4L2_CID_MPEG_MSM_VIDC_BASE+10) +enum v4l2_mpeg_vidc_video_rotation { + V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90 = 1, + V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180 = 2, + V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270 = 3, +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+11) +enum v4l2_mpeg_vidc_h264_cabac_model { + V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0 = 0, + V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1 = 1, + V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2 = 2, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE_CYCLIC \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+12) #define V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM \ (V4L2_CID_MPEG_MSM_VIDC_BASE+13) #define V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 14) + +enum v4l2_mpeg_vidc_video_au_delimiter { + V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED = 1 +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 15) +enum v4l2_mpeg_vidc_video_sync_frame_decode { + V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE = 0, + V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE = 1 +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_SECURE (V4L2_CID_MPEG_MSM_VIDC_BASE+16) #define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 17) @@ -824,6 +885,66 @@ enum v4l2_mpeg_vidc_extradata { EXTRADATA_ENC_INPUT_HDR10PLUS = 8, EXTRADATA_ENC_INPUT_CVP = 16, }; +enum v4l2_mpeg_vidc3x_extradata { + V4L2_MPEG_VIDC_EXTRADATA_NONE = 0, + V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION = 1, + V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO = 2, + #define V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS \ + V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS + V4L2_MPEG_VIDC_EXTRADATA_ENC_DTS = 3, + V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP = 4, + V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP = 5, + V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING = 6, + V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE = 7, + V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW = 8, + V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 9, + V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO = 10, + V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB = 11, + V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER = 12, +#define V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP \ + V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP + V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP = 13, + V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM = 14, + V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO = 15, + V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP = 16, + V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA = 17, + V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP = 18, + V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO = 19, + V4L2_MPEG_VIDC_EXTRADATA_LTR = 20, + V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI = 21, + V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI = 22, +#define V4L2_MPEG_VIDC_EXTRADATA_HDR10PLUS_METADATA \ + V4L2_MPEG_VIDC_EXTRADATA_HDR10PLUS_METADATA + V4L2_MPEG_VIDC_EXTRADATA_HDR10PLUS_METADATA = 23, + V4L2_MPEG_VIDC_EXTRADATA_ROI_QP = 24, +#define V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP \ + V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP + V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP = 25, +#define V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI \ + V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI + V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI = 26, +#define V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \ + V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI + V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 27, +#define V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO \ + V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO + V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO = 28, +#define V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY \ + V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY + V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY = 29, +#define V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE \ + V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE + V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE = 30, +#define V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO \ + V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO + V4L2_MPEG_VIDC_EXTRADATA_UBWC_CR_STATS_INFO = 31, +#define V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP \ + V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP + V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP = 32, + V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP = 33, + V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS = 34, +}; + enum v4l2_mpeg_cvp_extradata { V4L2_MPEG_CVP_EXTRADATA_NONE = 0, V4L2_MPEG_CVP_EXTRADATA_INTERLACE_VIDEO = 2, @@ -850,9 +971,17 @@ enum v4l2_mpeg_cvp_extradata { V4L2_MPEG_CVP_EXTRADATA_UBWC_CR_STATS_INFO = 31, }; +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 18) + #define V4L2_CID_MPEG_VIDC_VIDEO_VUI_TIMING_INFO \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 19) +enum v4l2_mpeg_vidc_video_vui_timing_info { + V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_ENABLED = 1 +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 20) enum v4l2_mpeg_vidc_video_vp8_profile_level { @@ -863,6 +992,13 @@ enum v4l2_mpeg_vidc_video_vp8_profile_level { V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3, }; +#define V4L2_CID_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 21) +enum v4l2_mpeg_vidc_video_preserve_text_quality { + V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_ENABLED = 1 +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE+23) enum v4l2_mpeg_vidc_video_mpeg2_level { V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0 = 0, @@ -875,6 +1011,26 @@ enum v4l2_mpeg_vidc_video_mpeg2_profile { V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE = 0, V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN = 1, V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH = 2, + V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SNR_SCALABLE = 3, + V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SPATIAL_SCALABLE = 4, + V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_422 = 5, +}; + +enum v4l2_mpeg_vidc_video_mvc_layout { + V4L2_MPEG_VIDC_VIDEO_MVC_SEQUENTIAL = 0, + V4L2_MPEG_VIDC_VIDEO_MVC_TOP_BOTTOM = 1 +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 25) + +#define V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 26) + +enum v4l2_mpeg_vidc_video_ltrmode { + V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE = 0, + V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL = 1, + V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC = 2 }; #define V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT \ @@ -886,29 +1042,176 @@ enum v4l2_mpeg_vidc_video_mpeg2_profile { #define V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 29) +#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 30) + +#define V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_OUTPUT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+31) +enum v4l2_mpeg_vidc_video_alloc_mode_type { + V4L2_MPEG_VIDC_VIDEO_STATIC = 0, + V4L2_MPEG_VIDC_VIDEO_RING = 1, + V4L2_MPEG_VIDC_VIDEO_DYNAMIC = 2, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 32) + +#define V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 33) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 34) + +#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 35) + +#define V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 36) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 37) + #define V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 38) #define V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 39) +enum vl42_mpeg_vidc_video_vpx_error_resilience { + V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_ENABLED = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 40) +enum v4l2_cid_mpeg_video_hevc_profile { + V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN = 0, + V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10 = 1, + V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC = 2, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 41) +enum v4l2_cid_mpeg_video_hevc_level { + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1 = 0, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1 = 1, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2 = 2, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2 = 3, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1 = 4, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1 = 5, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3 = 6, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3 = 7, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1 = 8, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1 = 9, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4 = 10, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4 = 11, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1 = 12, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1 = 13, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5 = 14, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5 = 15, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1 = 16, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1 = 17, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_2 = 18, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2 = 19, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6 = 20, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6 = 21, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_1 = 22, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_1 = 23, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_2 = 24, + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_2 = 25, +#define V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN \ + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_UNKNOWN = 26, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 42) + +#define V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 43) + +#define V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 44) + +enum v4l2_mpeg_vidc_video_dpb_color_format { + V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE = 0, + V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC = 1, + V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC = 2 +}; + +#define V4L2_CID_VIDC_QBUF_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 45) +enum v4l2_vidc_qbuf_mode { + V4L2_VIDC_QBUF_STANDARD = 0, + V4L2_VIDC_QBUF_BATCHED = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 46) + #define V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 47) +#define V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 48) + +#define V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 49) + +#define V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 50) + +enum v4l2_mpeg_vidc_video_vqzip_sei_enable { + V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_DISABLE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_ENABLE = 1 +}; + +#define V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 51) + #define V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 52) +enum v4l2_mpeg_vidc_video_priority { + V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE = 0, + V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE = 1, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 53) +#define V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 54) + +enum v4l2_mpeg_vidc_video_venc_bitrate_type_enable { + V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_DISABLE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE = 1 +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 55) + (V4L2_CID_MPEG_MSM_VIDC_BASE + 55) + +enum v4l2_cid_mpeg_vidc_video_vpe_csc_type_enable { + V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_DISABLE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE = 1 +}; #define V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 56) +enum v4l2_mpeg_vidc_video_lowlatency_mode { + V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE = 1, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_DIMENSIONS \ - (V4L2_CID_MPEG_MSM_VIDC_BASE + 57) + (V4L2_CID_MPEG_MSM_VIDC_BASE + 57) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 58) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 59) #define V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 60) @@ -916,12 +1219,31 @@ enum v4l2_mpeg_vidc_video_mpeg2_profile { #define V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 61) +enum v4l2_cid_mpeg_vidc_video_full_range { + V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_ENABLE = 1, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 62) #define V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 63) +#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 64) + +#define V4L2_CID_MPEG_VIDC_VIDEO_LAYER_ID \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 65) + +#define V4L2_CID_MPEG_VIDC_VIDEO_VP9_PROFILE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 66) +enum v4l2_mpeg_vidc_video_vp9_profile { + V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_UNUSED = 0, + V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_P0 = 1, + V4L2_MPEG_VIDC_VIDEO_VP9_PROFILE_P2_10 = 2, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_VP9_LEVEL \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 67) enum v4l2_mpeg_vidc_video_vp9_level { @@ -940,17 +1262,132 @@ enum v4l2_mpeg_vidc_video_vp9_level { V4L2_MPEG_VIDC_VIDEO_VP9_LEVEL_61 = 12, }; +#define V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE_CAPS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 68) +#define V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_CAPS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 69) + +#define V4L2_CID_MPEG_VIDC_VIDEO_MB_ERROR_MAP_REPORTING \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 70) +#define V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 71) + +#define V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_INPUT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 72) + +#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_ASSEMBLY \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 73) +enum v4l2_mpeg_vidc_video_assembly { + V4L2_MPEG_VIDC_FRAME_ASSEMBLY_DISABLE = 0, + V4L2_MPEG_VIDC_FRAME_ASSEMBLY_ENABLE = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE\ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 74) +enum v4l2_mpeg_vidc_video_h263_profile { + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BASELINE = 0, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_H320CODING = 1, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_BACKWARDCOMPATIBLE = 2, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV2 = 3, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_ISWV3 = 4, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHCOMPRESSION = 5, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERNET = 6, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_INTERLACE = 7, + V4L2_MPEG_VIDC_VIDEO_H263_PROFILE_HIGHLATENCY = 8, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+75) +enum v4l2_mpeg_vidc_video_output_order { + V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY = 0, + V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 76) +enum v4l2_mpeg_vidc_video_h264_transform_8x8 { + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0, + V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_QP_MASK \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 77) + +#define V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 96) +#define V4L2_CID_MPEG_VIDEO_MAX_QP_PACKED \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 97) +#define V4L2_CID_MPEG_VIDC_VIDEO_ADAPTIVE_B \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 98) +#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 99) +#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 100) +#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 101) +#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MIN \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 102) +#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MIN \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 103) +#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MIN \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 104) +#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 105) +#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 106) +#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 107) + #define V4L2_CID_MPEG_VIDC_VIDEO_DYN_QP \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 108) +enum v4l2_mpeg_vidc_video_venc_iframesize_type { + V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT, + V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM, + V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE, + V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_8BIT \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 109) #define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR_10BIT \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 110) +#define V4L2_CID_MPEG_VIDC_VIDEO_TME_PROFILE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 111) + +enum v4l2_mpeg_vidc_video_tme_profile { + V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_0 = 0, + V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_1 = 1, + V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_2 = 2, + V4L2_MPEG_VIDC_VIDEO_TME_PROFILE_3 = 3, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_TME_LEVEL \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 112) + +enum v4l2_mpeg_vidc_video_tme_level { + V4L2_MPEG_VIDC_VIDEO_TME_LEVEL_INTEGER = 0, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_TME_PAYLOAD_VERSION \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 113) + #define V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_CUSTOM_MATRIX \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 114) +#define V4L2_CID_MPEG_VIDC_VIDEO_FLIP (V4L2_CID_MPEG_MSM_VIDC_BASE + 115) +enum v4l2_mpeg_vidc_video_flip { + V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_FLIP_HORI = 1, + V4L2_CID_MPEG_VIDC_VIDEO_FLIP_VERT = 2, + V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH = 3, +}; + +/* HDR SEI INFO related control IDs and definitions*/ +#define V4L2_MPEG_VIDC_VENC_HDR_INFO_ENABLED 1 +#define V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED 0 + #define V4L2_CID_MPEG_VIDC_VENC_HDR_INFO \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 116) @@ -1003,9 +1440,166 @@ enum v4l2_mpeg_vidc_video_roi_type { V4L2_CID_MPEG_VIDC_VIDEO_ROI_TYPE_2BYTE = 2, }; +#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 128) +enum v4l2_mpeg_vidc_perf_level { + V4L2_CID_MPEG_VIDC_PERF_LEVEL_NOMINAL = 0, + V4L2_CID_MPEG_VIDC_PERF_LEVEL_PERFORMANCE = 1, + V4L2_CID_MPEG_VIDC_PERF_LEVEL_TURBO = 2, +}; + #define V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_HINT \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 133) +#define V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE + 134) +#define V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF (V4L2_CID_MPEG_MSM_VIDC_BASE + 135) +#define V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE + 136) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 137) + +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_GOB \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 138) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 139) +enum v4l2_mpeg_vidc_video_h264_vui_bitstream_restrict { + V4L2_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT_ENABLED = 1 +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 140) +enum v4l2_mpeg_vidc_video_deinterlace { + V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED = 0, + V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_MPEG4_TIME_RESOLUTION \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 141) + +#define V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_SEQ_HEADER \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 142) + +#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 143) +enum v4l2_mpeg_vidc_video_rate_control_timestamp_mode { + V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_HONOR = 0, + V4L2_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE_IGNORE = 1, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 144) +enum vl42_mpeg_vidc_video_enable_initial_qp { + V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_IFRAME = 0x1, + V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_PFRAME = 0x2, + V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_BFRAME = 0x4, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 145) + +#define V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 146) + +#define V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 147) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 148) + +#define V4L2_CID_MPEG_VIDC_VIDEO_PERF_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 149) +enum v4l2_mpeg_vidc_video_perf_mode { + V4L2_MPEG_VIDC_VIDEO_PERF_MAX_QUALITY = 1, + V4L2_MPEG_VIDC_VIDEO_PERF_POWER_SAVE = 2 +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_MBI_STATISTICS_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 150) + +#define V4L2_CID_MPEG_VIDC_VIDEO_CONFIG_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 151) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_PIC_ORDER_CNT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 152) + +#define V4L2_CID_MPEG_VIDC_VIDEO_SCS_THRESHOLD \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 153) + +#define V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 154) + + +#define V4L2_CID_MPEG_VIDC_VIDEO_SECURE_SCALING_THRESHOLD \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 155) + +#define V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 156) + +#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP (V4L2_CID_MPEG_MSM_VIDC_BASE + 157) +#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP (V4L2_CID_MPEG_MSM_VIDC_BASE + 158) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 159) +enum v4l2_mpeg_vidc_video_h263_level { + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_1_0 = 0, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_2_0 = 1, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_3_0 = 2, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_4_0 = 3, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_4_5 = 4, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_5_0 = 5, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_6_0 = 6, + V4L2_MPEG_VIDC_VIDEO_H263_LEVEL_7_0 = 7, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 160) + + +#define V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT (V4L2_CID_MPEG_MSM_VIDC_BASE+161) + +enum v4l2_mpeg_vidc_video_divx_format_type { + V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4 = 0, + V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_5 = 1, + V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6 = 2, +}; + +#define V4L2_CID_MPEG_VIDC_IMG_GRID_DIMENSION \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+163) + +#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+164) + +#define \ +V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+165) +enum v4l2_mpeg_vidc_video_intra_refresh_mode { + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE = 0, + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC = 1, + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOMMODE = 2, + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_ADAPTIVE = 3, + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE = 4, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_IR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE+166) + +enum v4l2_mpeg_vidc_video_mbi_statistics_mode { + V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_DEFAULT = 0, + V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_1 = 1, + V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_2 = 2, + V4L2_CID_MPEG_VIDC_VIDEO_MBI_MODE_3 = 3, +}; + +enum vl42_mpeg_vidc_video_h264_svc_nal { + V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC_DISABLED = 0, + V4L2_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC_ENABLED = 1, +}; + +enum v4l2_mpeg_vidc_video_h264_vui_timing_info { + V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO_ENABLED = 1 +}; + /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 7d98b88996d9..9a9cd71c9d2f 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -662,7 +662,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */ #define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */ #define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */ +#define V4L2_PIX_FMT_DIVX_311 v4l2_fourcc('D', 'I', 'V', '3') /* DIVX311 */ +#define V4L2_PIX_FMT_DIVX v4l2_fourcc('D', 'I', 'V', 'X') /* DIVX */ #define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */ +#define V4L2_PIX_FMT_HEVC_HYBRID v4l2_fourcc('H', 'V', 'C', 'H') #define V4L2_PIX_FMT_FWHT v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */ #define V4L2_PIX_FMT_TME v4l2_fourcc('T', 'M', 'E', '0') /* TME stream */ #define V4L2_PIX_FMT_CVP v4l2_fourcc('C', 'V', 'P', '0') /* CVP stream */ @@ -1085,7 +1088,23 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_EOS 0x02000000 #define V4L2_BUF_FLAG_READONLY 0x04000000 #define V4L2_BUF_FLAG_PERF_MODE 0x20000000 -#define V4L2_BUF_FLAG_CVPMETADATA_SKIP 0x40000000 +#define V4L2_BUF_FLAG_CVPMETADATA_SKIP 0x40000000 + +#define V4L2_QCOM_BUF_FLAG_EOSEQ 0x00040000 +#define V4L2_QCOM_BUF_TIMESTAMP_INVALID 0x00080000 +#define V4L2_MSM_BUF_FLAG_MBAFF 0x00000200 +#define V4L2_QCOM_BUF_FLAG_DECODEONLY 0x00200000 +#define V4L2_QCOM_BUF_DROP_FRAME 0x00800000 +#define V4L2_MSM_VIDC_BUF_START_CODE_NOT_FOUND 0x08000000 +#define V4L2_MSM_BUF_FLAG_YUV_601_709_CLAMP 0x10000000 +#define V4L2_MSM_BUF_FLAG_DEFER 0x40000000 +#define V4L2_QCOM_BUF_FLAG_IDRFRAME 0x80000000 +#define V4L2_QCOM_BUF_END_OF_SUBFRAME V4L2_BUF_FLAG_END_OF_SUBFRAME +#define V4L2_QCOM_BUF_FLAG_CODECCONFIG V4L2_BUF_FLAG_CODECCONFIG +#define V4L2_QCOM_BUF_INPUT_UNSUPPORTED V4L2_BUF_INPUT_UNSUPPORTED +#define V4L2_QCOM_BUF_FLAG_EOS V4L2_BUF_FLAG_EOS +#define V4L2_QCOM_BUF_FLAG_READONLY V4L2_BUF_FLAG_READONLY +#define V4L2_QCOM_BUF_FLAG_PERF_MODE V4L2_BUF_FLAG_PERF_MODE /** * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor @@ -1994,6 +2013,8 @@ struct v4l2_encoder_cmd { #define V4L2_DEC_CMD_RESUME (3) #define V4L2_CMD_FLUSH (4) #define V4L2_CMD_SESSION_CONTINUE (5) +#define V4L2_DEC_QCOM_CMD_RECONFIG_HINT (6) + /* Flags for V4L2_DEC_CMD_START */ #define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0) @@ -2008,6 +2029,11 @@ struct v4l2_encoder_cmd { #define V4L2_CMD_FLUSH_OUTPUT (1 << 0) #define V4L2_CMD_FLUSH_CAPTURE (1 << 1) +#define V4L2_QCOM_CMD_FLUSH V4L2_CMD_FLUSH +#define V4L2_QCOM_CMD_SESSION_CONTINUE V4L2_CMD_SESSION_CONTINUE +#define V4L2_QCOM_CMD_FLUSH_OUTPUT V4L2_CMD_FLUSH_OUTPUT +#define V4L2_QCOM_CMD_FLUSH_CAPTURE V4L2_CMD_FLUSH_CAPTURE + /* Play format requirements (returned by the driver): */ /* The decoder has no special format requirements */ @@ -2286,6 +2312,10 @@ struct v4l2_streamparm { #define V4L2_EVENT_MOTION_DET 6 #define V4L2_EVENT_PRIVATE_START 0x08000000 +#define V4L2_EVENT_BITDEPTH_FLAG 0x1 +#define V4L2_EVENT_PICSTRUCT_FLAG 0x2 +#define V4L2_EVENT_COLOUR_SPACE_FLAG 0x4 + #define V4L2_EVENT_MSM_VIDC_START (V4L2_EVENT_PRIVATE_START + 0x00001000) #define V4L2_EVENT_MSM_VIDC_FLUSH_DONE (V4L2_EVENT_MSM_VIDC_START + 1) #define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT \ @@ -2293,6 +2323,13 @@ struct v4l2_streamparm { #define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT \ (V4L2_EVENT_MSM_VIDC_START + 3) +/* + * Bitdepth changed insufficient is deprecated now, however retaining + * to prevent changing the values of the other macros after bitdepth + */ +#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_BITDEPTH_CHANGED_INSUFFICIENT \ + (V4L2_EVENT_MSM_VIDC_START + 4) + #define V4L2_EVENT_MSM_VIDC_SYS_ERROR (V4L2_EVENT_MSM_VIDC_START + 5) #define V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE \ (V4L2_EVENT_MSM_VIDC_START + 6) diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h new file mode 100644 index 000000000000..11b6965bcfe4 --- /dev/null +++ b/include/uapi/media/msm_vidc.h @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ +#ifndef __MSM_VIDC_H__ +#define __MSM_VIDC_H__ + +#include + +#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12 0x2 +#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC 0x8002 +#define MSM_VIDC_4x_1 0x1 +#define MSM_VIDC_EXTRADATA_FRAME_QP_ADV 0x1 + + +static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height) +{ + (void)height; + (void)width; + /* + * In the future, calculate the size based on the w/h but just + * hardcode it for now since 16K satisfies all current usecases. + */ + return 16 * 1024; +} + +#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 22) +enum v4l2_mpeg_vidc_video_decoder_multi_stream { + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY = 0, + V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY = 1, +}; + +struct msm_vidc_extradata_header { + unsigned int size; + unsigned int:32; /** Keeping binary compatibility */ + unsigned int:32; /* with firmware and OpenMAX IL **/ + unsigned int type; /* msm_vidc_extradata_type */ + unsigned int data_size; + unsigned char data[1]; +}; + +struct msm_vidc_interlace_payload { + unsigned int format; + unsigned int color_format; +}; + +struct msm_vidc_framerate_payload { + unsigned int frame_rate; +}; + +struct msm_vidc_ts_payload { + unsigned int timestamp_lo; + unsigned int timestamp_hi; +}; + +struct msm_vidc_concealmb_payload { + unsigned int num_mbs; +}; + +struct msm_vidc_recoverysei_payload { + unsigned int flags; +}; + +struct msm_vidc_aspect_ratio_payload { + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int aspect_width; + unsigned int aspect_height; +}; + +struct msm_vidc_mpeg2_seqdisp_payload { + unsigned int video_format; + unsigned int color_descp; + unsigned int color_primaries; + unsigned int transfer_char; + unsigned int matrix_coeffs; + unsigned int disp_width; + unsigned int disp_height; +}; + +struct msm_vidc_vc1_seqdisp_payload { + unsigned int prog_seg_format; + unsigned int uv_sampl_fmt; + unsigned int color_format; + unsigned int color_primaries; + unsigned int transfer_char; + unsigned int matrix_coeffs; + unsigned int aspect_ratio; + unsigned int aspect_horiz; + unsigned int aspect_vert; +}; + +struct msm_vidc_input_crop_payload { + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int left; + unsigned int top; + unsigned int width; + unsigned int height; +}; + +struct msm_vidc_misr_info { + unsigned int misr_dpb_luma; + unsigned int misr_dpb_chroma; + unsigned int misr_opb_luma; + unsigned int misr_opb_chroma; +}; +struct msm_vidc_output_crop_payload { + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int left; + unsigned int top; + unsigned int display_width; + unsigned int display_height; + unsigned int width; + unsigned int height; + unsigned int frame_num; + unsigned int bit_depth_y; + unsigned int bit_depth_c; + struct msm_vidc_misr_info misr_info[2]; +}; + + +struct msm_vidc_digital_zoom_payload { + unsigned int size; + unsigned int version; + unsigned int port_index; + unsigned int zoom_width; + unsigned int zoom_height; +}; + +struct msm_vidc_extradata_index { + unsigned int type; + union { + struct msm_vidc_input_crop_payload input_crop; + struct msm_vidc_digital_zoom_payload digital_zoom; + struct msm_vidc_aspect_ratio_payload aspect_ratio; + }; +}; + +struct msm_vidc_panscan_window { + unsigned int panscan_height_offset; + unsigned int panscan_width_offset; + unsigned int panscan_window_width; + unsigned int panscan_window_height; +}; + +struct msm_vidc_panscan_window_payload { + unsigned int num_panscan_windows; + struct msm_vidc_panscan_window wnd[1]; +}; + +struct msm_vidc_stream_userdata_payload { + unsigned int type; + unsigned int data[1]; +}; + +struct msm_vidc_frame_qp_payload { + unsigned int frame_qp; + unsigned int qp_sum; + unsigned int skip_qp_sum; + unsigned int skip_num_blocks; + unsigned int total_num_blocks; +}; + +struct msm_vidc_frame_bits_info_payload { + unsigned int frame_bits; + unsigned int header_bits; +}; + +struct msm_vidc_s3d_frame_packing_payload { + unsigned int fpa_id; + unsigned int cancel_flag; + unsigned int fpa_type; + unsigned int quin_cunx_flag; + unsigned int content_interprtation_type; + unsigned int spatial_flipping_flag; + unsigned int frame0_flipped_flag; + unsigned int field_views_flag; + unsigned int current_frame_is_frame0_flag; + unsigned int frame0_self_contained_flag; + unsigned int frame1_self_contained_flag; + unsigned int frame0_graid_pos_x; + unsigned int frame0_graid_pos_y; + unsigned int frame1_graid_pos_x; + unsigned int frame1_graid_pos_y; + unsigned int fpa_reserved_byte; + unsigned int fpa_repetition_period; + unsigned int fpa_extension_flag; +}; + +struct msm_vidc_vqzip_sei_payload { + unsigned int size; + unsigned int data[1]; +}; + +struct msm_vidc_ubwc_cr_stats_info { + unsigned int stats_tile_32; + unsigned int stats_tile_64; + unsigned int stats_tile_96; + unsigned int stats_tile_128; + unsigned int stats_tile_160; + unsigned int stats_tile_192; + unsigned int stats_tile_256; +}; + +struct msm_vidc_yuv_stats_payload { + unsigned int frame_qp; + unsigned int texture; + unsigned int luma_in_q16; + unsigned int frame_difference; +}; + +struct msm_vidc_vpx_colorspace_payload { + unsigned int color_space; + unsigned int yuv_range_flag; + unsigned int sumsampling_x; + unsigned int sumsampling_y; +}; + +struct msm_vidc_roi_qp_payload { + int upper_qp_offset; + int lower_qp_offset; + unsigned int b_roi_info; + int mbi_info_size; + unsigned int data[1]; +}; + +struct msm_vidc_mastering_display_colour_sei_payload { + unsigned int nDisplayPrimariesX[3]; + unsigned int nDisplayPrimariesY[3]; + unsigned int nWhitePointX; + unsigned int nWhitePointY; + unsigned int nMaxDisplayMasteringLuminance; + unsigned int nMinDisplayMasteringLuminance; +}; + +struct msm_vidc_content_light_level_sei_payload { + unsigned int nMaxContentLight; + unsigned int nMaxPicAverageLight; +}; + +struct msm_vidc_vui_display_info_payload { + unsigned int video_signal_present_flag; + unsigned int video_format; + unsigned int bit_depth_y; + unsigned int bit_depth_c; + unsigned int video_full_range_flag; + unsigned int color_description_present_flag; + unsigned int color_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + unsigned int chroma_location_info_present_flag; + unsigned int chroma_format_idc; + unsigned int separate_color_plane_flag; + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; +}; + +enum msm_vidc_extradata_type { + MSM_VIDC_EXTRADATA_NONE = 0x00000000, + MSM_VIDC_EXTRADATA_MB_QUANTIZATION = 0x00000001, + MSM_VIDC_EXTRADATA_INTERLACE_VIDEO = 0x00000002, + MSM_VIDC_EXTRADATA_VC1_FRAMEDISP = 0x00000003, + MSM_VIDC_EXTRADATA_VC1_SEQDISP = 0x00000004, + MSM_VIDC_EXTRADATA_TIMESTAMP = 0x00000005, + MSM_VIDC_EXTRADATA_S3D_FRAME_PACKING = 0x00000006, + MSM_VIDC_EXTRADATA_FRAME_RATE = 0x00000007, + MSM_VIDC_EXTRADATA_PANSCAN_WINDOW = 0x00000008, + MSM_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 0x00000009, + MSM_VIDC_EXTRADATA_MPEG2_SEQDISP = 0x0000000D, + MSM_VIDC_EXTRADATA_STREAM_USERDATA = 0x0000000E, + MSM_VIDC_EXTRADATA_FRAME_QP = 0x0000000F, + MSM_VIDC_EXTRADATA_FRAME_BITS_INFO = 0x00000010, + MSM_VIDC_EXTRADATA_VQZIP_SEI = 0x00000011, + MSM_VIDC_EXTRADATA_ROI_QP = 0x00000013, +#define MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO \ + MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO + MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO = 0x00000014, +#define MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI \ + MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI + MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI = 0x00000015, +#define MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \ + MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI + MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 0x00000016, +#define MSM_VIDC_EXTRADATA_PQ_INFO \ + MSM_VIDC_EXTRADATA_PQ_INFO + MSM_VIDC_EXTRADATA_PQ_INFO = 0x00000017, +#define MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI \ + MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI + MSM_VIDC_EXTRADATA_COLOUR_REMAPPING_INFO_SEI = 0x00000018, +#define MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO \ + MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO + MSM_VIDC_EXTRADATA_UBWC_CR_STAT_INFO = 0x00000019, + MSM_VIDC_EXTRADATA_INPUT_CROP = 0x0700000E, +#define MSM_VIDC_EXTRADATA_OUTPUT_CROP \ + MSM_VIDC_EXTRADATA_OUTPUT_CROP + MSM_VIDC_EXTRADATA_OUTPUT_CROP = 0x0700000F, + MSM_VIDC_EXTRADATA_DIGITAL_ZOOM = 0x07000010, + MSM_VIDC_EXTRADATA_MULTISLICE_INFO = 0x7F100000, + MSM_VIDC_EXTRADATA_NUM_CONCEALED_MB = 0x7F100001, + MSM_VIDC_EXTRADATA_INDEX = 0x7F100002, + MSM_VIDC_EXTRADATA_ASPECT_RATIO = 0x7F100003, + MSM_VIDC_EXTRADATA_METADATA_LTR = 0x7F100004, + MSM_VIDC_EXTRADATA_METADATA_FILLER = 0x7FE00002, + MSM_VIDC_EXTRADATA_METADATA_MBI = 0x7F100005, +#define MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO \ + MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO + MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO = 0x7F100006, + MSM_VIDC_EXTRADATA_YUVSTATS_INFO = 0x7F100007, +}; +enum msm_vidc_interlace_type { + MSM_VIDC_INTERLACE_FRAME_PROGRESSIVE = 0x01, + MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST = 0x02, + MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04, + MSM_VIDC_INTERLACE_FRAME_TOPFIELDFIRST = 0x08, + MSM_VIDC_INTERLACE_FRAME_BOTTOMFIELDFIRST = 0x10, +#define MSM_VIDC_INTERLACE_FRAME_MBAFF \ + MSM_VIDC_INTERLACE_FRAME_MBAFF + MSM_VIDC_INTERLACE_FRAME_MBAFF = 0x20, +}; + +/* enum msm_vidc_framepack_type */ +#define MSM_VIDC_FRAMEPACK_CHECKERBOARD 0x00 +#define MSM_VIDC_FRAMEPACK_COLUMN_INTERLEAVE 0x01 +#define MSM_VIDC_FRAMEPACK_ROW_INTERLEAVE 0x02 +#define MSM_VIDC_FRAMEPACK_SIDE_BY_SIDE 0x03 +#define MSM_VIDC_FRAMEPACK_TOP_BOTTOM 0x04 +#define MSM_VIDC_FRAMEPACK_TEMPORAL_INTERLEAVE 0x05 + +enum msm_vidc_recovery_sei { + MSM_VIDC_FRAME_RECONSTRUCTION_INCORRECT = 0x0, + MSM_VIDC_FRAME_RECONSTRUCTION_CORRECT = 0x01, + MSM_VIDC_FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT = 0x02, +}; +enum msm_vidc_userdata_type { + MSM_VIDC_USERDATA_TYPE_FRAME = 0x1, + MSM_VIDC_USERDATA_TYPE_TOP_FIELD = 0x2, + MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD = 0x3, +}; + +/* See colour_primaries of ISO/IEC 14496 for significance */ +enum msm_vidc_h264_color_primaries_values { + MSM_VIDC_RESERVED_1 = 0, + MSM_VIDC_BT709_5 = 1, + MSM_VIDC_UNSPECIFIED = 2, + MSM_VIDC_RESERVED_2 = 3, + MSM_VIDC_BT470_6_M = 4, + MSM_VIDC_BT601_6_625 = 5, + MSM_VIDC_BT470_6_BG = MSM_VIDC_BT601_6_625, + MSM_VIDC_BT601_6_525 = 6, + MSM_VIDC_SMPTE_240M = 7, + MSM_VIDC_GENERIC_FILM = 8, + MSM_VIDC_BT2020 = 9, +}; + +enum msm_vidc_vp9_color_primaries_values { + MSM_VIDC_CS_UNKNOWN, + MSM_VIDC_CS_BT_601, + MSM_VIDC_CS_BT_709, + MSM_VIDC_CS_SMPTE_170, + MSM_VIDC_CS_SMPTE_240, + MSM_VIDC_CS_BT_2020, + MSM_VIDC_CS_RESERVED, + MSM_VIDC_CS_RGB, +}; + +enum msm_vidc_h264_matrix_coeff_values { + MSM_VIDC_MATRIX_RGB = 0, + MSM_VIDC_MATRIX_BT_709_5 = 1, + MSM_VIDC_MATRIX_UNSPECIFIED = 2, + MSM_VIDC_MATRIX_RESERVED = 3, + MSM_VIDC_MATRIX_FCC_47 = 4, + MSM_VIDC_MATRIX_601_6_625 = 5, + MSM_VIDC_MATRIX_BT470_BG = MSM_VIDC_MATRIX_601_6_625, + MSM_VIDC_MATRIX_601_6_525 = 6, + MSM_VIDC_MATRIX_SMPTE_170M = MSM_VIDC_MATRIX_601_6_525, + MSM_VIDC_MATRIX_SMPTE_240M = 7, + MSM_VIDC_MATRIX_Y_CG_CO = 8, + MSM_VIDC_MATRIX_BT_2020 = 9, + MSM_VIDC_MATRIX_BT_2020_CONST = 10, +}; + +enum msm_vidc_h264_transfer_chars_values { + MSM_VIDC_TRANSFER_RESERVED_1 = 0, + MSM_VIDC_TRANSFER_BT709_5 = 1, + MSM_VIDC_TRANSFER_UNSPECIFIED = 2, + MSM_VIDC_TRANSFER_RESERVED_2 = 3, + MSM_VIDC_TRANSFER_BT_470_6_M = 4, + MSM_VIDC_TRANSFER_BT_470_6_BG = 5, + MSM_VIDC_TRANSFER_601_6_625 = 6, + MSM_VIDC_TRANSFER_601_6_525 = MSM_VIDC_TRANSFER_601_6_625, + MSM_VIDC_TRANSFER_SMPTE_240M = 7, + MSM_VIDC_TRANSFER_LINEAR = 8, + MSM_VIDC_TRANSFER_LOG_100_1 = 9, + MSM_VIDC_TRANSFER_LOG_100_SQRT10_1 = 10, + MSM_VIDC_TRANSFER_IEC_61966 = 11, + MSM_VIDC_TRANSFER_BT_1361 = 12, + MSM_VIDC_TRANSFER_SRGB = 13, + MSM_VIDC_TRANSFER_BT_2020_10 = 14, + MSM_VIDC_TRANSFER_BT_2020_12 = 15, +#define MSM_VIDC_TRANSFER_SMPTE_ST2084 \ + MSM_VIDC_TRANSFER_SMPTE_ST2084 + MSM_VIDC_TRANSFER_SMPTE_ST2084 = 16, +#define MSM_VIDC_TRANSFER_SMPTE_ST428_1 \ + MSM_VIDC_TRANSFER_SMPTE_ST428_1 + MSM_VIDC_TRANSFER_SMPTE_ST428_1 = 17, +#define MSM_VIDC_TRANSFER_HLG \ + MSM_VIDC_TRANSFER_HLG + MSM_VIDC_TRANSFER_HLG = 18, +}; + +enum msm_vidc_pixel_depth { + MSM_VIDC_BIT_DEPTH_8, + MSM_VIDC_BIT_DEPTH_10, + MSM_VIDC_BIT_DEPTH_UNSUPPORTED = 0XFFFFFFFF, +}; + +enum msm_vidc_video_format { + MSM_VIDC_COMPONENT, + MSM_VIDC_PAL, + MSM_VIDC_NTSC, + MSM_VIDC_SECAM, + MSM_VIDC_MAC, + MSM_VIDC_UNSPECIFIED_FORMAT, + MSM_VIDC_RESERVED_1_FORMAT, + MSM_VIDC_RESERVED_2_FORMAT, +}; + +enum msm_vidc_color_desc_flag { + MSM_VIDC_COLOR_DESC_NOT_PRESENT, + MSM_VIDC_COLOR_DESC_PRESENT, +}; + +/*enum msm_vidc_pic_struct */ +#define MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED 0x0 +#define MSM_VIDC_PIC_STRUCT_PROGRESSIVE 0x1 +#define MSM_VIDC_PIC_STRUCT_UNKNOWN 0XFFFFFFFF +/*default when layer ID isn't specified*/ +#define MSM_VIDC_ALL_LAYER_ID 0xFF + +#endif