diff options
author | Nicolas Dechesne <nicolas.dechesne@linaro.org> | 2015-06-18 10:26:00 +0200 |
---|---|---|
committer | Nicolas Dechesne <nicolas.dechesne@linaro.org> | 2015-06-18 10:26:09 +0200 |
commit | 39c45dd18669b0b8ace5b5a37b68e64cc3408de6 (patch) | |
tree | e0a5440fd16ff2b7cad9160bea41ad58107dee41 | |
parent | 4531bab9e1aa272905c5361f9219d35d14514eb4 (diff) | |
parent | 866fdf69f5ddae91907a2a88b59d0a816daeddb1 (diff) |
Merge branch 'db410c_gpu2-venus' into release/qcomlt-4.0ubuntu-qcom-dragonboard410c-15.06
Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Conflicts:
arch/arm64/boot/dts/qcom/msm8916.dtsi
68 files changed, 33892 insertions, 21 deletions
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 60fe83b703d4..d92f605d3c54 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -59,19 +59,12 @@ reg = <0x0 0x8b600000 0x0 0x0600000>; }; - secure_mem: secure_region { - compatible = "shared-dma-pool"; - reusable; - alignment = <0 0x400000>; - size = <0 0x7000000>; - }; - venus_qseecom_mem: venus_qseecom_region { compatible = "shared-dma-pool"; alloc-ranges = <0 0x80000000 0 0x10000000>; reusable; alignment = <0 0x400000>; - size = <0 0x1400000>; + size = <0 0x800000>; }; audio_mem: audio_region { @@ -1613,6 +1606,132 @@ qcom,vdd-apc-step-down-limit = <1>; qcom,cpr-cpus = <&CPU0 &CPU1 &CPU2 &CPU3>; }; + + qcom,iommu-domains { + compatible = "qcom,iommu-domains"; + + /* + * non-secure addr pool from 1500 MB to 3548 MB + */ + venus_domain_ns: qcom,iommu-domain1 { + label = "venus_ns"; + qcom,iommu-contexts = <&venus_ns>; + qcom,virtual-addr-pool = <0x5dc00000 0x8f000000>; + }; + + /* + * secure bitstream addr pool from 1200 MB to 1500 MB + */ + venus_domain_sec_bitstream: qcom,iommu-domain2 { + label = "venus_sec_bitstream"; + qcom,iommu-contexts = <&venus_sec_bitstream>; + qcom,virtual-addr-pool = <0x4b000000 0x12c00000>; + qcom,secure-domain; + }; + + /* + * secure pixel addr pool from 616 MB to 1200 MB + */ + venus_domain_sec_pixel: qcom,iommu-domain3 { + label = "venus_sec_pixel"; + qcom,iommu-contexts = <&venus_sec_pixel>; + qcom,virtual-addr-pool = <0x25800000 0x25800000>; + qcom,secure-domain; + }; + + /* + * secure non-pixel addr pool from 16 MB to 616 MB + */ + venus_domain_sec_non_pixel: qcom,iommu-domain4 { + label = "venus_sec_non_pixel"; + qcom,iommu-contexts = <&venus_sec_non_pixel>; + qcom,virtual-addr-pool = <0x1000000 0x24800000>; + qcom,secure-domain; + }; + }; + + qcom,vidc@1d00000 { + compatible = "qcom,msm-vidc"; + reg = <0x01d00000 0xff000>; + interrupts = <0 44 0>; + power-domains = <&gcc VENUS_GDSC>; + clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_AHB_CLK>, + <&gcc GCC_VENUS0_AXI_CLK>; + clock-names = "core_clk", "iface_clk", "bus_clk"; + qcom,clock-configs = <0x1 0x0 0x0>; + qcom,load-freq-tbl = <352800 228570000 0xffffffff>, + <352800 228570000 0x55555555>, + <244800 160000000 0xffffffff>, + <244800 160000000 0x55555555>, + <108000 100000000 0xffffffff>, + <108000 100000000 0x55555555>; + qcom,hfi = "venus"; + qcom,reg-presets = <0xE0020 0x05555556>, + <0xE0024 0x05555556>, + <0x80124 0x00000003>; + qcom,qdss-presets = <0x826000 0x1000>, + <0x827000 0x1000>, + <0x822000 0x1000>, + <0x803000 0x1000>, + <0x9180000 0x1000>, + <0x9181000 0x1000>; + qcom,max-hw-load = <352800>; /* 720p @ 30 + 1080p @ 30 */ + qcom,enable-idle-indicator; + qcom,msm-bus-clients { + qcom,msm-bus-client@0 { + qcom,msm-bus,name = "venc-ddr"; + qcom,msm-bus,num-cases = <6>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <63 512 0 0>, + <63 512 133600 674400>, /* VGA 30 fps */ + <63 512 400900 1079000>, /* VGA 60 fps */ + <63 512 400900 1079000>, /* 720p 30 fps */ + <63 512 908600 1537600>, /* 720p 60 fps */ + <63 512 908600 1537600>; /* 1080p 30 fps */ + qcom,bus-configs = <0x01000414>; + }; + + qcom,msm-bus-client@1 { + qcom,msm-bus,name = "vdec-ddr"; + qcom,msm-bus,num-cases = <6>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <63 512 0 0>, + <63 512 99600 831900>, /* VGA 30 fps */ + <63 512 298900 831900>, /* VGA 60 fps */ + <63 512 298900 831900>, /* 720p 30 fps */ + <63 512 677600 1331000>, /* 720p 60 fps */ + <63 512 677600 1331000>; /* 1080p 30 fps */ + qcom,bus-configs = <0x030fcfff>; + }; + }; + + non_secure_cb { + compatible = "qcom,msm-vidc,context-bank"; + qcom,vidc-domain-phandle = <&venus_domain_ns>; + qcom,vidc-partition-buffer-types = <0xfff>; + }; + + secure_bitstream_cb { + compatible = "qcom,msm-vidc,context-bank"; + qcom,vidc-domain-phandle = <&venus_domain_sec_bitstream>; + qcom,vidc-partition-buffer-types = <0x241>; + }; + + secure_pixel_cb { + compatible = "qcom,msm-vidc,context-bank"; + qcom,vidc-domain-phandle = <&venus_domain_sec_pixel>; + qcom,vidc-partition-buffer-types = <0x106>; + }; + + secure_non_pixel_cb { + compatible = "qcom,msm-vidc,context-bank"; + qcom,vidc-domain-phandle = <&venus_domain_sec_non_pixel>; + qcom,vidc-partition-buffer-types = <0x480>; + }; + }; }; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 08c009487ad4..aa2d497eab51 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -139,6 +139,9 @@ CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_QCOM_RPM=y CONFIG_REGULATOR_QCOM_SPMI=y CONFIG_REGULATOR_RPM_SMD=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MSM_VIDC_V4L2=y CONFIG_DRM=y CONFIG_DRM_I2C_ADV7511=y # CONFIG_DRM_I2C_ADV7511_SLAVE_ENCODER is not set diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 9f080ae0f6fa..842c354ac19f 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -1005,6 +1005,39 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg, return __iommu_map_sg(dev, sg, nents, dir, attrs, true); } +static int iommu_map_range(struct iommu_domain *domain, unsigned int iova, + struct scatterlist *sg, unsigned int len, int opt) +{ + s32 ret = 0; + u32 offset = 0; + u32 start_iova = iova; + + BUG_ON(iova & (~PAGE_MASK)); + + while (offset < len) { + phys_addr_t phys = page_to_phys(sg_page(sg)); + u32 page_len = PAGE_ALIGN(sg->offset + sg->length); + + ret = iommu_map(domain, iova, phys, page_len, opt); + if (ret) + goto fail; + + iova += page_len; + offset += page_len; + if (offset < len) + sg = sg_next(sg); + } + + goto out; + +fail: + /* undo mappings already done in case of error */ + iommu_unmap(domain, start_iova, offset); +out: + + return ret; +} + /** * arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA * @dev: valid struct device pointer @@ -1020,7 +1053,29 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg, int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) { - return __iommu_map_sg(dev, sg, nents, dir, attrs, false); + struct scatterlist *s; + int ret, i; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + unsigned int iova, total_length = 0, current_offset = 0; + int prot = __dma_direction_to_prot(dir); + + for_each_sg(sg, s, nents, i) + total_length += s->length; + + iova = __alloc_iova(mapping, total_length); + ret = iommu_map_range(mapping->domain, iova, sg, total_length, prot); + if (ret) { + __free_iova(mapping, iova, total_length); + return 0; + } + + for_each_sg(sg, s, nents, i) { + s->dma_address = iova + current_offset; + s->dma_length = total_length - current_offset; + current_offset += s->length; + } + + return nents; } static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg, @@ -1070,7 +1125,15 @@ void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) { - __iommu_unmap_sg(dev, sg, nents, dir, attrs, false); + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + unsigned int total_length = sg_dma_len(sg); + unsigned int iova = sg_dma_address(sg); + + total_length = PAGE_ALIGN((iova & ~PAGE_MASK) + total_length); + iova &= PAGE_MASK; + + iommu_unmap(mapping->domain, iova, total_length); + __free_iova(mapping, iova, total_length); } /** diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 605ec730c97b..dc1768a4f568 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -3015,6 +3015,7 @@ static struct gdsc venus_gdsc = { .pd = { .name = "venus", }, + .con_ids = { NULL }, }; static struct gdsc mdss_gdsc = { @@ -3022,6 +3023,7 @@ static struct gdsc mdss_gdsc = { .pd = { .name = "mdss", }, + .con_ids = { NULL }, }; static struct gdsc jpeg_gdsc = { @@ -3029,6 +3031,7 @@ static struct gdsc jpeg_gdsc = { .pd = { .name = "jpeg", }, + .con_ids = { NULL }, }; static struct gdsc vfe_gdsc = { @@ -3036,6 +3039,7 @@ static struct gdsc vfe_gdsc = { .pd = { .name = "vfe", }, + .con_ids = { NULL }, }; static struct gdsc oxili_gdsc = { @@ -3044,6 +3048,7 @@ static struct gdsc oxili_gdsc = { .name = "oxili", }, .root_con_id = "gfx3d_clk_src", + .con_ids = { NULL }, }; static struct clk_regmap *gcc_msm8916_clocks[] = { diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 3361082b57c9..750db6a545c6 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -742,3 +742,51 @@ int __qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) return 0; } + +#define TZBSP_VIDEO_SET_STATE 0xa +int __qcom_scm_set_video_state(u32 state, u32 spare) +{ + struct { + u32 state; + u32 spare; + } req; + int scm_ret = 0; + int ret; + + req.state = state; + req.spare = spare; + + ret = qcom_scm_call(SCM_SVC_BOOT, TZBSP_VIDEO_SET_STATE, &req, + sizeof(req), &scm_ret, sizeof(scm_ret)); + if (ret || scm_ret) + return ret ? ret : -EINVAL; + + return 0; +} + +#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8 + +int __qcom_scm_mem_protect_video_var(u32 start, u32 size, u32 nonpixel_start, + u32 nonpixel_size) +{ + struct { + u32 cp_start; + u32 cp_size; + u32 cp_nonpixel_start; + u32 cp_nonpixel_size; + } req; + int ret, scm_ret; + + req.cp_start = start; + req.cp_size = size; + req.cp_nonpixel_start = nonpixel_start; + req.cp_nonpixel_size = nonpixel_size; + + ret = qcom_scm_call(SCM_SVC_MP, TZBSP_MEM_PROTECT_VIDEO_VAR, &req, + sizeof(req), &scm_ret, sizeof(scm_ret)); + + if (ret || scm_ret) + return ret ? ret : -EINVAL; + + return 0; +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index e13326f6fd9b..7ee5cecb2032 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -688,6 +688,50 @@ int __qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) return 0; } +#define TZBSP_VIDEO_SET_STATE 0xa +int __qcom_scm_set_video_state(u32 state, u32 spare) +{ + struct qcom_scm_desc desc = {0}; + int ret, scm_ret; + + desc.args[0] = state; + desc.args[1] = spare; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call(SCM_SVC_BOOT, TZBSP_VIDEO_SET_STATE, &desc); + + scm_ret = desc.ret[0]; + + if (ret || scm_ret) + return ret ? ret : -EINVAL; + + return 0; +} + +#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8 + +int __qcom_scm_mem_protect_video_var(u32 start, u32 size, u32 nonpixel_start, + u32 nonpixel_size) +{ + struct qcom_scm_desc desc = {0}; + int ret, scm_ret; + + desc.args[0] = start; + desc.args[1] = size; + desc.args[2] = nonpixel_start; + desc.args[3] = nonpixel_size; + desc.arginfo = QCOM_SCM_ARGS(4); + + ret = qcom_scm_call(SCM_SVC_MP, TZBSP_MEM_PROTECT_VIDEO_VAR, &desc); + + scm_ret = desc.ret[0]; + + if (ret || scm_ret) + return ret ? ret : -EINVAL; + + return 0; +} + #define QCOM_SCM_SVC_INFO 0x6 static int __init qcom_scm_init(void) { diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 59625416a2e2..c5cac7f18aaa 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -145,3 +145,18 @@ int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) return __qcom_scm_restore_sec_cfg(device_id, spare); } EXPORT_SYMBOL(qcom_scm_restore_sec_cfg); + +int qcom_scm_set_video_state(u32 state, u32 spare) +{ + return __qcom_scm_set_video_state(state, spare); +} +EXPORT_SYMBOL(qcom_scm_set_video_state); + +int qcom_scm_mem_protect_video_var(u32 start, u32 size, + u32 nonpixel_start, + u32 nonpixel_size) +{ + return __qcom_scm_mem_protect_video_var(start, size, nonpixel_start, + nonpixel_size); +} +EXPORT_SYMBOL(qcom_scm_mem_protect_video_var); diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index dbc74cc9cca6..927e00c5084a 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -34,6 +34,7 @@ enum scm_cmd { PAS_SHUTDOWN_CMD, }; +#define SCM_SVC_BOOT 0x1 #define SCM_SVC_PIL 0x2 #define SCM_SVC_INFO 0x6 @@ -59,4 +60,8 @@ extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); extern int __qcom_scm_get_feat_version(u32 feat); extern int __qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); +extern int __qcom_scm_set_video_state(u32 state, u32 spare); +extern int __qcom_scm_mem_protect_video_var(u32 start, u32 size, + u32 nonpixel_start, + u32 nonpixel_size); #endif diff --git a/drivers/iommu/qcom/msm_iommu-v1.c b/drivers/iommu/qcom/msm_iommu-v1.c index e3676229a9eb..43092d7e9778 100644 --- a/drivers/iommu/qcom/msm_iommu-v1.c +++ b/drivers/iommu/qcom/msm_iommu-v1.c @@ -996,7 +996,7 @@ static size_t msm_iommu_map_sg(struct iommu_domain *domain, unsigned long iova, } for_each_sg(sg, tmp, nents, i) - len += sg->length; + len += tmp->length; ret = msm_iommu_pagetable_map_range(&priv->pt, iova, sg, len, prot); if (ret) diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c index 2709b83d57b1..da48ad7745f7 100644 --- a/drivers/media/pci/saa7134/saa7134-ts.c +++ b/drivers/media/pci/saa7134/saa7134-ts.c @@ -212,8 +212,8 @@ int saa7134_ts_init1(struct saa7134_dev *dev) /* sanitycheck insmod options */ if (tsbufs < 2) tsbufs = 2; - if (tsbufs > VIDEO_MAX_FRAME) - tsbufs = VIDEO_MAX_FRAME; + if (tsbufs > VB2_MAX_FRAME) + tsbufs = VB2_MAX_FRAME; if (ts_nr_packets < 4) ts_nr_packets = 4; if (ts_nr_packets > 312) diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c index 5306e549e526..2a3397792be9 100644 --- a/drivers/media/pci/saa7134/saa7134-vbi.c +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -189,8 +189,8 @@ int saa7134_vbi_init1(struct saa7134_dev *dev) if (vbibufs < 2) vbibufs = 2; - if (vbibufs > VIDEO_MAX_FRAME) - vbibufs = VIDEO_MAX_FRAME; + if (vbibufs > VB2_MAX_FRAME) + vbibufs = VB2_MAX_FRAME; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 99d09a7566d3..c08c8558bcb4 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2011,7 +2011,7 @@ int saa7134_video_init1(struct saa7134_dev *dev) int ret; /* sanitycheck insmod options */ - if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) + if (gbuffers < 2 || gbuffers > VB2_MAX_FRAME) gbuffers = 2; if (gbufsize > gbufsize_max) gbufsize = gbufsize_max; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index d9b872b9285a..9d88dd1e8421 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -268,3 +268,5 @@ config VIDEO_VIM2M This is a virtual test device for the memory-to-memory driver framework. endif #V4L_TEST_DRIVERS + +source "drivers/media/platform/msm/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 3ec154742083..ab60119bfe52 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -49,3 +49,5 @@ obj-y += omap/ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ ccflags-y += -I$(srctree)/drivers/media/i2c + +obj-y += msm/ diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig new file mode 100644 index 000000000000..4f55c85d6025 --- /dev/null +++ b/drivers/media/platform/msm/Kconfig @@ -0,0 +1,7 @@ +# +# MSM camera configuration +# + +comment "Qualcomm MSM Camera And Video" + +source "drivers/media/platform/msm/vidc/Kconfig" diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile new file mode 100644 index 000000000000..ef7d98790a8e --- /dev/null +++ b/drivers/media/platform/msm/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the QCOM spcific video device drivers +# based on V4L2. +# + +obj-$(CONFIG_MSM_VIDC_V4L2) += vidc/ diff --git a/drivers/media/platform/msm/vidc/Kconfig b/drivers/media/platform/msm/vidc/Kconfig new file mode 100644 index 000000000000..3be4cf1b60e7 --- /dev/null +++ b/drivers/media/platform/msm/vidc/Kconfig @@ -0,0 +1,11 @@ +# +# VIDEO CORE +# + +menuconfig MSM_VIDC_V4L2 + bool "Qualcomm MSM V4L2 based video driver" + depends on ARCH_QCOM && VIDEO_V4L2 + select VIDEOBUF2_CORE + select VIDEOBUF2_DMA_CONTIG + +source "drivers/media/platform/msm/vidc/vmem/Kconfig" diff --git a/drivers/media/platform/msm/vidc/Makefile b/drivers/media/platform/msm/vidc/Makefile new file mode 100644 index 000000000000..4dd6cf66861b --- /dev/null +++ b/drivers/media/platform/msm/vidc/Makefile @@ -0,0 +1,17 @@ +obj-$(CONFIG_MSM_VIDC_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 \ + q6_hfi.o \ + venus_boot.o \ + msm_vidc_dcvs.o \ + +obj-$(CONFIG_MSM_VIDC_VMEM) += vmem/ diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c new file mode 100644 index 000000000000..63c5627be971 --- /dev/null +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -0,0 +1,2048 @@ +/* Copyright (c) 2012-2015, 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 <linux/errno.h> +#include <linux/log2.h> +#include <linux/hash.h> +#include <soc/qcom/ocmem.h> +#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 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, + /* 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, +}; + +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 && 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]; + default: + return -ENOTSUPP; + } +} + +static inline 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; +} + +static inline u32 get_hfi_codec(enum hal_video_codec hal_codec) +{ + u32 hfi_codec; + 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_HEVC_HYBRID: + hfi_codec = HFI_VIDEO_CODEC_HEVC_HYBRID; + break; + default: + dprintk(VIDC_ERR, "Invalid codec %#x\n", hal_codec); + hfi_codec = 0; + break; + } + return hfi_codec; +} + +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_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 = session_domain; + pkt->session_codec = 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; + + 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: + 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_FRAME_QP: + ret = HFI_PROPERTY_PARAM_VDEC_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; + 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; + 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_OUTPUT2: + pkt->flush_type = HFI_FLUSH_OUTPUT2; + 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_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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_REALTIME; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_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_BUFFER_SIZE_ACTUAL: + { + struct hfi_buffer_size_actual *hfi; + struct hal_buffer_size_actual *prop = + (struct hal_buffer_size_actual *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; + + hfi = (struct hfi_buffer_size_actual *) + &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_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL: + { + struct hfi_buffer_display_hold_count_actual *hfi; + struct hal_buffer_display_hold_count_actual *prop = + (struct hal_buffer_display_hold_count_actual *) pdata; + u32 buffer_type; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL; + + hfi = (struct hfi_buffer_display_hold_count_actual *) + &pkt->rg_property_data[1]; + hfi->hold_count = prop->hold_count; + + 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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_enable *) pdata)->enable; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_enable *) pdata)->enable; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_enable *) pdata)->enable; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VDEC_SYNC_FRAME_DECODE: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_enable *) pdata)->enable; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_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; + default: + dprintk(VIDC_ERR, + "Invalid Rate control setting: %p\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_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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VPE_DEINTERLACE; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hal_enable *) pdata)->enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_GENERATE_AUDNAL: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_enable *) pdata)->enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC: + { + struct hfi_enable *hfi; + struct hal_h264_vui_bitstream_restrc *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = hal->enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY: + { + struct hfi_enable *hfi; + struct hal_preserve_text_quality *hal = pdata; + + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = hal->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 hfi_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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP; + hfi = (struct hfi_enable *)&pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_enable *)pdata)->enable; + pkt->size += sizeof(u32) * 2; + 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: + { + struct hfi_enable *hfi; + struct hal_enable *err_res = pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE; + hfi = (struct hfi_enable *)&pkt->rg_property_data[1]; + hfi->enable = err_res->enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_PARAM_VENC_H264_NAL_SVC_EXT: + { + struct hfi_enable *hfi; + struct hal_enable *svc_nal = pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT; + hfi = (struct hfi_enable *)&pkt->rg_property_data[1]; + hfi->enable = svc_nal->enable; + pkt->size += sizeof(u32) + sizeof(struct hfi_enable); + break; + } + case HAL_CONFIG_VENC_PERF_MODE: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_PERF_MODE; + pkt->rg_property_data[1] = *(u32 *)pdata; + 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: + { + struct hfi_enable *hfi; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2; + hfi = (struct hfi_enable *) &pkt->rg_property_data[1]; + hfi->enable = ((struct hfi_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; + } + /* 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_VENC_LOW_LATENCY: + 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: %p\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 :%p\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 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_3_x_ops(void) +{ + static struct hfi_packetization_ops hfi_venus_3_x; + + hfi_venus_3_x = hfi_default; + + /* Override new HFI functions for HFI_PACKETIZATION_3XX here. */ + + return &hfi_venus_3_x; +} + +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_3_x_ops(); + } + + return NULL; +} diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.h b/drivers/media/platform/msm/vidc/hfi_packetization.h new file mode 100644 index 000000000000..020cc046d904 --- /dev/null +++ b/drivers/media/platform/msm/vidc/hfi_packetization.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2012-2014, 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 <linux/types.h> +#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); +}; + +struct hfi_packetization_ops *hfi_get_pkt_ops_handle( + enum hfi_packetization_type); +#endif diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c new file mode 100644 index 000000000000..a3dabd87773e --- /dev/null +++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c @@ -0,0 +1,1475 @@ +/* Copyright (c) 2012-2015, 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 <linux/slab.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/hash.h> +#include <soc/qcom/smem.h> +#include "vidc_hfi_helper.h" +#include "vidc_hfi_io.h" +#include "msm_vidc_debug.h" +#include "vidc_hfi.h" + +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; +} + +struct hal_session *hfi_process_get_session( + struct list_head *sessions, u32 session_id) +{ + struct hal_session *session = NULL; + bool found_session = false; + + list_for_each_entry(session, sessions, list) { + if (hash32_ptr(session) == session_id) { + found_session = true; + break; + } + } + return found_session ? session : NULL; +} + +static void hfi_process_sess_evt_seq_changed( + msm_vidc_callback callback, u32 device_id, + struct hal_session *session, + struct hfi_msg_event_notify_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct msm_vidc_cb_event event_notify = {0}; + int num_properties_changed; + struct hfi_frame_size *frame_sz; + struct hfi_profile_level *profile_level; + u8 *data_ptr; + int prop_id; + + if (sizeof(struct hfi_msg_event_notify_packet) + > pkt->size) { + dprintk(VIDC_ERR, + "hal_process_session_init_done: bad_pkt_size\n"); + return; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.size = sizeof(struct msm_vidc_cb_event); + 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]; + do { + prop_id = (int) *((u32 *)data_ptr); + switch (prop_id) { + case HFI_PROPERTY_PARAM_FRAME_SIZE: + 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); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: + 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); + break; + default: + dprintk(VIDC_ERR, + "%s cmd: %#x not supported\n", + __func__, prop_id); + break; + } + num_properties_changed--; + } while (num_properties_changed > 0); + } + cmd_done.data = &event_notify; + callback(VIDC_EVENT_CHANGE, &cmd_done); +} + +static void hfi_process_evt_release_buffer_ref( + msm_vidc_callback callback, u32 device_id, + struct hal_session *session, + struct hfi_msg_event_notify_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + 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; + } + + data = (struct hfi_msg_release_buffer_ref_event_packet *) + pkt->rg_ext_event_data; + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.size = sizeof(struct msm_vidc_cb_event); + + 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; + cmd_done.data = &event_notify; + callback(VIDC_EVENT_CHANGE, &cmd_done); +} + +static void hfi_process_sys_error( + msm_vidc_callback callback, u32 device_id) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + cmd_done.device_id = device_id; + callback(SYS_ERROR, &cmd_done); +} +static void hfi_process_session_error( + msm_vidc_callback callback, u32 device_id, + struct hal_session *session, + struct hfi_msg_event_notify_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + cmd_done.device_id = device_id; + cmd_done.session_id = session->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"); + break; + default: + dprintk(VIDC_ERR, "HFI_EVENT_SESSION_ERROR\n"); + callback(SESSION_ERROR, &cmd_done); + break; + } +} +static void hfi_process_event_notify(msm_vidc_callback callback, u32 device_id, + struct hal_session *session, + struct hfi_msg_event_notify_packet *pkt) +{ + dprintk(VIDC_DBG, "Received: EVENT_NOTIFY\n"); + + if (!callback || !pkt || + pkt->size < sizeof(struct hfi_msg_event_notify_packet)) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return; + } + + 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); + hfi_process_sys_error(callback, device_id); + break; + case HFI_EVENT_SESSION_PROPERTY_CHANGED: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED[%p]\n", + session); + break; + } + + if (!session) { + dprintk(VIDC_ERR, "%s Got invalid session id\n", __func__); + return; + } + + switch (pkt->event_id) { + case HFI_EVENT_SESSION_ERROR: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR[%p]\n", session); + hfi_process_session_error(callback, device_id, session, pkt); + break; + case HFI_EVENT_SESSION_SEQUENCE_CHANGED: + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED[%p]\n", + session); + hfi_process_sess_evt_seq_changed(callback, device_id, + session, pkt); + break; + case HFI_EVENT_RELEASE_BUFFER_REFERENCE: + dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE[%p]\n", + session); + hfi_process_evt_release_buffer_ref(callback, device_id, + session, pkt); + break; + default: + dprintk(VIDC_WARN, + "hal_process_event_notify: unknown_event_id[%p]\n", + session); + break; + } +} +static void hfi_process_sys_init_done( + msm_vidc_callback callback, u32 device_id, + struct hfi_msg_sys_init_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct vidc_hal_sys_init_done sys_init_done = {0}; + u32 rem_bytes, bytes_read = 0, num_properties; + u8 *data_ptr; + int prop_id; + 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, + "hal_process_sys_init_done: bad_pkt_size: %d\n", + pkt->size); + return; + } + + status = hfi_map_err_status(pkt->error_type); + + if (!status) { + if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hal_process_sys_init_done: no_properties\n"); + status = VIDC_ERR_FAIL; + goto err_no_prop; + } + + rem_bytes = pkt->size - sizeof(struct + hfi_msg_sys_init_done_packet) + sizeof(u32); + + if (!rem_bytes) { + dprintk(VIDC_ERR, + "hal_process_sys_init_done: missing_prop_info\n"); + status = VIDC_ERR_FAIL; + goto err_no_prop; + } + + data_ptr = (u8 *) &pkt->rg_property_data[0]; + num_properties = pkt->num_properties; + + while (num_properties && rem_bytes >= sizeof(u32)) { + prop_id = *((u32 *)data_ptr); + data_ptr = data_ptr + 4; + + switch (prop_id) { + case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: + { + struct hfi_codec_supported *prop = + (struct hfi_codec_supported *) data_ptr; + if (rem_bytes < sizeof(struct + hfi_codec_supported)) { + status = VIDC_ERR_BAD_PARAM; + break; + } + sys_init_done.dec_codec_supported = + prop->decoder_codec_supported; + sys_init_done.enc_codec_supported = + prop->encoder_codec_supported; + break; + } + default: + dprintk(VIDC_ERR, + "hal_process_sys_init_done: bad_prop_id\n"); + status = VIDC_ERR_BAD_PARAM; + break; + } + if (!status) { + rem_bytes -= bytes_read; + data_ptr += bytes_read; + num_properties--; + } + } + } +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); + cmd_done.data = (void *) &sys_init_done; + callback(SYS_INIT_DONE, &cmd_done); +} + +static void hfi_process_sys_rel_resource_done( + msm_vidc_callback callback, u32 device_id, + struct hfi_msg_sys_release_resource_done_packet *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; + } + 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; + cmd_done.data = NULL; + callback(RELEASE_RESOURCE_DONE, &cmd_done); +} + +static inline void copy_cap_prop( + struct hfi_capability_supported *in, + struct vidc_hal_session_init_done *sess_init_done) +{ + struct hal_capability_supported *out = NULL; + if (!in || !sess_init_done) { + dprintk(VIDC_ERR, "%s Invalid input parameter\n", + __func__); + return; + } + switch (in->capability_type) { + case HFI_CAPABILITY_FRAME_WIDTH: + out = &sess_init_done->width; + break; + + case HFI_CAPABILITY_FRAME_HEIGHT: + out = &sess_init_done->height; + break; + + case HFI_CAPABILITY_MBS_PER_FRAME: + out = &sess_init_done->mbs_per_frame; + break; + + case HFI_CAPABILITY_MBS_PER_SECOND: + out = &sess_init_done->mbs_per_sec; + break; + + case HFI_CAPABILITY_FRAMERATE: + out = &sess_init_done->frame_rate; + break; + + case HFI_CAPABILITY_SCALE_X: + out = &sess_init_done->scale_x; + break; + + case HFI_CAPABILITY_SCALE_Y: + out = &sess_init_done->scale_y; + break; + + case HFI_CAPABILITY_BITRATE: + out = &sess_init_done->bitrate; + break; + + case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: + out = &sess_init_done->hier_p; + break; + + case HFI_CAPABILITY_ENC_LTR_COUNT: + out = &sess_init_done->ltr_count; + break; + + case HFI_CAPABILITY_CP_OUTPUT2_THRESH: + out = &sess_init_done->secure_output2_threshold; + break; + } + + if (out) { + out->min = in->min; + out->max = in->max; + out->step_size = in->step_size; + } +} + +enum vidc_status hfi_process_sess_init_done_prop_read( + struct hfi_msg_sys_session_init_done_packet *pkt, + struct vidc_hal_session_init_done *sess_init_done) +{ + u32 rem_bytes, num_properties; + u8 *data_ptr; + enum vidc_status status = VIDC_ERR_NONE; + u32 prop_id, next_offset = 0; + u32 prop_count = 0; + + rem_bytes = pkt->size - sizeof(struct + hfi_msg_sys_session_init_done_packet) + sizeof(u32); + + if (!rem_bytes) { + dprintk(VIDC_ERR, + "hfi_msg_sys_session_init_done: missing_prop_info\n"); + return VIDC_ERR_FAIL; + } + + status = hfi_map_err_status(pkt->error_type); + if (status) + return status; + + data_ptr = (u8 *) &pkt->rg_property_data[0]; + num_properties = pkt->num_properties; + + 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_CAPABILITY_SUPPORTED: + { + struct hfi_capability_supported_info *prop = + (struct hfi_capability_supported_info *) + (data_ptr + next_offset); + u32 num_capabilities; + struct hfi_capability_supported *cap_ptr; + + if ((rem_bytes - next_offset) < sizeof(*cap_ptr)) { + status = VIDC_ERR_BAD_PARAM; + break; + } + + num_capabilities = prop->num_capabilities; + cap_ptr = &prop->rg_data[0]; + next_offset += sizeof(u32); + + while (num_capabilities && + (rem_bytes - next_offset) >= sizeof(u32)) { + copy_cap_prop(cap_ptr, sess_init_done); + cap_ptr++; + next_offset += sizeof(*cap_ptr); + num_capabilities--; + } + 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; + + if ((rem_bytes - next_offset) < sizeof(*prop)) { + status = VIDC_ERR_BAD_PARAM; + break; + } + num_format_entries = prop->format_entries; + next_offset = sizeof(*prop) - sizeof(u32); + 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; + + if ((rem_bytes - next_offset) < + sizeof(*plane_info)) { + status = VIDC_ERR_BAD_PARAM; + break; + } + bytes_to_skip = sizeof(*plane_info) - + sizeof(struct + hfi_uncompressed_plane_constraints) + + plane_info->num_planes * + sizeof(struct + hfi_uncompressed_plane_constraints); + + 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); + + next_offset += sizeof(*prop) - sizeof(u32) + + prop->num_properties * sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + { + char *ptr = NULL; + int count = 0; + struct hfi_profile_level *prop_level; + struct hfi_profile_level_supported *prop = + (struct hfi_profile_level_supported *) + (data_ptr + next_offset); + ptr = (char *) &prop->rg_profile_level[0]; + dprintk(VIDC_DBG, "prop->profile_count: %d\n", + prop->profile_count); + prop_count = prop->profile_count; + if (prop_count > MAX_PROFILE_COUNT) { + prop_count = MAX_PROFILE_COUNT; + dprintk(VIDC_WARN, + "prop count exceeds max profile count\n"); + } + while (prop_count) { + ptr++; + prop_level = (struct hfi_profile_level *) ptr; + sess_init_done-> + profile_level.profile_level[count].profile + = prop_level->profile; + sess_init_done-> + profile_level.profile_level[count].level + = prop_level->level; + prop_count--; + count++; + ptr += + sizeof(struct hfi_profile_level) / sizeof(u32); + } + next_offset += sizeof(*prop) - + sizeof(struct hfi_profile_level) + + prop->profile_count * + sizeof(struct hfi_profile_level); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: + { + next_offset += + sizeof(struct hfi_nal_stream_format_supported); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: + { + next_offset += sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: + { + next_offset += sizeof(u32); + num_properties--; + break; + } + case HFI_PROPERTY_PARAM_VENC_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); + int i; + if (prop->buffer_type == HFI_BUFFER_OUTPUT || + prop->buffer_type == HFI_BUFFER_OUTPUT2) { + sess_init_done->alloc_mode_out = 0; + for (i = 0; i < prop->num_entries; i++) { + switch (prop->rg_data[i]) { + case HFI_BUFFER_MODE_STATIC: + sess_init_done->alloc_mode_out + |= HAL_BUFFER_MODE_STATIC; + break; + case HFI_BUFFER_MODE_DYNAMIC: + sess_init_done->alloc_mode_out + |= HAL_BUFFER_MODE_DYNAMIC; + break; + } + if (i >= 32) { + dprintk(VIDC_ERR, + "%s - num_entries: %d from f/w seems suspect\n", + __func__, prop->num_entries); + break; + } + } + } + next_offset += sizeof(*prop) - + sizeof(u32) + prop->num_entries * sizeof(u32); + num_properties--; + break; + } + default: + dprintk(VIDC_DBG, + "%s default case - %#x\n", __func__, prop_id); + } + rem_bytes -= next_offset; + data_ptr += next_offset; + } + return status; +} + +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: %p\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; + + if (!prop) { + dprintk(VIDC_ERR, + "hal_process_sess_get_prop_buf_req: bad_prop: %p\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) { + if (hfi_buf_req->buffer_size && + hfi_buf_req->buffer_count_min > hfi_buf_req-> + buffer_count_actual) + dprintk(VIDC_WARN, + "hal_process_sess_get_prop_buf_req:" + "bad_buf_req\n"); + + 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 void hfi_process_session_prop_info(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_property_info_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct hfi_profile_level profile_level = {0}; + struct buffer_requirements buff_req; + + memset(&buff_req, 0, sizeof(struct buffer_requirements)); + dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%p]\n", session); + + if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) { + dprintk(VIDC_ERR, + "hal_process_session_prop_info: bad_pkt_size\n"); + return; + } + + if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hal_process_session_prop_info: no_properties\n"); + return; + } + + 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 = session->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.data = &buff_req; + cmd_done.size = sizeof(struct buffer_requirements); + callback(SESSION_PROPERTY_INFO, &cmd_done); + 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 = session->session_id; + cmd_done.status = VIDC_ERR_NONE; + cmd_done.data = &profile_level; + cmd_done.size = sizeof(struct hal_profile_level); + callback(SESSION_PROPERTY_INFO, &cmd_done); + break; + default: + dprintk(VIDC_DBG, + "hal_process_session_prop_info: unknown_prop_id: %x\n", + pkt->rg_property_data[0]); + break; + } +} + +static void hfi_process_session_init_done( + msm_vidc_callback callback, u32 device_id, + struct hal_session *session, + struct hfi_msg_sys_session_init_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + struct vidc_hal_session_init_done session_init_done; + + if (!session) { + dprintk(VIDC_ERR, "%s - invalid session\n", __func__); + return; + } + + memset(&session_init_done, 0, sizeof(struct + vidc_hal_session_init_done)); + dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE[%p]\n", session); + + 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; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = &session_init_done; + if (!cmd_done.status) { + cmd_done.status = hfi_process_sess_init_done_prop_read( + pkt, &session_init_done); + } else { + dprintk(VIDC_WARN, + "Sess init failed: %p, %p\n", + session->session_id, session); + } + cmd_done.size = sizeof(struct vidc_hal_session_init_done); + callback(SESSION_INIT_DONE, &cmd_done); +} + +static void hfi_process_session_load_res_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_load_resources_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE[%p]\n", + session); + + 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; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = NULL; + cmd_done.size = 0; + callback(SESSION_LOAD_RESOURCE_DONE, &cmd_done); +} + +static void hfi_process_session_flush_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_flush_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE[%p]\n", session); + + 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; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = (void *)(unsigned long)pkt->flush_type; + cmd_done.size = sizeof(u32); + callback(SESSION_FLUSH_DONE, &cmd_done); +} + +static void hfi_process_session_etb_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_empty_buffer_done_packet *pkt) +{ + struct msm_vidc_cb_data_done data_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%p]\n", session); + + if (!pkt || pkt->size < + sizeof(struct hfi_msg_session_empty_buffer_done_packet)) { + dprintk(VIDC_ERR, + "hal_process_session_etb_done: bad_pkt_size\n"); + return; + } + + data_done.device_id = device_id; + data_done.session_id = session->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 = + (ion_phys_addr_t)pkt->packet_buffer; + data_done.input_done.extra_data_buffer = + (ion_phys_addr_t)pkt->extra_data_buffer; + data_done.input_done.status = + hfi_map_err_status(pkt->error_type); + + trace_msm_v4l2_vidc_buffer_event_end("ETB", + (u32)pkt->packet_buffer, -1, -1, + pkt->filled_len, pkt->offset); + callback(SESSION_ETB_DONE, &data_done); +} + +static void hfi_process_session_ftb_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + void *msg_hdr) +{ + struct msm_vidc_cb_data_done data_done = {0}; + bool is_decoder = session->is_decoder; + + if (!msg_hdr) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return; + } + + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%p]\n", session); + + if (!is_decoder) { + struct hfi_msg_session_fill_buffer_done_compressed_packet *pkt = + (struct hfi_msg_session_fill_buffer_done_compressed_packet *) + msg_hdr; + 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; + } 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 = session->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 = + (ion_phys_addr_t)pkt->packet_buffer; + data_done.output_done.extra_data_buffer = + (ion_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; + 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; + } + + data_done.device_id = device_id; + data_done.session_id = session->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); + + callback(SESSION_FTB_DONE, &data_done); +} + +static void hfi_process_session_start_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_start_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE[%p]\n", session); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_start_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = NULL; + cmd_done.size = 0; + callback(SESSION_START_DONE, &cmd_done); +} + +static void hfi_process_session_stop_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_stop_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE[%p]\n", session); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_stop_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = NULL; + cmd_done.size = 0; + callback(SESSION_STOP_DONE, &cmd_done); +} + +static void hfi_process_session_rel_res_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_release_resources_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE[%p]\n", + session); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_session_release_resources_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + return; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = NULL; + cmd_done.size = 0; + callback(SESSION_RELEASE_RESOURCE_DONE, &cmd_done); +} + +static void hfi_process_session_rel_buf_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_release_buffers_done_packet *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; + } + dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_BUFFER_DONE[%p]\n", + session); + + cmd_done.device_id = device_id; + cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done); + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + if (pkt->rg_buffer_info) { + cmd_done.data = (void *) &pkt->rg_buffer_info; + cmd_done.size = sizeof(struct hfi_buffer_info); + } else { + dprintk(VIDC_ERR, "invalid payload in rel_buff_done\n"); + } + callback(SESSION_RELEASE_BUFFER_DONE, &cmd_done); +} + +static void hfi_process_session_end_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_sys_session_end_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE[%p]\n", session); + + if (!pkt || pkt->size != + sizeof(struct hfi_msg_sys_session_end_done_packet)) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", __func__); + return; + } + + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = NULL; + cmd_done.size = 0; + callback(SESSION_END_DONE, &cmd_done); +} + +static void hfi_process_session_abort_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_sys_session_abort_done_packet *pkt) +{ + struct msm_vidc_cb_cmd_done cmd_done = {0}; + + dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE[%p]\n", session); + + 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; + } + cmd_done.device_id = device_id; + cmd_done.session_id = session->session_id; + cmd_done.status = hfi_map_err_status(pkt->error_type); + cmd_done.data = NULL; + cmd_done.size = 0; + + callback(SESSION_ABORT_DONE, &cmd_done); +} + +static void hfi_process_session_get_seq_hdr_done(msm_vidc_callback callback, + u32 device_id, struct hal_session *session, + struct hfi_msg_session_get_sequence_header_done_packet *pkt) +{ + 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; + } + dprintk(VIDC_DBG, "RECEIVED:SESSION_GET_SEQ_HDR_DONE[%p]\n", session); + + data_done.device_id = device_id; + data_done.size = sizeof(struct msm_vidc_cb_data_done); + data_done.session_id = session->session_id; + data_done.status = hfi_map_err_status(pkt->error_type); + data_done.output_done.packet_buffer1 = + (ion_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); + callback(SESSION_GET_SEQ_HDR_DONE, &data_done); +} + +static void hfi_process_sys_get_prop_image_version( + struct hfi_msg_sys_property_info_packet *pkt) +{ + int i = 0; + u32 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, + "hfi_process_sys_get_prop_image_version: bad_pkt: %d\n", + 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 = smem_get_entry(SMEM_IMAGE_VERSION_TABLE, + &smem_block_size, 0, SMEM_ANY_HOST_FLAG); + 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 void hfi_process_sys_property_info( + struct hfi_msg_sys_property_info_packet *pkt) +{ + if (!pkt) { + dprintk(VIDC_ERR, "%s: invalid param\n", __func__); + return; + } + if (pkt->size < sizeof(*pkt)) { + dprintk(VIDC_ERR, + "hfi_process_sys_property_info: bad_pkt_size\n"); + return; + } + if (!pkt->num_properties) { + dprintk(VIDC_ERR, + "hfi_process_sys_property_info: no_properties\n"); + return; + } + + switch (pkt->rg_property_data[0]) { + case HFI_PROPERTY_SYS_IMAGE_VERSION: + hfi_process_sys_get_prop_image_version(pkt); + break; + default: + dprintk(VIDC_DBG, + "hfi_process_sys_property_info: unknown_prop_id: %x\n", + pkt->rg_property_data[0]); + } +} + +u32 hfi_process_msg_packet(msm_vidc_callback callback, u32 device_id, + struct vidc_hal_msg_pkt_hdr *msg_hdr, + struct list_head *sessions, struct mutex *session_lock) +{ + u32 rc = 0; + struct hal_session *session = NULL; + typedef void (*session_pkt_func_def)(msm_vidc_callback, u32, + void *, void *); + typedef void (*sys_pkt_func_def)(msm_vidc_callback, u32, void *); + session_pkt_func_def session_pkt_func = NULL; + sys_pkt_func_def sys_pkt_func = NULL; + + if (!callback || !session_lock || !msg_hdr || + msg_hdr->size < VIDC_IFACEQ_MIN_PKT_SIZE) { + dprintk(VIDC_ERR, "%s: bad packet/packet size\n", + __func__); + rc = -EINVAL; + return rc; + } + + dprintk(VIDC_INFO, "Received: %#x\n", msg_hdr->packet); + rc = (u32) msg_hdr->packet; + switch (msg_hdr->packet) { + case HFI_MSG_EVENT_NOTIFY: + session_pkt_func = + (session_pkt_func_def)hfi_process_event_notify; + break; + case HFI_MSG_SYS_INIT_DONE: + sys_pkt_func = (sys_pkt_func_def)hfi_process_sys_init_done; + break; + case HFI_MSG_SYS_SESSION_INIT_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_init_done; + break; + case HFI_MSG_SYS_PROPERTY_INFO: + sys_pkt_func = (sys_pkt_func_def)hfi_process_sys_property_info; + break; + case HFI_MSG_SYS_SESSION_END_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_end_done; + break; + case HFI_MSG_SESSION_LOAD_RESOURCES_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_load_res_done; + break; + case HFI_MSG_SESSION_START_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_start_done; + break; + case HFI_MSG_SESSION_STOP_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_stop_done; + break; + case HFI_MSG_SESSION_EMPTY_BUFFER_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_etb_done; + break; + case HFI_MSG_SESSION_FILL_BUFFER_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_ftb_done; + break; + case HFI_MSG_SESSION_FLUSH_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_flush_done; + break; + case HFI_MSG_SESSION_PROPERTY_INFO: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_prop_info; + break; + case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_rel_res_done; + break; + case HFI_MSG_SYS_RELEASE_RESOURCE: + sys_pkt_func = + (sys_pkt_func_def)hfi_process_sys_rel_resource_done; + break; + case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE: + session_pkt_func = + (session_pkt_func_def) + hfi_process_session_get_seq_hdr_done; + break; + case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_rel_buf_done; + break; + case HFI_MSG_SYS_SESSION_ABORT_DONE: + session_pkt_func = + (session_pkt_func_def)hfi_process_session_abort_done; + break; + case HFI_MSG_SYS_PC_PREP_DONE: + break; + default: + dprintk(VIDC_DBG, "UNKNOWN_MSG_TYPE : %d\n", msg_hdr->packet); + break; + } + mutex_lock(session_lock); + if (session_pkt_func) { + struct vidc_hal_session_cmd_pkt *pkt = + (struct vidc_hal_session_cmd_pkt *)msg_hdr; + session = hfi_process_get_session(sessions, pkt->session_id); + /* Event of type HFI_EVENT_SYS_ERROR will not have any session + * associated with it */ + if (!session && msg_hdr->packet != HFI_MSG_EVENT_NOTIFY) { + dprintk(VIDC_ERR, "%s Got invalid session id: %d\n", + __func__, pkt->session_id); + goto invalid_session; + } + } + + if (session_pkt_func) + session_pkt_func(callback, device_id, session, msg_hdr); + if (sys_pkt_func) + sys_pkt_func(callback, device_id, msg_hdr); + +invalid_session: + mutex_unlock(session_lock); + return rc; +} diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c new file mode 100644 index 000000000000..ff9ff92283f3 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_smem.c @@ -0,0 +1,794 @@ +/* Copyright (c) 2012-2015, 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 <asm/dma-iommu.h> +#include <linux/dma-attrs.h> +#include <linux/dma-buf.h> +#include <linux/dma-direction.h> +#include <linux/iommu.h> +#include <linux/msm_iommu_domains.h> +#ifdef CONFIG_ION +#include <linux/msm_ion.h> +#endif +#include <linux/slab.h> +#include <linux/types.h> +#include <media/msm_vidc.h> +#include <media/videobuf2-dma-contig.h> +#include "msm_vidc_debug.h" +#include "msm_vidc_resources.h" + +struct smem_client { + int mem_type; + void *clnt; + struct msm_vidc_platform_resources *res; +}; + +static int get_device_address(struct smem_client *smem_client, + struct dma_buf *buf, dma_addr_t *iova, + unsigned long *buffer_size, + unsigned long flags, struct context_bank_info *cb, + struct dma_mapping_info *mapping_info) +{ + int rc = 0; + struct dma_buf_attachment *attach; + struct sg_table *table = NULL; + phys_addr_t phys, orig_phys; + + if (!iova || !buffer_size || !buf || !smem_client || !mapping_info || !cb) { + dprintk(VIDC_ERR, "Invalid params: %p, %p, %p, %p, %p\n", + smem_client, buf, iova, buffer_size, cb); + return -EINVAL; + } + + + /* Prepare a dma buf for dma on the given device */ + attach = dma_buf_attach(buf, 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 */ + 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; + } + + if (table->sgl) { + dprintk(VIDC_DBG, + "%s: DMA buf: %p, device: %p, attach: %p, table: %p, table sgl: %p, rc: %d, dma_address: %pa\n", + __func__, buf, cb->dev, attach, + table, table->sgl, rc, + &table->sgl->dma_address); + + *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; + } + + /* Translation check for debugging */ + orig_phys = sg_phys(table->sgl); + + phys = iommu_iova_to_phys(cb->mapping->domain, *iova); + if (phys != orig_phys) { + dprintk(VIDC_ERR, + "%s iova_to_phys failed!!! mapped: %pa, got: %pa\n", + __func__, &orig_phys, &phys); + rc = -EIO; + goto mem_iova_to_phys_failed; + } + + mapping_info->dev = cb->dev; + mapping_info->mapping = cb->mapping; + mapping_info->table = table; + mapping_info->attach = attach; + mapping_info->buf = buf; + + dprintk(VIDC_DBG, "mapped dma buf %p to %pa\n", buf, iova); + return 0; +mem_iova_to_phys_failed: + dma_unmap_sg(cb->dev, table->sgl, table->nents, DMA_BIDIRECTIONAL); +mem_map_sg_failed: + dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL); +mem_map_table_failed: + dma_buf_detach(buf, attach); +mem_buf_attach_failed: + return rc; +} + +static void put_device_address(struct smem_client *smem_client, + u32 flags, struct dma_mapping_info *mapping_info, + enum hal_buffer buffer_type) +{ + if (!smem_client || !mapping_info) { + dprintk(VIDC_WARN, "Invalid params: %p, %p\n", + smem_client, mapping_info); + return; + } + + if (!mapping_info->dev || !mapping_info->table || + !mapping_info->buf || !mapping_info->attach) { + dprintk(VIDC_WARN, "Invalid params:\n"); + return; + } + + if (is_iommu_present(smem_client->res)) { + dprintk(VIDC_DBG, + "Calling dma_unmap_sg - device: %p, address: %pa, buf: %p, table: %p, attach: %p\n", + mapping_info->dev, + &mapping_info->table->sgl->dma_address, + mapping_info->buf, mapping_info->table, + mapping_info->attach); + + dma_buf_unmap_attachment(mapping_info->attach, + mapping_info->table, DMA_BIDIRECTIONAL); + dma_buf_detach(mapping_info->buf, mapping_info->attach); + dma_buf_put(mapping_info->buf); + } +} + +#ifdef CONFIG_MSM_VIDC_USES_ION +static int ion_get_device_address(struct smem_client *smem_client, + struct ion_handle *hndl, unsigned long align, + ion_phys_addr_t *iova, unsigned long *buffer_size, + unsigned long flags, enum hal_buffer buffer_type, + struct dma_mapping_info *mapping_info) +{ + int rc = 0; + struct dma_buf *buf = NULL; + struct context_bank_info *cb = NULL; + struct ion_client *clnt = smem_client->clnt; + if (!clnt) { + dprintk(VIDC_ERR, "Invalid client\n"); + return -EINVAL; + } + cb = msm_smem_get_context_bank(smem_client, flags & SMEM_SECURE, + buffer_type); + if (!cb) { + dprintk(VIDC_ERR, + "%s: Failed to get context bank device\n", + __func__); + return -EIO; + } + if (is_iommu_present(smem_client->res)) { + /* Convert an Ion handle to a dma buf */ + buf = ion_share_dma_buf(clnt, hndl); + if (IS_ERR_OR_NULL(buf)) { + rc = PTR_ERR(buf) ?: -ENOMEM; + dprintk(VIDC_ERR, "Share ION buf to DMA failed\n"); + goto mem_map_failed; + } + rc = get_device_address(smem_client, buf, align, iova, buffer_size, flags, cb, mapping_info); + if (rc) { + dprintk(VIDC_ERR, "ion iommu map failed - %d\n", rc); + goto mem_map_failed; + } + } else { + dprintk(VIDC_DBG, "Using physical memory address\n"); + rc = ion_phys(clnt, hndl, iova, (size_t *)buffer_size); + if (rc) { + dprintk(VIDC_ERR, "ion memory map failed - %d\n", rc); + goto mem_map_failed; + } + } +mem_map_failed: + return rc; +} + +static int ion_user_to_kernel(struct smem_client *client, int fd, u32 offset, + struct msm_smem *mem, enum hal_buffer buffer_type) +{ + struct ion_handle *hndl; + ion_phys_addr_t iova = 0; + unsigned long buffer_size = 0; + int rc = 0; + unsigned long align = SZ_4K; + + hndl = ion_import_dma_buf(client->clnt, fd); + dprintk(VIDC_DBG, "%s ion handle: %p\n", __func__, hndl); + if (IS_ERR_OR_NULL(hndl)) { + dprintk(VIDC_ERR, "Failed to get handle: %p, %d, %d, %p\n", + client, fd, offset, hndl); + rc = -ENOMEM; + goto fail_import_fd; + } + mem->kvaddr = NULL; + rc = ion_handle_get_flags(client->clnt, hndl, &mem->flags); + if (rc) { + dprintk(VIDC_ERR, "Failed to get ion flags: %d\n", rc); + goto fail_device_address; + } + + mem->buffer_type = buffer_type; + if (mem->flags & SMEM_SECURE) + align = ALIGN(align, SZ_1M); + + rc = ion_get_device_address(client, hndl, align, &iova, &buffer_size, + mem->flags, buffer_type, &mem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc); + goto fail_device_address; + } + + mem->mem_type = client->mem_type; + mem->smem_priv = hndl; + mem->device_addr = iova; + mem->size = buffer_size; + if ((u32)mem->device_addr != iova) { + dprintk(VIDC_ERR, "iova(%pa) truncated to %#x", + &iova, (u32)mem->device_addr); + goto fail_device_address; + } + dprintk(VIDC_DBG, + "%s: ion_handle = %p, fd = %d, device_addr = %pa, size = %zx, kvaddr = %p, buffer_type = %d, flags = %#lx\n", + __func__, mem->smem_priv, fd, &mem->device_addr, mem->size, + mem->kvaddr, mem->buffer_type, mem->flags); + return rc; +fail_device_address: + ion_free(client->clnt, hndl); +fail_import_fd: + return rc; +} + +static int alloc_ion_mem(struct smem_client *client, size_t size, u32 align, + u32 flags, enum hal_buffer buffer_type, struct msm_smem *mem, + int map_kernel) +{ + struct ion_handle *hndl; + ion_phys_addr_t iova = 0; + unsigned long buffer_size = 0; + unsigned long heap_mask = 0; + int rc = 0; + + align = ALIGN(align, SZ_4K); + size = ALIGN(size, SZ_4K); + + if (flags & SMEM_SECURE) { + size = ALIGN(size, SZ_1M); + align = ALIGN(align, SZ_1M); + flags |= ION_FLAG_ALLOW_NON_CONTIG; + } + + if (is_iommu_present(client->res)) { + heap_mask = ION_HEAP(ION_IOMMU_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_SECURE) + heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID); + + trace_msm_smem_buffer_ion_op_start("ALLOC", (u32)buffer_type, + heap_mask, size, align, flags, map_kernel); + hndl = ion_alloc(client->clnt, size, align, heap_mask, flags); + if (IS_ERR_OR_NULL(hndl)) { + dprintk(VIDC_ERR, + "Failed to allocate shared memory = %p, %zx, %d, %#x\n", + client, size, align, flags); + rc = -ENOMEM; + goto fail_shared_mem_alloc; + } + trace_msm_smem_buffer_ion_op_end("ALLOC", (u32)buffer_type, + heap_mask, size, align, flags, map_kernel); + mem->mem_type = client->mem_type; + mem->smem_priv = hndl; + mem->flags = flags; + mem->buffer_type = buffer_type; + if (map_kernel) { + mem->kvaddr = ion_map_kernel(client->clnt, hndl); + if (IS_ERR_OR_NULL(mem->kvaddr)) { + dprintk(VIDC_ERR, + "Failed to map shared mem in kernel\n"); + rc = -EIO; + goto fail_map; + } + } else { + mem->kvaddr = NULL; + } + + rc = ion_get_device_address(client, hndl, align, &iova, &buffer_size, + flags, buffer_type, &mem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get device address: %d\n", + rc); + goto fail_device_address; + } + mem->device_addr = iova; + if ((u32)mem->device_addr != iova) { + dprintk(VIDC_ERR, "iova(%pa) truncated to %#x", + &iova, (u32)mem->device_addr); + goto fail_device_address; + } + mem->size = size; + dprintk(VIDC_DBG, + "%s: ion_handle = %p, device_addr = %pa, size = %#zx, kvaddr = %p, buffer_type = %#x, flags = %#lx\n", + __func__, mem->smem_priv, &mem->device_addr, + mem->size, mem->kvaddr, mem->buffer_type, mem->flags); + return rc; +fail_device_address: + if (mem->kvaddr) + ion_unmap_kernel(client->clnt, hndl); +fail_map: + ion_free(client->clnt, hndl); +fail_shared_mem_alloc: + return rc; +} + +static void free_ion_mem(struct smem_client *client, struct msm_smem *mem) +{ + dprintk(VIDC_DBG, + "%s: ion_handle = %p, device_addr = %pa, size = %#zx, kvaddr = %p, buffer_type = %#x\n", + __func__, mem->smem_priv, &mem->device_addr, + mem->size, mem->kvaddr, mem->buffer_type); + + if (mem->device_addr) + put_device_address(client, mem->flags, + &mem->mapping_info, mem->buffer_type); + + if (mem->kvaddr) + ion_unmap_kernel(client->clnt, mem->smem_priv); + if (mem->smem_priv) { + trace_msm_smem_buffer_ion_op_start("FREE", + (u32)mem->buffer_type, -1, mem->size, -1, + mem->flags, -1); + dprintk(VIDC_DBG, + "%s: Freeing handle %p, client: %p\n", + __func__, mem->smem_priv, client->clnt); + ion_free(client->clnt, mem->smem_priv); + trace_msm_smem_buffer_ion_op_end("FREE", (u32)mem->buffer_type, + -1, mem->size, -1, mem->flags, -1); + } +} + +static void *ion_new_client(void) +{ + struct ion_client *client = NULL; + client = msm_ion_client_create("video_client"); + if (!client) + dprintk(VIDC_ERR, "Failed to create smem client\n"); + return client; +}; + +static void ion_delete_client(struct smem_client *client) +{ + ion_client_destroy(client->clnt); +} + +static int ion_cache_operations(struct smem_client *client, + struct msm_smem *mem, enum smem_cache_ops cache_op) +{ + unsigned long ionflag = 0; + int rc = 0; + int msm_cache_ops = 0; + if (!mem || !client) { + dprintk(VIDC_ERR, "Invalid params: %p, %p\n", + mem, client); + return -EINVAL; + } + rc = ion_handle_get_flags(client->clnt, mem->smem_priv, + &ionflag); + if (rc) { + dprintk(VIDC_ERR, + "ion_handle_get_flags failed: %d\n", rc); + goto cache_op_failed; + } + if (ION_IS_CACHED(ionflag)) { + switch (cache_op) { + case SMEM_CACHE_CLEAN: + msm_cache_ops = ION_IOC_CLEAN_CACHES; + break; + case SMEM_CACHE_INVALIDATE: + msm_cache_ops = ION_IOC_INV_CACHES; + break; + case SMEM_CACHE_CLEAN_INVALIDATE: + msm_cache_ops = ION_IOC_CLEAN_INV_CACHES; + break; + default: + dprintk(VIDC_ERR, "cache operation not supported\n"); + rc = -EINVAL; + goto cache_op_failed; + } + rc = msm_ion_do_cache_op(client->clnt, + (struct ion_handle *)mem->smem_priv, + 0, (unsigned long)mem->size, + msm_cache_ops); + if (rc) { + dprintk(VIDC_ERR, + "cache operation failed %d\n", rc); + goto cache_op_failed; + } + } +cache_op_failed: + return rc; +} +#endif + +static int alloc_dma_mem(struct smem_client *client, size_t size, u32 align, + u32 flags, enum hal_buffer buffer_type, struct msm_smem *mem, + int map_kernel) +{ + int rc = 0; + enum dma_data_direction dma_dir; + unsigned long buffer_size = 0; + struct context_bank_info *cb = NULL; + + align = ALIGN(align, SZ_4K); + size = ALIGN(size, SZ_4K); + size = PAGE_ALIGN(size); + + dprintk(VIDC_DBG, "alloc_dma_mem type %d, size %zu\n", buffer_type, size); + + switch (buffer_type) { + case HAL_BUFFER_INPUT: + dma_dir = DMA_TO_DEVICE; + break; + case HAL_BUFFER_OUTPUT: + dma_dir = DMA_FROM_DEVICE; + break; + default: + dma_dir = DMA_FROM_DEVICE; + break; + } + + mem->mem_type = client->mem_type; + mem->flags = flags; + mem->buffer_type = buffer_type; + mem->kvaddr = NULL; + mem->size = size; + + cb = msm_smem_get_context_bank(client, flags & SMEM_SECURE, + buffer_type); + if (!cb) { + dprintk(VIDC_ERR, + "%s: Failed to get context bank device\n", + __func__); + return -EIO; + } + + mem->smem_priv = vb2_dma_contig_memops.alloc(cb->alloc_ctx, size, dma_dir, 0); + if (IS_ERR_OR_NULL(mem->smem_priv)) { + dprintk(VIDC_ERR, "VB2 contig memops alloc failed %zu\n", size); + rc = PTR_ERR(mem->smem_priv) ?: -ENOMEM; + goto err_dma_alloc; + } + + dprintk(VIDC_DBG, "alloc_dma_mem, map_kernel\n"); + mem->kvaddr = vb2_dma_contig_memops.vaddr(mem->smem_priv); + if (!mem->kvaddr) { + dprintk(VIDC_WARN, "Failed to map dma buf %pad\n", &mem->device_addr); + rc = -ENOMEM; + goto err_put; + } + + //rc = dma_get_sgtable(&res->pdev->dev, &mem->mapping_info.table, mem->kvaddr, mem->device_addr, size); + rc = get_device_address(client, vb2_dma_contig_memops.get_dmabuf(mem->smem_priv, O_CLOEXEC), + &mem->device_addr, &buffer_size, flags, cb, &mem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get iova for dma buf %pad\n", + vb2_dma_contig_memops.cookie(mem->smem_priv)); + goto err_put; + } + + dprintk(VIDC_DBG, + "%s: dma_handle = %p, device_addr = %pad, size = %#zx, kvaddr = %p, buffer_type = %#x, flags = %#lx\n", + __func__, mem->smem_priv, &mem->device_addr, + mem->size, mem->kvaddr, mem->buffer_type, mem->flags); + + dprintk(VIDC_DBG, + "%s: sg_handle = 0x%p, device_addr = 0x%x, size = %zu, kvaddr = 0x%p, buffer_type = %d\n", + __func__, mem->smem_priv, (u32)mem->device_addr, + mem->size, mem->kvaddr, mem->buffer_type); + return rc; +err_put: + vb2_dma_contig_memops.put(mem->smem_priv); +err_dma_alloc: + mem->smem_priv = NULL; + return rc; +} + +static void free_dma_mem(struct smem_client *client, struct msm_smem *mem) +{ + dprintk(VIDC_DBG, + "%s: mem priv = 0x%p, device_addr = 0x%pa, size = 0x%zx, kvaddr = 0x%p, buffer_type = 0x%x\n", + __func__, mem->smem_priv, &mem->device_addr, + mem->size, mem->kvaddr, mem->buffer_type); + if (mem->device_addr) + put_device_address(client, mem->flags, + &mem->mapping_info, mem->buffer_type); + if (mem->smem_priv) { + vb2_dma_contig_memops.put(mem->smem_priv); + mem->smem_priv = NULL; + } +} + +struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, + enum hal_buffer buffer_type) +{ + struct smem_client *client = clt; + int rc = 0; + struct msm_smem *mem; + if (fd < 0) { + dprintk(VIDC_ERR, "Invalid fd: %d\n", fd); + return NULL; + } + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) { + dprintk(VIDC_ERR, "Failed to allocte shared mem\n"); + return NULL; + } + switch (client->mem_type) { +#ifdef CONFIG_MSM_VIDC_USES_ION + case SMEM_ION: + rc = ion_user_to_kernel(clt, fd, offset, mem, buffer_type); + break; +#endif + default: + dprintk(VIDC_ERR, "Mem type not supported\n"); + rc = -EINVAL; + break; + } + if (rc) { + dprintk(VIDC_ERR, "Failed to allocate shared memory\n"); + kfree(mem); + mem = NULL; + } + return mem; +} + +int msm_smem_cache_operations(void *clt, struct msm_smem *mem, + enum smem_cache_ops cache_op) +{ + struct smem_client *client = clt; + int rc = 0; + if (!client) { + dprintk(VIDC_ERR, "Invalid params: %p\n", + client); + return -EINVAL; + } + switch (client->mem_type) { +#ifdef CONFIG_MSM_VIDC_USES_ION + case SMEM_ION: + rc = ion_cache_operations(client, mem, cache_op); + if (rc) + dprintk(VIDC_ERR, + "Failed cache operations: %d\n", rc); + break; +#endif + case SMEM_DMA: + dprintk(VIDC_DBG, + "Ignore dma cache operations: %d\n", rc); + break; + default: + dprintk(VIDC_ERR, "Mem type not supported\n"); + break; + } + return rc; +} + +void *msm_smem_new_client(enum smem_type mtype, + void *platform_resources) +{ + struct smem_client *client = NULL; + void *clnt = NULL; + struct msm_vidc_platform_resources *res = platform_resources; + switch (mtype) { +#ifdef CONFIG_MSM_VIDC_USES_ION + case SMEM_ION: + clnt = ion_new_client(); + break; +#endif + case SMEM_DMA: + clnt = vb2_dma_contig_init_ctx(&(res->pdev->dev)); + break; + default: + dprintk(VIDC_ERR, "Mem type not supported\n"); + break; + } + if (clnt) { + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client) { + client->mem_type = mtype; + client->clnt = clnt; + client->res = res; + } + } else { + dprintk(VIDC_ERR, "Failed to create new client: mtype = %d\n", + mtype); + } + return client; +} + +struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel) +{ + struct smem_client *client; + int rc = 0; + struct msm_smem *mem; + client = clt; + if (!client) { + dprintk(VIDC_ERR, "Invalid client passed\n"); + return NULL; + } + if (!size) { + dprintk(VIDC_ERR, "No need to allocate memory of size: %zx\n", + size); + return NULL; + } + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) { + dprintk(VIDC_ERR, "Failed to allocate shared mem\n"); + return NULL; + } + switch (client->mem_type) { +#ifdef CONFIG_MSM_VIDC_USES_ION + case SMEM_ION: + rc = alloc_ion_mem(client, size, align, flags, buffer_type, + mem, map_kernel); + break; +#endif + case SMEM_DMA: + rc = alloc_dma_mem(client, size, align, flags, buffer_type, + mem, map_kernel); + break; + default: + dprintk(VIDC_ERR, "Mem type not supported\n"); + rc = -EINVAL; + break; + } + if (rc) { + dprintk(VIDC_ERR, "Failed to allocate shared memory\n"); + kfree(mem); + mem = NULL; + } + return mem; +} + +void msm_smem_free(void *clt, struct msm_smem *mem) +{ + struct smem_client *client = clt; + if (!client || !mem) { + dprintk(VIDC_ERR, "Invalid client/handle passed\n"); + return; + } + switch (client->mem_type) { +#ifdef CONFIG_MSM_VIDC_USES_ION + case SMEM_ION: + free_ion_mem(client, mem); + break; +#endif + case SMEM_DMA: + free_dma_mem(client, mem); + break; + default: + dprintk(VIDC_ERR, "Mem type not supported\n"); + break; + } + kfree(mem); +}; + +void msm_smem_delete_client(void *clt) +{ + struct smem_client *client = clt; + if (!client) { + dprintk(VIDC_ERR, "Invalid client passed\n"); + return; + } + switch (client->mem_type) { +#ifdef CONFIG_MSM_VIDC_USES_ION + case SMEM_ION: + ion_delete_client(client); + break; +#endif + case SMEM_DMA: + vb2_dma_contig_cleanup_ctx(client->clnt); + break; + default: + dprintk(VIDC_ERR, "Mem type not supported\n"); + break; + } + kfree(client); +} + +struct context_bank_info *msm_smem_get_context_bank(void *clt, + bool is_secure, enum hal_buffer buffer_type) +{ + struct smem_client *client = clt; + struct context_bank_info *cb = NULL, *match = NULL; + + if (!clt) { + dprintk(VIDC_ERR, "%s - invalid params\n", __func__); + return NULL; + } + + list_for_each_entry(cb, &client->res->context_banks, list) { + if (cb->is_secure == is_secure && + cb->buffer_type & buffer_type) { + match = cb; + dprintk(VIDC_DBG, + "context bank found for CB : %s, device: %p mapping: %p\n", + match->name, match->dev, match->mapping); + break; + } + } + + return match; +} + +void *msm_smem_get_alloc_ctx(void *mem_client) +{ + struct smem_client *client = mem_client; + return client->clnt; +} + + +struct msm_smem* msm_smem_map_dma_buf(void* smem_client, struct dma_buf* dbuf, + enum hal_buffer buffer_type) +{ + struct smem_client *client = smem_client; + int rc = 0; + struct msm_smem *mem; + unsigned long buffer_size = 0; + u32 flags = 0; + struct context_bank_info *cb = NULL; + + dprintk(VIDC_DBG, "msm_smem_map_dma_buf type %d\n", buffer_type); + + if (client == NULL || dbuf == NULL) { + dprintk(VIDC_ERR, "%s: Invalid params \n", __func__); + return NULL; + } + + if (is_iommu_present(client->res)) { + cb = msm_smem_get_context_bank(smem_client, flags & SMEM_SECURE, + buffer_type); + if (!cb) { + dprintk(VIDC_ERR, + "%s: Failed to get context bank device\n", + __func__); + return NULL; + } + } + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) { + dprintk(VIDC_ERR, "Failed to allocte shared mem\n"); + return NULL; + } + + rc = get_device_address(client, dbuf, &mem->device_addr, &buffer_size, + flags, cb, &mem->mapping_info); + if (rc) { + dprintk(VIDC_ERR, "Failed to get iova for dma buf %pad\n", dbuf); + goto err_get_device_address; + } + mem->mem_type = client->mem_type; + mem->smem_priv = NULL; + mem->size = buffer_size; + mem->kvaddr = NULL; + mem->buffer_type = buffer_type; + + return mem; +err_get_device_address: + kfree(mem); + mem = NULL; + return ERR_PTR(rc); +} diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c new file mode 100644 index 000000000000..ac421b41c9fa --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -0,0 +1,816 @@ +/* Copyright (c) 2012-2015, 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 <linux/debugfs.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/msm_iommu_domains.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/qcom_iommu.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/version.h> +#include <media/msm_vidc.h> +#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 +#define EARLY_FIRMWARE_LOAD_DELAY 1000 + +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("msm_v4l2_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("msm_v4l2_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("msm_v4l2_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("msm_v4l2_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_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + return msm_vidc_querybuf(get_vidc_inst(file, fh), 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); + int rc = 0; + if (dec->cmd == V4L2_DEC_CMD_STOP) + rc = msm_vidc_release_buffers(vidc_inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (rc) + dprintk(VIDC_WARN, + "Failed to release dec output buffers: %d\n", rc); + return msm_vidc_decoder_cmd((void *)vidc_inst, 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); + int rc = 0; + if (enc->cmd == V4L2_ENC_CMD_STOP) + rc = msm_vidc_release_buffers(vidc_inst, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (rc) + dprintk(VIDC_WARN, + "Failed to release enc output buffers: %d\n", rc); + return msm_vidc_encoder_cmd((void *)vidc_inst, 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_s_parm((void *)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_querybuf = msm_v4l2_querybuf, + .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 int msm_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, NULL); + int rc = msm_vidc_mmap(vidc_inst, vma); + if (rc) { + dprintk(VIDC_ERR, "msm_vidc_mmap error %d \n", rc); + } + return rc; +} + +static const struct v4l2_file_operations msm_v4l2_vidc_fops = { + .owner = THIS_MODULE, + .open = msm_v4l2_open, + .release = msm_v4l2_close, + .ioctl = video_ioctl2, + .poll = msm_v4l2_poll, + .mmap = msm_v4l2_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = v4l2_compat_ioctl32, +#endif +}; + +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 %p %p\n", + __func__, core, pdev); + return -EINVAL; + } + core->hfi_type = read_hfi_type(pdev); + if (core->hfi_type < 0) { + dprintk(VIDC_ERR, "Failed to identify core type\n"); + return core->hfi_type; + } + + core->resources.pdev = pdev; + if (pdev->dev.of_node) { + /* Target supports DT, parse from it */ + return read_platform_resources_from_dt(&core->resources); + } else { + 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 msm_vidc_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) + if (core->hfi_type == VIDC_HFI_Q6) + return snprintf(buf, PAGE_SIZE, "q6_dec"); + else + return snprintf(buf, PAGE_SIZE, "venus_dec"); + else if (dev == &core->vdev[MSM_VIDC_ENCODER].vdev.dev) + if (core->hfi_type == VIDC_HFI_Q6) + return snprintf(buf, PAGE_SIZE, "q6_enc"); + else + return snprintf(buf, PAGE_SIZE, "venus_enc"); + else + return 0; + else + return 0; +} + +static DEVICE_ATTR(link_name, 0444, msm_vidc_link_name_show, NULL); + +static ssize_t store_pwr_collapse_delay(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 show_pwr_collapse_delay(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", msm_vidc_pwr_collapse_delay); +} + +static DEVICE_ATTR(pwr_collapse_delay, 0644, show_pwr_collapse_delay, + store_pwr_collapse_delay); + +static ssize_t show_thermal_level(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", vidc_driver->thermal_level); +} + +static ssize_t store_thermal_level(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(thermal_level, S_IRUGO | S_IWUSR, show_thermal_level, + store_thermal_level); + +static struct attribute *msm_vidc_core_attrs[] = { + &dev_attr_pwr_collapse_delay.attr, + &dev_attr_thermal_level.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"}, + {} +}; + +struct fw_load_handler_data { + struct msm_vidc_core *core; + struct delayed_work work; +}; + + +static void fw_load_handler(struct work_struct *work) +{ + struct msm_vidc_core *core = NULL; + struct fw_load_handler_data *handler = NULL; + int rc = 0; + + handler = container_of(work, struct fw_load_handler_data, + work.work); + if (!handler || !handler->core) { + dprintk(VIDC_ERR, "%s - invalid work or core handle\n", + __func__); + goto exit; + } + core = handler->core; + + rc = msm_comm_load_fw(core); + if (rc) { + dprintk(VIDC_ERR, "%s - failed to load fw\n", __func__); + goto exit; + } + + rc = msm_comm_check_core_init(core); + if (rc) { + dprintk(VIDC_ERR, "%s - failed to init core\n", __func__); + goto exit; + } + dprintk(VIDC_DBG, "%s - firmware loaded successfully\n", __func__); + +exit: + kfree(handler); +} + +static void load_firmware(struct msm_vidc_core *core) +{ + struct fw_load_handler_data *handler = NULL; + + handler = kzalloc(sizeof(*handler), GFP_KERNEL); + if (!handler) { + dprintk(VIDC_ERR, + "%s - failed to allocate sys error handler\n", + __func__); + return; + } + handler->core = core; + INIT_DELAYED_WORK(&handler->work, fw_load_handler); + schedule_delayed_work(&handler->work, + msecs_to_jiffies(EARLY_FIRMWARE_LOAD_DELAY)); +} + +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; + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core || !vidc_driver) { + dprintk(VIDC_ERR, + "Failed to allocate memory for device core\n"); + rc = -ENOMEM; + goto err_no_mem; + } + + 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; + } + if (core->hfi_type == VIDC_HFI_Q6) { + dprintk(VIDC_DBG, "Q6 hfi device probe called\n"); + nr += MSM_VIDC_MAX_DEVICES; + core->id = MSM_VIDC_CORE_Q6; + } else { + 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); + + 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"); + goto err_fail_sub_device_probe; + } + + if (core->resources.early_fw_load) + load_firmware(core); + + 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); +err_no_mem: + return rc; +} + +static int msm_vidc_probe(struct platform_device *pdev) +{ + int rc = 0; + + /* + * 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,context-bank")) { + rc = msm_vidc_probe_sub_devices(pdev); + if (rc) + dprintk(VIDC_ERR, "sub-devices probe failed\n"); + return rc; + } + + return msm_vidc_probe_vidc_device(pdev); +} + +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 %p", __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) +{ + struct msm_vidc_core *core; + + if (!dev || !dev->driver) { + /* driver possibly not probed yet */ + return 0; + } + + core = dev_get_drvdata(dev); + if (!core) { + dprintk(VIDC_ERR, "%s invalid core\n", __func__); + return -EINVAL; + } + + dprintk(VIDC_INFO, "%s\n", __func__); + return msm_vidc_suspend(core->id); +} + +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", + .owner = THIS_MODULE, + .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"); + 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/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c new file mode 100644 index 000000000000..9d1f067ff721 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -0,0 +1,2779 @@ +/* Copyright (c) 2012-2015, 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 <linux/slab.h> +#include <linux/qcom_scm.h> +#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 MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME +#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8010 +#define MB_SIZE_IN_PIXEL (16 * 16) + +#define TZ_DYNAMIC_BUFFER_FEATURE_ID 12 +#define TZ_FEATURE_VERSION(major, minor, patch) \ + (((major & 0x3FF) << 22) | ((minor & 0x3FF) << 12) | (patch & 0xFFF)) + +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 *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_video_vidc_extradata[] = { + "Extradata none", + "Extradata MB Quantization", + "Extradata Interlace Video", + "Extradata VC1 Framedisp", + "Extradata VC1 Seqdisp", + "Extradata timestamp", + "Extradata S3D Frame Packing", + "Extradata Frame Rate", + "Extradata Panscan Window", + "Extradata Recovery point SEI", + "Extradata Closed Caption UD", + "Extradata AFD UD", + "Extradata Multislice info", + "Extradata number of concealed MB", + "Extradata metadata filler", + "Extradata input crop", + "Extradata digital zoom", + "Extradata aspect ratio", + "Extradata mpeg2 seqdisp", +}; +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[] = { + "Level 0", + "Level 1", + "Level 2", + "Level 3", +}; + +static const char *const mpeg_vidc_video_h264_mvc_layout[] = { + "Frame packing arrangement sequential", + "Frame packing arrangement top-bottom", +}; + +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, + .step = 0, + }, + { + .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, + .step = 0, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_PICTURE_TYPE, + .name = "Picture Type Decoding", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 15, + .default_value = 15, + .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, + .step = 0, + }, + { + .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_FRAME_BITS_INFO, + .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) | + (1 << 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) + ), + .qmenu = mpeg_video_vidc_extradata, + .step = 0, + }, + { + .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, + .step = 0, + }, + { + .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, + .step = 0, + }, + { + .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, + .step = 0, + }, + { + .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, + }, + { + .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, + }, + { + .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, + }, + { + .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, + }, + { + .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, + }, + { + .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 = 0, + .qmenu = mpeg2_profile, + .flags = V4L2_CTRL_FLAG_VOLATILE, + }, + { + .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 = 0, + .qmenu = mpeg2_level, + .flags = V4L2_CTRL_FLAG_VOLATILE, + }, + { + .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, + }, + { + .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, + }, +}; + +#define NUM_CTRLS ARRAY_SIZE(msm_vdec_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_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(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->capability.buffer_size_limit && + (inst->capability.buffer_size_limit < frame_size)) { + frame_size = inst->capability.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 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 = 1, /* TODO: was 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 = 1, /* TODO: was 2 */ + .get_frame_size = get_frame_size_nv12_ubwc, + .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 = "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 %p 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 %p 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 = %p, buffer = %p\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; + unsigned int *plane_sizes = NULL, extra_idx = 0; + struct hal_buffer_requirements *bufreq; + + if (!inst || !f || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, + "Invalid input, inst = %p, format = %p\n", inst, f); + return -EINVAL; + } + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, "Getting buffer requirements failed: %d\n", + rc); + return rc; + } + + 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) { + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_PRIMARY) { + inst->prop.height[CAPTURE_PORT] = inst->reconfig_height; + inst->prop.width[CAPTURE_PORT] = inst->reconfig_width; + inst->prop.height[OUTPUT_PORT] = inst->reconfig_height; + inst->prop.width[OUTPUT_PORT] = inst->reconfig_width; + } else { + 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]; + + rc = msm_comm_try_get_bufreqs(inst); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed : Buffer requirements\n", __func__); + goto exit; + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + plane_sizes = &inst->bufq[OUTPUT_PORT].vb2_bufq.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]; + } + } else { + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV12_UBWC: + call_hfi_op(hdev, get_stride_scanline, + COLOR_FMT_NV12, + inst->prop.width[CAPTURE_PORT], + inst->prop.height[CAPTURE_PORT], + &stride, &scanlines); + break; + default: + dprintk(VIDC_WARN, "Color format not recognized\n"); + break; + } + + bufreq = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + f->fmt.pix_mp.plane_fmt[0].sizeimage = + bufreq ? bufreq->buffer_size : 0; + + extra_idx = EXTRADATA_IDX(fmt->num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_OUTPUT); + f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage = + bufreq ? bufreq->buffer_size : 0; + } + + for (i = 0; i < fmt->num_planes; ++i) + inst->bufq[CAPTURE_PORT].vb2_bufq.plane_sizes[i] = + f->fmt.pix_mp.plane_fmt[i].sizeimage; + } + + if (stride && scanlines) { + f->fmt.pix_mp.plane_fmt[0].bytesperline = + (__u16)stride; + f->fmt.pix_mp.plane_fmt[0].reserved[0] = + (__u16)scanlines; + } else { + f->fmt.pix_mp.plane_fmt[0].bytesperline = + (__u16)inst->prop.width[CAPTURE_PORT]; + f->fmt.pix_mp.plane_fmt[0].reserved[0] = + (__u16)inst->prop.height[CAPTURE_PORT]; + } + + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) { + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + f->fmt.pix_mp.height = inst->prop.height[CAPTURE_PORT]; + f->fmt.pix_mp.width = inst->prop.width[CAPTURE_PORT]; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + 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]; + } + } +exit: + return rc; +} +int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a) +{ + u64 us_per_frame = 0; + int rc = 0, fps = 0; + 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 = USEC_PER_SEC; + do_div(fps, us_per_frame); + + if (fps % 15 == 14 || fps % 24 == 23) + fps = fps + 1; + else if (fps % 24 == 1 || fps % 15 == 1) + fps = fps - 1; + + if (inst->prop.fps != fps) { + dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n", + inst, inst->prop.fps, fps); + inst->prop.fps = fps; + msm_dcvs_init_load(inst); + msm_comm_scale_clocks_and_bus(inst); + } +exit: + return rc; +} + +static int set_buffer_size(struct msm_vidc_inst *inst, + u32 buffer_size, enum hal_buffer buffer_type) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_buffer_size_actual buffer_size_actual; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + + dprintk(VIDC_DBG, + "Set actual buffer size = %d for buffer type %d to fw\n", + buffer_size, buffer_type); + + buffer_size_actual.buffer_type = buffer_type; + buffer_size_actual.buffer_size = buffer_size; + rc = call_hfi_op(hdev, session_set_property, + inst->session, HAL_PARAM_BUFFER_SIZE_ACTUAL, + &buffer_size_actual); + if (rc) + dprintk(VIDC_ERR, + "%s - failed to set actual buffer size %u on firmware\n", + __func__, buffer_size); + return rc; +} + +static int update_output_buffer_size(struct msm_vidc_inst *inst, + struct v4l2_format *f, struct msm_vidc_format *fmt) +{ + int rc = 0, i = 0; + struct hal_buffer_requirements *bufreq; + + if (!inst || !f || !fmt) + return -EINVAL; + + /* + * Compare set buffer size and update to firmware if it's bigger + * then firmware returned buffer size. + */ + for (i = 0; i < fmt->num_planes; ++i) { + enum hal_buffer type = msm_comm_get_hal_output_buffer(inst); + + if (EXTRADATA_IDX(fmt->num_planes) && + i == EXTRADATA_IDX(fmt->num_planes)) { + type = HAL_BUFFER_EXTRADATA_OUTPUT; + } + + bufreq = get_buff_req_buffer(inst, type); + if (!bufreq) + goto exit; + + if (f->fmt.pix_mp.plane_fmt[i].sizeimage > + bufreq->buffer_size) { + rc = set_buffer_size(inst, + f->fmt.pix_mp.plane_fmt[i].sizeimage, type); + if (rc) + goto exit; + } + } + + /* Query buffer requirements from firmware */ + rc = msm_comm_try_get_bufreqs(inst); + if (rc) + dprintk(VIDC_WARN, + "Failed to get buf req, %d\n", rc); + + /* Read back updated firmware size */ + for (i = 0; i < fmt->num_planes; ++i) { + enum hal_buffer type = msm_comm_get_hal_output_buffer(inst); + + if (EXTRADATA_IDX(fmt->num_planes) && + i == EXTRADATA_IDX(fmt->num_planes)) { + type = HAL_BUFFER_EXTRADATA_OUTPUT; + } + + bufreq = get_buff_req_buffer(inst, type); + f->fmt.pix_mp.plane_fmt[i].sizeimage = bufreq ? + bufreq->buffer_size : 0; + dprintk(VIDC_DBG, + "updated buffer size for plane[%d] = %d\n", + i, f->fmt.pix_mp.plane_fmt[i].sizeimage); + } +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 = v4l2_s_ctrl(NULL, &inst->ctrl_handler, &ctrl); + if (rc) + dprintk(VIDC_ERR, "set alloc_mode failed\n"); + } + + if (defaults & HAL_VIDEO_CONTINUE_DATA_TRANSFER) { + dprintk(VIDC_DBG, "Enable continue_data_transfer\n"); + ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER; + ctrl.value = true; + rc = v4l2_s_ctrl(NULL, &inst->ctrl_handler, &ctrl); + if (rc) + dprintk(VIDC_ERR, "set cont_data_transfer failed\n"); + } + + 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; + int rc = 0; + int ret = 0; + int i; + int max_input_size = 0; + + if (!inst || !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; + } + + 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_PRIMARY) { + inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width; + inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height; + msm_comm_set_color_format(inst, HAL_BUFFER_OUTPUT, + f->fmt.pix_mp.pixelformat); + } + + inst->fmts[fmt->type] = fmt; + 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_OUTPUT, + f->fmt.pix_mp.pixelformat); + 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); + } + + ret = ret || msm_comm_try_get_bufreqs(inst); + if (ret) { + for (i = 0; i < fmt->num_planes; ++i) { + f->fmt.pix_mp.plane_fmt[i].sizeimage = + get_frame_size(inst, fmt, f->type, i); + } + } else { + rc = update_output_buffer_size(inst, f, fmt); + if (rc) { + dprintk(VIDC_ERR, + "%s - failed to update buffer size: %d\n", + __func__, rc); + goto err_invalid_fmt; + } + } + + f->fmt.pix_mp.num_planes = fmt->num_planes; + for (i = 0; i < fmt->num_planes; ++i) { + inst->bufq[CAPTURE_PORT].vb2_bufq.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; + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_PRIMARY) { + 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 err_invalid_fmt; + } + + 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; + } + + 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_type(fmt->fourcc) & + inst->core->dec_codec_supported)) { + dprintk(VIDC_ERR, + "Codec(%#x) is not present in the supported codecs list(%#x)\n", + get_hal_codec_type(fmt->fourcc), + inst->core->dec_codec_supported); + rc = -EINVAL; + goto err_invalid_fmt; + } + + inst->fmts[fmt->type] = 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; + } + + 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, fmt, 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 = fmt->num_planes; + for (i = 0; i < fmt->num_planes; ++i) { + inst->bufq[OUTPUT_PORT].vb2_bufq.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 = %p, cap = %p\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 | + V4L2_CAP_DEVICE_CAPS; + cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; + 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 = %p, f = %p\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_display_hold_count(struct msm_vidc_inst *inst, + unsigned int display_count) +{ + int rc = 0; + struct hal_buffer_display_hold_count_actual display; + struct hfi_device *hdev; + + hdev = inst->core->device; + + display.buffer_type = + msm_comm_get_hal_output_buffer(inst); + display.hold_count = display_count; + + rc = call_hfi_op(hdev, session_set_property, + inst->session, + HAL_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL, + &display); + if (rc) + dprintk(VIDC_ERR, "failed to set display hold count %d\n", rc); + + return rc; +} + +static int msm_vdec_queue_setup(struct vb2_queue *q, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + void *alloc_ctxs[]) +{ + int i, rc = 0; + struct msm_vidc_inst *inst; + struct hal_buffer_requirements *bufreq; + int extra_idx = 0; + struct hfi_device *hdev; + struct hal_buffer_count_actual new_buf_count; + enum hal_property property_id; + struct context_bank_info *cb; + + if (!q || !num_buffers || !num_planes + || !sizes || !q->drv_priv) { + dprintk(VIDC_ERR, "Invalid input, q = %p, %p, %p\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; + } + + hdev = inst->core->device; + + 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; + cb = msm_smem_get_context_bank(inst->mem_client, false, HAL_BUFFER_INPUT); + if (!cb) { + dprintk(VIDC_ERR,"Failed to find context bank for input buffer\n"); + rc = -EINVAL; + break; + } + for (i = 0; i < *num_planes; i++) { + sizes[i] = get_frame_size(inst, + inst->fmts[OUTPUT_PORT], q->type, i); + alloc_ctxs[i] = cb->alloc_ctx; + } + property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; + new_buf_count.buffer_type = HAL_BUFFER_INPUT; + new_buf_count.buffer_count_actual = *num_buffers; + rc = call_hfi_op(hdev, session_set_property, + inst->session, property_id, &new_buf_count); + if (rc) { + dprintk(VIDC_WARN, + "Failed to set new buffer count(%d) on FW, err: %d\n", + new_buf_count.buffer_count_actual, rc); + } + 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; + } + + *num_buffers = max(*num_buffers, bufreq->buffer_count_min); + if (*num_buffers != bufreq->buffer_count_actual) { + property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; + new_buf_count.buffer_type = + msm_comm_get_hal_output_buffer(inst); + new_buf_count.buffer_count_actual = *num_buffers; + rc = call_hfi_op(hdev, session_set_property, + inst->session, property_id, &new_buf_count); + + set_display_hold_count(inst, + *num_buffers - bufreq->buffer_count_actual); + } + + 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); + cb = msm_smem_get_context_bank(inst->mem_client, false, + msm_comm_get_hal_output_buffer(inst)); + if (!cb) { + dprintk(VIDC_ERR,"Failed to find context bank for output buffer\n"); + rc = -EINVAL; + break; + } + sizes[0] = bufreq->buffer_size; + alloc_ctxs[0] = cb->alloc_ctx; + extra_idx = + EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + bufreq = get_buff_req_buffer(inst, + HAL_BUFFER_EXTRADATA_OUTPUT); + cb = msm_smem_get_context_bank(inst->mem_client, false, + HAL_BUFFER_EXTRADATA_OUTPUT); + if (!cb) { + dprintk(VIDC_ERR,"Failed to find context bank for extradata output buffer\n"); + rc = -EINVAL; + break; + } + alloc_ctxs[extra_idx] = cb->alloc_ctx; + if (bufreq) { + sizes[extra_idx] = bufreq->buffer_size; + } else { + *num_planes = 1; + sizes[extra_idx] = 0; + } + } + break; + default: + dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type); + rc = -EINVAL; + break; + } + return rc; +} + +static int msm_vdec_buf_init(struct vb2_buffer *vb) +{ + int rc = 0, i = 0; + struct vb2_queue *q = vb->vb2_queue; + struct msm_vidc_inst *inst = q->drv_priv; + struct buffer_info* binfo = NULL; + struct vidc_buffer_addr_info buffer_info; + enum hal_buffer buffer_type = HAL_BUFFER_INPUT; + struct dma_buf* dbuf; + struct hfi_device *hdev; + + dprintk(VIDC_DBG, "msm_vdec_buf_init \n"); + if (q->memory == V4L2_MEMORY_USERPTR || + q->memory == V4L2_MEMORY_DMABUF) { + dprintk(VIDC_DBG, + "Ignore buf init for userptr & DMABUF modes"); + return rc; + } + if (!inst) { + return -EINVAL; + } + + hdev = inst->core->device; + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + return rc; + } + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + buffer_type = HAL_BUFFER_INPUT; + else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + buffer_type = HAL_BUFFER_OUTPUT; + + for (i = 0; i < vb->num_planes; ++i) { + if (vb->vb2_queue->mem_ops->get_dmabuf != NULL) { + dbuf = vb->vb2_queue->mem_ops->get_dmabuf( + vb->planes[i].mem_priv, 0); + } + else { + dprintk(VIDC_ERR, "get_dmabuf is not supported \n"); + rc = -EINVAL; + goto free_binfo; + } + binfo->handle[i] = msm_smem_map_dma_buf( + inst->mem_client, dbuf, buffer_type); + binfo->mapped[i] = true; + binfo->device_addr[i] = binfo->handle[i]->device_addr; + binfo->size[i] = vb->v4l2_planes[i].length; + dprintk(VIDC_DBG, "%s: device_addr 0x %pa, index %d, plane %d, size %d", + __func__, &binfo->device_addr[i], vb->v4l2_buf.index, i, binfo->size[i]); + } + binfo->num_planes = vb->num_planes; + binfo->memory = q->memory; + binfo->v4l2_index = vb->v4l2_buf.index; + binfo->type = q->type; + + mutex_lock(&inst->registeredbufs.lock); + list_add_tail(&binfo->list, &inst->registeredbufs.list); + mutex_unlock(&inst->registeredbufs.lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + //set buffer to FW, TODO move to dymaic buf mode + buffer_info.buffer_size = vb->v4l2_planes[0].length; + buffer_info.buffer_type = + msm_comm_get_hal_output_buffer(inst); + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = binfo->device_addr[0]; + 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"); + + } + + return rc; +free_binfo: + kfree(binfo); + return rc; +} + +static inline int start_streaming(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct vb2_buf_entry *temp; + struct hfi_device *hdev; + struct list_head *ptr, *next; + + hdev = inst->core->device; + inst->in_reconfig = false; + if (msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) + 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_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; + } + } + + 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: %p 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; + } + } + mutex_lock(&inst->pendingq.lock); + list_for_each_safe(ptr, next, &inst->pendingq.list) { + temp = list_entry(ptr, struct vb2_buf_entry, list); + rc = msm_comm_qbuf(temp->vb); + if (rc) { + dprintk(VIDC_ERR, + "Failed to qbuf to hardware\n"); + break; + } + list_del(&temp->list); + kfree(temp); + } + mutex_unlock(&inst->pendingq.lock); + return rc; +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: %p 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 = %p\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\n", q->type); + 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, "Q-type is not supported: %d\n", q->type); + rc = -EINVAL; + break; + } + 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 = %p\n", q); + return /*-EINVAL*/; + } + 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: %p, cap = %d to state: %d\n", + inst, q->type, MSM_VIDC_RELEASE_RESOURCES_DONE); + /*return rc;*/ +} + +static void msm_vdec_buf_queue(struct vb2_buffer *vb) +{ + int rc; + rc = msm_comm_qbuf(vb); + if (rc) + dprintk(VIDC_ERR, "Failed to queue buffer: %d\n", rc); +} + +int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec) +{ + int rc = 0; + struct msm_vidc_core *core; + + if (!dec || !inst || !inst->core) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return -EINVAL; + } + core = inst->core; + switch (dec->cmd) { + case V4L2_DEC_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, + "Failed to recover from session_error: %d\n", + rc); + } + rc = msm_comm_flush(inst, dec->flags); + if (rc) { + dprintk(VIDC_ERR, + "Failed to flush buffers: %d\n", rc); + } + break; + case V4L2_DEC_CMD_STOP: + if (core->state != VIDC_CORE_INVALID && + inst->state == MSM_VIDC_CORE_INVALID) { + rc = msm_comm_kill_session(inst); + if (rc) + dprintk(VIDC_ERR, + "Failed to recover from session_error: %d\n", + rc); + } + rc = msm_comm_release_scratch_buffers(inst, false); + if (rc) + dprintk(VIDC_ERR, + "Failed to release scratch buffers: %d\n", rc); + rc = msm_comm_release_persist_buffers(inst); + if (rc) + pr_err("Failed to release persist buffers: %d\n", rc); + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core %p in bad state, Sending CLOSE event\n", + core); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_CLOSE_DONE); + goto exit; + } + rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE); + /* Clients rely on this event for joining poll thread. + * This event should be returned even if firmware has + * failed to respond */ + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_CLOSE_DONE); + break; + default: + dprintk(VIDC_ERR, "Unknown Decoder Command\n"); + rc = -ENOTSUPP; + goto exit; + } + if (rc) { + dprintk(VIDC_ERR, "Failed to exec decoder cmd %d\n", dec->cmd); + goto exit; + } +exit: + return rc; +} + + +static const struct vb2_ops msm_vdec_vb2q_ops = { + .queue_setup = msm_vdec_queue_setup, + .buf_init = msm_vdec_buf_init, + .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 = %p\n", inst); + return -EINVAL; + } + inst->fmts[OUTPUT_PORT] = &vdec_formats[2]; + inst->fmts[CAPTURE_PORT] = &vdec_formats[0]; + 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.buffer_mode[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC; + inst->capability.buffer_mode[CAPTURE_PORT] = 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 = 30; + 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 check_tz_dynamic_buffer_support(void) +{ + int rc = 0; + int version = qcom_scm_get_feat_version(TZ_DYNAMIC_BUFFER_FEATURE_ID); + + /* + * if the version is < 1.1.0 then dynamic buffer allocation is + * not supported + */ + if (version < TZ_FEATURE_VERSION(1, 1, 0)) { + dprintk(VIDC_DBG, + "Dynamic buffer mode not supported, tz version is : %u vs required : %u\n", + version, TZ_FEATURE_VERSION(1, 1, 0)); + rc = -ENOTSUPP; + } + + return rc; +} + +static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) +{ + int rc = 0; + struct hfi_device *hdev; + struct hal_profile_level profile_level; + union hal_get_property hprop; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + hdev = inst->core->device; + dprintk(VIDC_DBG, "%s ctrl->id:%x\n", __func__, ctrl->id); + 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 Error rc:%d\n", __func__, rc); + return rc; + } + profile_level = hprop.profile_level; + ctrl->val = profile_level.profile; + dprintk(VIDC_DBG, "%s: PROFILE ctrl->id:%x ctrl->val:%d\n", + __func__, ctrl->id, ctrl->val); + 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 Error rc:%d\n", __func__, rc); + return rc; + } + profile_level = hprop.profile_level; + ctrl->val = profile_level.level; + dprintk(VIDC_DBG, "%s: LEVEL ctrl->id:%x ctrl->val:%d\n", + __func__, ctrl->id, ctrl->val); + break; + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE_SCALING_THRESHOLD: + if (!(inst->flags & VIDC_SECURE) || + !inst->capability.secure_output2_threshold.max) { + dprintk(VIDC_ERR, "%s id:%x invalid configuration\n", + __func__, ctrl->id); + rc = -EINVAL; + break; + } + dprintk(VIDC_DBG, + "Secure Scaling Threshold is : %d", + inst->capability.secure_output2_threshold.max); + ctrl->val = inst->capability.secure_output2_threshold.max; + break; + default: + dprintk(VIDC_DBG, "%s id:%x not supported\n", + __func__, ctrl->id); + break; + } + 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_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)", 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 hfi_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 */ \ + BUG_ON(1); \ + } \ + __temp; \ + }) + + 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 = + (0x00000001 << 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_ENABLE_PICTURE_TYPE: + property_id = + HAL_PARAM_VDEC_PICTURE_TYPE_DECODE; + enable_picture.picture_type = ctrl->val; + 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.buffer_mode[CAPTURE_PORT])) { + dprintk(VIDC_DBG, + "buffer mode[%d] not supported for Capture Port\n", + ctrl->val); + rc = -ENOTSUPP; + break; + } + + if (alloc_mode.buffer_mode == HAL_BUFFER_MODE_DYNAMIC && + (inst->flags & VIDC_SECURE) && + check_tz_dynamic_buffer_support()) { + 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); + 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); + 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); + 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: + inst->capability.buffer_size_limit = ctrl->val; + dprintk(VIDC_DBG, + "Limiting input buffer size to :%u\n", 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; + default: + break; + } +#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 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: %p 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: + if (rc) + dprintk(VIDC_ERR, "Failed setting control: %x (%s)", + ctrl->id, v4l2_ctrl_get_name(ctrl->id)); + 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: %p 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_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) +{ + return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl); +} +int msm_vdec_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) +{ + return v4l2_g_ctrl(&inst->ctrl_handler, ctrl); +} + +static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst, + int *size) +{ + int c = 0, sz = 0; + struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) * + NUM_CTRLS, GFP_KERNEL); + + if (!size || !cluster || !inst) + return NULL; + + for (c = 0; c < NUM_CTRLS; c++) + cluster[sz++] = inst->ctrls[c]; + + *size = sz; + return cluster; +} + +int msm_vdec_ctrl_init(struct msm_vidc_inst *inst) +{ + int idx = 0; + struct v4l2_ctrl_config ctrl_cfg = {0}; + int ret_val = 0; + int cluster_size = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s - invalid input\n", __func__); + return -EINVAL; + } + + inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * NUM_CTRLS, + 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(msm_vdec_ctrls[idx].id)) { + /*add private control*/ + ctrl_cfg.def = msm_vdec_ctrls[idx].default_value; + ctrl_cfg.flags = 0; + ctrl_cfg.id = msm_vdec_ctrls[idx].id; + /* ctrl_cfg.is_private = + * msm_vdec_ctrls[idx].is_private; + * ctrl_cfg.is_volatile = + * msm_vdec_ctrls[idx].is_volatile;*/ + ctrl_cfg.max = msm_vdec_ctrls[idx].maximum; + ctrl_cfg.min = msm_vdec_ctrls[idx].minimum; + ctrl_cfg.menu_skip_mask = + msm_vdec_ctrls[idx].menu_skip_mask; + ctrl_cfg.name = msm_vdec_ctrls[idx].name; + ctrl_cfg.ops = &msm_vdec_ctrl_ops; + ctrl_cfg.step = msm_vdec_ctrls[idx].step; + ctrl_cfg.type = msm_vdec_ctrls[idx].type; + ctrl_cfg.qmenu = msm_vdec_ctrls[idx].qmenu; + + ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler, + &ctrl_cfg, NULL); + } else { + if (msm_vdec_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) { + ctrl = v4l2_ctrl_new_std_menu( + &inst->ctrl_handler, + &msm_vdec_ctrl_ops, + msm_vdec_ctrls[idx].id, + msm_vdec_ctrls[idx].maximum, + msm_vdec_ctrls[idx].menu_skip_mask, + msm_vdec_ctrls[idx].default_value); + } else { + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, + &msm_vdec_ctrl_ops, + msm_vdec_ctrls[idx].id, + msm_vdec_ctrls[idx].minimum, + msm_vdec_ctrls[idx].maximum, + msm_vdec_ctrls[idx].step, + msm_vdec_ctrls[idx].default_value); + } + } + + if (!ctrl) { + dprintk(VIDC_ERR, "%s - invalid ctrl\n", __func__); + return -EINVAL; + } + + switch (msm_vdec_ctrls[idx].id) { + case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE: + case V4L2_CID_MPEG_VIDC_VIDEO_H263_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL: + case V4L2_CID_MPEG_VIDC_VIDEO_SECURE_SCALING_THRESHOLD: + ctrl->flags |= msm_vdec_ctrls[idx].flags; + break; + } + + ret_val = inst->ctrl_handler.error; + if (ret_val) { + dprintk(VIDC_ERR, + "Error adding ctrl (%s) to ctrl handle, %d\n", + msm_vdec_ctrls[idx].name, + inst->ctrl_handler.error); + return ret_val; + } + + inst->ctrls[idx] = ctrl; + } + + /* Construct a super cluster of all controls */ + inst->cluster = get_super_cluster(inst, &cluster_size); + if (!inst->cluster || !cluster_size) { + dprintk(VIDC_WARN, + "Failed to setup super cluster\n"); + return -EINVAL; + } + + v4l2_ctrl_cluster(cluster_size, inst->cluster); + + return ret_val; +} + +int msm_vdec_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; +} diff --git a/drivers/media/platform/msm/vidc/msm_vdec.h b/drivers/media/platform/msm/vidc/msm_vdec.h new file mode 100644 index 000000000000..c7f842700161 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vdec.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2012, 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 <media/msm_vidc.h> +#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_ctrl_deinit(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_ctrl(void *instance, struct v4l2_control *a); +int msm_vdec_g_ctrl(void *instance, struct v4l2_control *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/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c new file mode 100644 index 000000000000..4db8f9921440 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -0,0 +1,3771 @@ +/* Copyright (c) 2012-2015, 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 <linux/slab.h> +#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 160000000 +#define DEFAULT_BIT_RATE 64000 +#define BIT_RATE_STEP 100 +#define DEFAULT_FRAME_RATE 15 +#define MAX_SLICE_BYTE_SIZE 1024 +#define MIN_SLICE_BYTE_SIZE 1024 +#define MAX_SLICE_MB_SIZE 300 +#define I_FRAME_QP 26 +#define P_FRAME_QP 28 +#define B_FRAME_QP 30 +#define MAX_INTRA_REFRESH_MBS 300 +#define MAX_NUM_B_FRAMES 4 +#define MAX_LTR_FRAME_COUNT 10 + +#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", + 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 mpeg_video_vidc_extradata[] = { + "Extradata none", + "Extradata MB Quantization", + "Extradata Interlace Video", + "Extradata VC1 Framedisp", + "Extradata VC1 Seqdisp", + "Extradata timestamp", + "Extradata S3D Frame Packing", + "Extradata Frame Rate", + "Extradata Panscan Window", + "Extradata Recovery point SEI", + "Extradata Closed Caption UD", + "Extradata AFD UD", + "Extradata Multislice info", + "Extradata number of concealed MB", + "Extradata metadata filler", + "Extradata input crop", + "Extradata digital zoom", + "Extradata aspect ratio", + "Extradata macroblock metadata", +}; + +static const char *const perf_level[] = { + "Nominal", + "Performance", + "Turbo" +}; + +static const char *const intra_refresh_modes[] = { + "None", + "Cyclic", + "Adaptive", + "Cyclic Adaptive", + "Random" +}; + +static const char *const timestamp_mode[] = { + "Honor", + "Ignore", +}; + +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_CBR_CFR, + .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) + ), + .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, + .step = 0, + .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, + .step = 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, + .step = 1, + .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, + .step = 1, + .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, + .step = 1, + .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, + .step = 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, + .step = 0, + .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_6_2, + .default_value = + V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1, + .step = 0, + .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_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, + .step = 0, + .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 = "I Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = I_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + .name = "P Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = P_FRAME_QP, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + .name = "B Frame Quantization", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = B_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_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_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, + .step = 1, + .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_RANDOM, + .default_value = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE, + .step = 0, + .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_RANDOM) + ), + .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_DISABLED, + .maximum = L_MODE, + .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED, + .step = 1, + .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, + .step = 1, + .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_METADATA_MBI, + .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) | + (1 << 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) + ), + .qmenu = mpeg_video_vidc_extradata, + .step = 0, + }, + { + .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_H264_AU_DELIMITER, + .name = "H264 AU Delimiter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED, + .maximum = V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED, + .step = 1, + .default_value = + V4L2_MPEG_VIDC_VIDEO_H264_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, + .step = 0, + }, + { + .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_PERIODIC, + .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 = 3, + .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_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, + }, +}; + +#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_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 = "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 int msm_venc_queue_setup(struct vb2_queue *q, + const struct v4l2_format *fmt, + unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + void *alloc_ctxs[]) +{ + int i, 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; + struct v4l2_ctrl *ctrl = NULL; + u32 extradata = 0, 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) { + *num_buffers = buff_req->buffer_count_actual = + max(*num_buffers, buff_req->buffer_count_actual); + } + + 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); + } + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA); + if (ctrl) + extradata = v4l2_ctrl_g_ctrl(ctrl); + if (extradata != V4L2_MPEG_VIDC_EXTRADATA_NONE) + *num_planes = *num_planes + 1; + inst->fmts[CAPTURE_PORT]->num_planes = *num_planes; + + for (i = 0; i < *num_planes; i++) { + int extra_idx = EXTRADATA_IDX(*num_planes); + + sizes[i] = inst->fmts[CAPTURE_PORT]->get_frame_size( + i, inst->prop.height[CAPTURE_PORT], + inst->prop.width[CAPTURE_PORT]); + + 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; + } + } + + property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; + new_buf_count.buffer_type = HAL_BUFFER_OUTPUT; + new_buf_count.buffer_count_actual = *num_buffers; + new_buf_count.buffer_count_actual += + msm_dcvs_get_extra_buff_count(inst, false); + rc = call_hfi_op(hdev, session_set_property, inst->session, + property_id, &new_buf_count); + if (!rc) + msm_dcvs_set_buff_req_handled(inst, false); + + 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_actual); + + property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; + new_buf_count.buffer_type = HAL_BUFFER_INPUT; + new_buf_count.buffer_count_actual = *num_buffers; + + ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA); + if (ctrl) + extradata = v4l2_ctrl_g_ctrl(ctrl); + if (extradata == V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP) + *num_planes = *num_planes + 1; + inst->fmts[OUTPUT_PORT]->num_planes = *num_planes; + new_buf_count.buffer_count_actual += + msm_dcvs_get_extra_buff_count(inst, true); + rc = call_hfi_op(hdev, session_set_property, inst->session, + property_id, &new_buf_count); + if (!rc) + msm_dcvs_set_buff_req_handled(inst, true); + + 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 && + inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_H264) + 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 int set_bitrate_for_each_layer(struct msm_vidc_inst *inst, + u32 num_enh_layers, u32 total_bitrate) +{ + u32 property_id = 0; + int i = 0, rc = 0; + struct hfi_device *hdev = NULL; + struct hal_bitrate bitrate; + int bitrate_table[3][4] = { + {50, 50, 0, 0}, + {34, 33, 33, 0}, + {25, 25, 25, 25} + }; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s - invalid input\n", __func__); + return -EINVAL; + } + + if (!num_enh_layers || num_enh_layers > ARRAY_SIZE(bitrate_table)) { + dprintk(VIDC_ERR, "%s - invalid number of enh layers: %d\n", + __func__, num_enh_layers); + return -EINVAL; + } + hdev = inst->core->device; + + for (i = 0; !rc && i <= num_enh_layers; i++) { + property_id = HAL_CONFIG_VENC_TARGET_BITRATE; + bitrate.bit_rate = (u32)((total_bitrate * + bitrate_table[num_enh_layers - 1][i]) / 100); + bitrate.layer_id = i; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, &bitrate); + } + return rc; +} + +static inline int start_streaming(struct msm_vidc_inst *inst) +{ + int rc = 0; + struct vb2_buf_entry *temp; + struct list_head *ptr, *next; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + 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: %p to start done state\n", inst); + goto fail_start; + } + msm_dcvs_init_load(inst); + mutex_lock(&inst->pendingq.lock); + list_for_each_safe(ptr, next, &inst->pendingq.list) { + temp = list_entry(ptr, struct vb2_buf_entry, list); + rc = msm_comm_qbuf(temp->vb); + if (rc) { + dprintk(VIDC_ERR, + "Failed to qbuf to hardware\n"); + break; + } + list_del(&temp->list); + kfree(temp); + } + mutex_unlock(&inst->pendingq.lock); + return rc; +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 = %p\n", q); + return -EINVAL; + } + inst = q->drv_priv; + dprintk(VIDC_DBG, "Streamon 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 = 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, "Q-type is not supported: %d\n", q->type); + rc = -EINVAL; + break; + } + 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, "Invalid input, q = %p\n", q); + return /*-EINVAL*/; + } + 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: %p, cap = %d to state: %d\n", + inst, q->type, MSM_VIDC_CLOSE_DONE); + /*return rc;*/ +} + +static void msm_venc_buf_queue(struct vb2_buffer *vb) +{ + int rc; + rc = msm_comm_qbuf(vb); + if (rc) + dprintk(VIDC_ERR, "Failed to queue buffer: %d\n", rc); +} + +static int msm_venc_buf_init(struct vb2_buffer *vb) +{ + int rc = 0, i = 0; + struct vb2_queue *q = vb->vb2_queue; + struct msm_vidc_inst *inst = q->drv_priv; + struct buffer_info* binfo = NULL; + struct vidc_buffer_addr_info buffer_info; + enum hal_buffer buffer_type = HAL_BUFFER_INPUT; + struct dma_buf* dbuf; + struct hfi_device *hdev; + + dprintk(VIDC_DBG, "msm_venc_buf_init \n"); + if (q->memory == V4L2_MEMORY_USERPTR || + q->memory == V4L2_MEMORY_DMABUF) { + dprintk(VIDC_DBG, + "Ignore buf init for userptr & DMABUF modes"); + return rc; + } + if (!inst) { + return -EINVAL; + } + + hdev = inst->core->device; + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + return rc; + } + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + buffer_type = HAL_BUFFER_INPUT; + else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + buffer_type = HAL_BUFFER_OUTPUT; + + for (i = 0; i < vb->num_planes; ++i) { + if (vb->vb2_queue->mem_ops->get_dmabuf != NULL) { + dbuf = vb->vb2_queue->mem_ops->get_dmabuf( + vb->planes[i].mem_priv, 0); + } + else { + dprintk(VIDC_ERR, "get_dmabuf is not supported \n"); + rc = -EINVAL; + goto free_binfo; + } + binfo->handle[i] = msm_smem_map_dma_buf( + inst->mem_client, dbuf, buffer_type); + binfo->mapped[i] = true; + binfo->device_addr[i] = binfo->handle[i]->device_addr; + binfo->size[i] = vb->v4l2_planes[i].length; + dprintk(VIDC_DBG, "%s: device_addr 0x %pa, index %d, plane %d, size %d", + __func__, &binfo->device_addr[i], vb->v4l2_buf.index, i, binfo->size[i]); + } + binfo->num_planes = vb->num_planes; + binfo->memory = q->memory; + binfo->v4l2_index = vb->v4l2_buf.index; + binfo->type = q->type; + + mutex_lock(&inst->registeredbufs.lock); + list_add_tail(&binfo->list, &inst->registeredbufs.list); + mutex_unlock(&inst->registeredbufs.lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + //set buffer to FW, TODO move to dymaic buf mode + buffer_info.buffer_size = vb->v4l2_planes[0].length; + buffer_info.buffer_type = + msm_comm_get_hal_output_buffer(inst); + buffer_info.num_buffers = 1; + buffer_info.align_device_addr = binfo->device_addr[0]; + 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"); + } + + return rc; +free_binfo: + kfree(binfo); + return rc; +} + +static const struct vb2_ops msm_venc_vb2q_ops = { + .queue_setup = msm_venc_queue_setup, + .buf_init = msm_venc_buf_init, + .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; + } + } + +unknown_value: + dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value); + return -EINVAL; +} + +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; + struct hal_venc_perf_mode venc_mode; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + + /* 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 */ \ + BUG_ON(1); \ + } \ + __temp; \ + }) + + 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) { + dprintk(VIDC_ERR, "Control %#x only valid for H264\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; + + 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; + + if (num_b) { + u32 max_num_b_frames = MAX_NUM_B_FRAMES; + 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; + 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; + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR: + case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR: + update_ctrl.val = + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + } + + 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: + { + struct v4l2_ctrl *hier_p = TRY_GET_CTRL( + V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS); + + bitrate.layer_id = 0; + if (hier_p->val && + inst->fmts[CAPTURE_PORT]->fourcc == + V4L2_PIX_FMT_H264) { + rc = set_bitrate_for_each_layer(inst, + hier_p->val, ctrl->val); + if (rc) { + dprintk(VIDC_ERR, + "failed to set bitrate for multiple layers\n"); + rc = -EINVAL; + } + } else { + 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; + + 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; + + 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; + + 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_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_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_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 valid "\ + "only for H264 encoder and MB based slicing\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; + 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); + + property_id = + HAL_PARAM_VENC_INTRA_REFRESH; + + 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 = 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 = 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 = 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 = 0; + break; + case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_I_FRAME: + enable.enable = 1; + 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_H264_AU_DELIMITER: + property_id = HAL_PARAM_VENC_H264_GENERATE_AUDNAL; + + switch (ctrl->val) { + case V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED: + enable.enable = 0; + break; + case V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED: + enable.enable = 1; + 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: + inst->flags |= VIDC_NOMINAL; + 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 = 1; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED: + default: + enable.enable = 0; + 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 = (1 << ctrl->val); + use_ltr.use_constraint = false; + use_ltr.frames = 0; + pdata = &use_ltr; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME: + 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; + rc = msm_venc_toggle_hier_p(inst, ctrl->val); + if (rc) + break; + if (hier_p_layers > inst->capability.hier_p.max) { + dprintk(VIDC_ERR, + "Error setting hier p num layers = %d max supported by f/w = %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; + venc_mode.mode = ctrl->val; + pdata = &venc_mode; + msm_dcvs_enc_set_power_save_mode(inst, + venc_mode.mode == + 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; + default: + dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id); + rc = -ENOTSUPP; + break; + } +#undef TRY_GET_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; +} + +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_core_capability *cap = NULL; + struct hal_initial_quantization quant; + + 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_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; + 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: %p 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 = %p\n", inst); + return -EINVAL; + } + inst->fmts[CAPTURE_PORT] = &venc_formats[2]; + inst->fmts[OUTPUT_PORT] = &venc_formats[0]; + 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->prop.fps = 15; + inst->capability.pixelprocess_capabilities = 0; + inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC; + inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC; + return rc; +} + +int msm_venc_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) +{ + return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl); +} +int msm_venc_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl) +{ + return v4l2_g_ctrl(&inst->ctrl_handler, ctrl); +} + +int msm_venc_s_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *ctrl) +{ + int rc = 0; + if (ctrl->ctrl_class != V4L2_CTRL_CLASS_MPEG) { + dprintk(VIDC_ERR, "Invalid Class set for extended control\n"); + return -EINVAL; + } + rc = try_set_ext_ctrl(inst, ctrl); + if (rc) { + dprintk(VIDC_ERR, "Error setting extended control\n"); + return rc; + } + return rc; +} + +int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc) +{ + int rc = 0; + struct msm_vidc_core *core; + core = inst->core; + switch (enc->cmd) { + case V4L2_ENC_QCOM_CMD_FLUSH: + rc = msm_comm_flush(inst, enc->flags); + break; + case V4L2_ENC_CMD_STOP: + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID) { + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_CLOSE_DONE); + return rc; + } + rc = msm_comm_release_scratch_buffers(inst, false); + if (rc) + dprintk(VIDC_ERR, "Failed to release scratch buf:%d\n", + rc); + rc = msm_comm_release_persist_buffers(inst); + if (rc) + dprintk(VIDC_ERR, "Failed to release persist buf:%d\n", + rc); + rc = msm_comm_try_state(inst, MSM_VIDC_CLOSE_DONE); + /* Clients rely on this event for joining poll thread. + * This event should be returned even if firmware has + * failed to respond */ + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_CLOSE_DONE); + break; + } + if (rc) + dprintk(VIDC_ERR, + "Command: %d failed with rc = %d\n", enc->cmd, 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 = %p, cap = %p\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 | + V4L2_CAP_DEVICE_CAPS; + cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; + 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 = %p, f = %p\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; +} + +int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a) +{ + u32 property_id = 0; + u64 us_per_frame = 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) { + 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 = USEC_PER_SEC; + do_div(fps, us_per_frame); + + if (fps % 15 == 14 || fps % 24 == 23) + fps = fps + 1; + else if (fps % 24 == 1 || fps % 15 == 1) + fps = fps - 1; + + if (inst->prop.fps != fps) { + dprintk(VIDC_PROF, "reported fps changed for %p: %d->%d\n", + inst, inst->prop.fps, fps); + inst->prop.fps = fps; + frame_rate.frame_rate = inst->prop.fps * (0x1<<16); + frame_rate.buffer_type = HAL_BUFFER_OUTPUT; + pdata = &frame_rate; + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, pdata); + + if (rc) { + dprintk(VIDC_WARN, + "Failed to set frame rate %d\n", rc); + } + + msm_comm_scale_clocks_and_bus(inst); + } +exit: + return rc; +} + +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 = %p, format = %p\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 (msm_vidc_vpe_csc_601_to_709) { + msm_venc_set_csc(inst); + } + + 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; + } + + 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; + } + + 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 hal property for framesize\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; + } + + 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; + } + + inst->fmts[fmt->type] = fmt; + f->fmt.pix_mp.num_planes = fmt->num_planes; + for (i = 0; i < fmt->num_planes; ++i) { + f->fmt.pix_mp.plane_fmt[i].sizeimage = fmt->get_frame_size(i, + f->fmt.pix_mp.height, f->fmt.pix_mp.width); + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + struct hal_frame_size frame_sz = {0}; + + rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE); + if (rc) { + dprintk(VIDC_ERR, "Failed to open instance\n"); + goto exit; + } + + 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; + } + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct hal_buffer_requirements *bufreq = NULL; + int extra_idx = 0; + + extra_idx = EXTRADATA_IDX(fmt->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; + unsigned int extra_idx = 0; + struct hal_buffer_requirements *bufreq = NULL; + + if (!inst || !f) { + dprintk(VIDC_ERR, + "Invalid input, inst = %p, format = %p\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]; + } 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]; + } 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 = fmt->num_planes; + for (i = 0; i < fmt->num_planes; ++i) { + f->fmt.pix_mp.plane_fmt[i].sizeimage = + fmt->get_frame_size(i, height, width); + } + + extra_idx = EXTRADATA_IDX(fmt->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 < fmt->num_planes; ++i) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + inst->bufq[OUTPUT_PORT].vb2_bufq.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].vb2_bufq.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 = %p, buffer = %p\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 %p 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: %p 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; +} + +static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst, + int *size) +{ + int c = 0, sz = 0; + struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) * + NUM_CTRLS, GFP_KERNEL); + + if (!size || !cluster || !inst) + return NULL; + + for (c = 0; c < NUM_CTRLS; c++) + cluster[sz++] = inst->ctrls[c]; + + *size = sz; + return cluster; +} + +int msm_venc_ctrl_init(struct msm_vidc_inst *inst) +{ + int idx = 0; + struct v4l2_ctrl_config ctrl_cfg = {0}; + int ret_val = 0; + int cluster_size = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s - invalid input\n", __func__); + return -EINVAL; + } + + inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * NUM_CTRLS, + 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(msm_venc_ctrls[idx].id)) { + ctrl_cfg.def = msm_venc_ctrls[idx].default_value; + ctrl_cfg.flags = 0; + ctrl_cfg.id = msm_venc_ctrls[idx].id; + ctrl_cfg.max = msm_venc_ctrls[idx].maximum; + ctrl_cfg.min = msm_venc_ctrls[idx].minimum; + ctrl_cfg.menu_skip_mask = + msm_venc_ctrls[idx].menu_skip_mask; + ctrl_cfg.name = msm_venc_ctrls[idx].name; + ctrl_cfg.ops = &msm_venc_ctrl_ops; + ctrl_cfg.step = msm_venc_ctrls[idx].step; + ctrl_cfg.type = msm_venc_ctrls[idx].type; + ctrl_cfg.qmenu = msm_venc_ctrls[idx].qmenu; + ctrl = v4l2_ctrl_new_custom( + &inst->ctrl_handler, + &ctrl_cfg, NULL); + } else { + if (msm_venc_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) { + ctrl = v4l2_ctrl_new_std_menu( + &inst->ctrl_handler, + &msm_venc_ctrl_ops, + msm_venc_ctrls[idx].id, + msm_venc_ctrls[idx].maximum, + msm_venc_ctrls[idx].menu_skip_mask, + msm_venc_ctrls[idx].default_value); + } else { + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, + &msm_venc_ctrl_ops, + msm_venc_ctrls[idx].id, + msm_venc_ctrls[idx].minimum, + msm_venc_ctrls[idx].maximum, + msm_venc_ctrls[idx].step, + msm_venc_ctrls[idx].default_value); + } + } + + ret_val = inst->ctrl_handler.error; + if (ret_val) { + dprintk(VIDC_ERR, + "Error adding ctrl (%s) to ctrl handle, %d\n", + msm_venc_ctrls[idx].name, + inst->ctrl_handler.error); + return ret_val; + } + + inst->ctrls[idx] = ctrl; + } + + /* Construct a super cluster of all controls */ + inst->cluster = get_super_cluster(inst, &cluster_size); + if (!inst->cluster || !cluster_size) { + dprintk(VIDC_WARN, + "Failed to setup super cluster\n"); + return -EINVAL; + } + + v4l2_ctrl_cluster(cluster_size, inst->cluster); + + return ret_val; +} + +int msm_venc_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; +} diff --git a/drivers/media/platform/msm/vidc/msm_venc.h b/drivers/media/platform/msm/vidc/msm_venc.h new file mode 100644 index 000000000000..07afd90cf071 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_venc.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2012-2014, 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 <media/msm_vidc.h> +#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_ctrl_deinit(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_ctrl(void *instance, struct v4l2_control *a); +int msm_venc_g_ctrl(void *instance, struct v4l2_control *a); +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/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c new file mode 100644 index 000000000000..eb869d8dfdf0 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -0,0 +1,1521 @@ +/* Copyright (c) 2012-2015, 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 <linux/sched.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <media/videobuf2-dma-contig.h> +#include <media/msm_vidc.h> +#include "msm_vidc_internal.h" +#include "msm_vidc_debug.h" +#include "msm_vdec.h" +#include "msm_venc.h" +#include "msm_vidc_common.h" +#include "vidc_hfi_api.h" +#include "msm_vidc_dcvs.h" + +#define MAX_EVENTS 30 + +/* Offset base for buffers on the destination queue - used to distinguish + * between source and destination buffers when mmapping - they receive the same + * offsets but for different queues */ +#define DST_QUEUE_OFF_BASE (1 << 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) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !cap) + return -EINVAL; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_querycap(instance, cap); + else if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_querycap(instance, cap); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_querycap); + +int msm_vidc_s_parm(void *instance, + struct v4l2_streamparm *a) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !a) + return -EINVAL; + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_s_parm(instance, a); + else if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_s_parm(instance, a); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_s_parm); + +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; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_s_ctrl(instance, control); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_s_ctrl(instance, control); + return -EINVAL; +} +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; + + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_g_ctrl(instance, control); + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_g_ctrl(instance, control); + return -EINVAL; +} +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_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_mmap_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b, int *plane) +{ + struct buffer_info *temp; + struct buffer_info *ret = NULL; + + if (!inst || !b) { + dprintk(VIDC_ERR, "Invalid input\n"); + return NULL; + } + + *plane = 0; + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(temp, &inst->registeredbufs.list, list) { + if (temp && temp->v4l2_index == b->index + && temp->type == b->type) { + ret = temp; + *plane = 0; + //TODO: Add for loop for plane no + break; + } + } + mutex_unlock(&inst->registeredbufs.lock); + return ret; +} + +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; + ion_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; + } + + *plane = 0; + 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 fd_matches = false; + 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 (b->memory == V4L2_MEMORY_DMABUF) + fd_matches = fd == temp->fd[i]; + + if ((fd_matches || device_addr_matches) && + (contains_within || overlaps)) { + dprintk(VIDC_DBG, + "This memory region is already mapped\n"); + dprintk(VIDC_DBG, "**sachins - fd_matches %d, device_addr_matches %d, contains_within %d, overlaps %d\n", + fd_matches, device_addr_matches, contains_within, overlaps); + ret = temp; + *plane = i; + break; + } + } + if (ret) + break; + } + mutex_unlock(&inst->registeredbufs.lock); +err_invalid_input: + return ret; +} + +static struct msm_smem *get_same_fd_buffer(struct msm_vidc_list *buf_list, + int fd) +{ + struct buffer_info *temp; + struct msm_smem *same_fd_handle = NULL; + + int i; + + if (!fd) + return NULL; + + if (!buf_list || fd < 0) { + dprintk(VIDC_ERR, "Invalid input\n"); + 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->fd[i] == fd && + temp->handle[i] && temp->mapped[i]) { + temp->same_fd_ref[i]++; + dprintk(VIDC_INFO, + "Found same fd buffer\n"); + same_fd_handle = temp->handle[i]; + break; + } + } + if (same_fd_handle) + break; + } + mutex_unlock(&buf_list->lock); + +err_invalid_input: + return same_fd_handle; +} + +struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list, + ion_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: %p\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) +{ + 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 struct msm_smem *map_buffer(struct msm_vidc_inst *inst, + struct v4l2_plane *p, enum hal_buffer buffer_type) +{ + struct msm_smem *handle = NULL; + handle = msm_comm_smem_user_to_kernel(inst, + p->reserved[0], + p->reserved[1], + buffer_type); + if (!handle) { + dprintk(VIDC_ERR, + "%s: Failed to get device buffer address\n", __func__); + return NULL; + } + if (msm_comm_smem_cache_operations(inst, handle, + SMEM_CACHE_CLEAN)) + dprintk(VIDC_WARN, + "CACHE Clean failed: %d, %d, %d\n", + p->reserved[0], + p->reserved[1], + p->length); + return handle; +} + +static inline enum hal_buffer get_hal_buffer_type( + struct msm_vidc_inst *inst, struct v4l2_buffer *b) +{ + if (inst->session_type == MSM_VIDC_DECODER) { + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return HAL_BUFFER_INPUT; + else /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ + return HAL_BUFFER_OUTPUT; + } else { + /* FIXME in the future. See comment in msm_comm_get_\ + * domain_partition. Same problem here. */ + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return HAL_BUFFER_OUTPUT; + else /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ + return HAL_BUFFER_INPUT; + } + 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 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); + } +} + +static 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; + } + + if (b->memory == V4L2_MEMORY_MMAP) { + dprintk(VIDC_DBG, "[MAP] Create binfo = %p mem_offset = %#x type = %d, length = %d\n", + binfo, b->m.planes[0].m.mem_offset, b->type, b->length); + } else { + dprintk(VIDC_DBG, "[MAP] Create binfo = %p fd = %d type = %d, length = %d\n", + binfo, b->m.planes[0].reserved[0], b->type, b->length); + } + + for (i = 0; i < b->length; ++i) { + if (EXTRADATA_IDX(b->length) && + (i == EXTRADATA_IDX(b->length)) && + !b->m.planes[i].length) { + continue; + } + mutex_lock(&inst->sync_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 = -EINVAL; + } + + 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; + mutex_lock(&inst->registeredbufs.lock); + list_for_each_entry(iterator, + &inst->registeredbufs.list, list) { + if (iterator == temp) { + rc = buf_ref_get(inst, temp); + if (rc > 0) { + save_v4l2_buffer(b, temp); + rc = -EEXIST; + } + break; + } + } + mutex_unlock(&inst->registeredbufs.lock); + } + mutex_unlock(&inst->sync_lock); + if (rc < 0) + goto exit; + + same_fd_handle = get_same_fd_buffer( + &inst->registeredbufs, + 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->handle[i] = same_fd_handle; + } else { + if (inst->map_output_buffer) { + binfo->handle[i] = + map_buffer(inst, &b->m.planes[i], + get_hal_buffer_type(inst, b)); + if (!binfo->handle[i]) { + rc = -EINVAL; + goto exit; + } + binfo->mapped[i] = true; + binfo->device_addr[i] = + binfo->handle[i]->device_addr + + binfo->buff_off[i]; + b->m.planes[i].m.userptr = + binfo->device_addr[i]; + } else { + binfo->device_addr[i] = + b->m.planes[i].m.userptr; + } + } + /* 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 = %p, handle[%d] = %p, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n", + __func__, binfo, i, binfo->handle[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: %p %p\n", + __func__, inst, binfo); + return -EINVAL; + } + + mutex_lock(&inst->registeredbufs.lock); + + /* + * 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 = %p, handle[%d] = %p, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n", + __func__, temp, i, temp->handle[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->handle[i] && temp->mapped[i] && + !temp->same_fd_ref[i]) { + msm_comm_smem_free(inst, + temp->handle[i]); + } + + if (temp->same_fd_ref[i]) + keep_node = true; + else { + temp->fd[i] = 0; + temp->handle[i] = 0; + temp->device_addr[i] = 0; + temp->uvaddr[i] = 0; + } + } + if (!keep_node) { + dprintk(VIDC_DBG, "[UNMAP] AND-FREED binfo: %p\n", temp); + list_del(&temp->list); + kfree(temp); + } else { + temp->inactive = true; + dprintk(VIDC_DBG, "[UNMAP] NOT-FREED binfo: %p\n", temp); + } +exit: + mutex_unlock(&inst->registeredbufs.lock); + 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: %p\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 output_buffer_cache_invalidate(struct msm_vidc_inst *inst, + struct buffer_info *binfo) +{ + int i = 0; + int rc = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid inst: %p\n", __func__, inst); + return -EINVAL; + } + + if (!inst->map_output_buffer) + return 0; + + if (!binfo) { + dprintk(VIDC_ERR, "%s: invalid buffer info: %p\n", + __func__, inst); + return -EINVAL; + } + + if (binfo->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return 0; + + + for (i = 0; i < binfo->num_planes; i++) { + if (binfo->handle[i]) { + rc = msm_comm_smem_cache_operations(inst, + binfo->handle[i], SMEM_CACHE_INVALIDATE); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to clean caches: %d\n", + __func__, rc); + return -EINVAL; + } + } else + dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n", + __func__, i); + } + return 0; +} + +int msm_vidc_mmap(void* instance, struct vm_area_struct *vma) +{ + int rc = 0; + struct msm_vidc_inst *inst = instance; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + + if (!inst) + return -EINVAL; + + if (offset < DST_QUEUE_OFF_BASE) { + dprintk(VIDC_DBG, "mmaping output plane\n"); + rc = vb2_mmap(&inst->bufq[OUTPUT_PORT].vb2_bufq, vma); + } else { /* capture */ + dprintk(VIDC_DBG, "mmaping capture plane\n"); + vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); + rc = vb2_mmap(&inst->bufq[CAPTURE_PORT].vb2_bufq, vma); + } + + return rc; +} + +int msm_vidc_querybuf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + int i = 0, rc = 0; + + if (!inst || !b) + return -EINVAL; + + if (b->memory != V4L2_MEMORY_MMAP) { + dprintk(VIDC_ERR, "Only MMAP bufs can be queried\n"); + return -EINVAL; + } + + dprintk(VIDC_DBG, "Before QueryBuf offset 0x%x for buffer index %d \n", + b->m.planes[0].m.mem_offset, b->index); + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + dprintk(VIDC_DBG, "querybuf on output port \n"); + vb2_querybuf(&inst->bufq[OUTPUT_PORT].vb2_bufq, b); + } else if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + dprintk(VIDC_DBG, "querybuf on capture port \n"); + vb2_querybuf(&inst->bufq[CAPTURE_PORT].vb2_bufq, b); + for (i = 0; i < b->length; i++) { + b->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE; + } + } + return rc; +} + +int msm_vidc_exportbuf(void *instance, struct v4l2_exportbuffer *b) +{ + struct msm_vidc_inst *inst = instance; + int rc = 0; + + if (!inst || !b) + return -EINVAL; + + if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + dprintk(VIDC_DBG, "exportbuf on capture port \n"); + rc = vb2_expbuf(&inst->bufq[CAPTURE_PORT].vb2_bufq, b); + } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + dprintk(VIDC_DBG, "exportbuf on output port \n"); + rc = vb2_expbuf(&inst->bufq[OUTPUT_PORT].vb2_bufq, b); + } + return rc; +} + +int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b) +{ + struct msm_vidc_inst *inst = instance; + + if (!inst || !b) + return -EINVAL; + + if (!V4L2_TYPE_IS_MULTIPLANAR(b->type) || !b->length || + (b->length > VIDEO_MAX_PLANES)) { + dprintk(VIDC_ERR, "%s: wrong input params\n", + __func__); + return -EINVAL; + } + + if (is_dynamic_output_buffer_mode(b, inst)) + return 0; + + /* Map the buffer only for non-kernel clients */ + /* + * TODO: We don't have any kernel clients anymore. Reconsider deleting + * the conditional once we confirm that no userspace client sends + * reserved[0] == 0 + */ + if (b->m.planes[0].reserved[0]) { + inst->map_output_buffer = true; + 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) { + rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move inst: %p 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]; + if (bi->memory == V4L2_MEMORY_USERPTR) + 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->handle[i] && bi->mapped[i]) { + dprintk(VIDC_DBG, + "%s: [UNMAP] binfo = %p, handle[%d] = %p, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n", + __func__, bi, i, bi->handle[i], + &bi->device_addr[i], bi->fd[i], + bi->buff_off[i], bi->mapped[i]); + msm_comm_smem_free(inst, + bi->handle[i]); + } + } + kfree(bi); + } + } + mutex_unlock(&inst->registeredbufs.lock); + return rc; +} +EXPORT_SYMBOL(msm_vidc_release_buffers); + +int msm_vidc_encoder_cmd(void *instance, struct v4l2_encoder_cmd *enc) +{ + struct msm_vidc_inst *inst = instance; + if (!inst || !inst->core || !enc) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return -EINVAL; + } + if (inst->session_type == MSM_VIDC_ENCODER) + return msm_venc_cmd(instance, enc); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_encoder_cmd); + +int msm_vidc_decoder_cmd(void *instance, struct v4l2_decoder_cmd *dec) +{ + struct msm_vidc_inst *inst = instance; + if (!inst || !inst->core || !dec) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return -EINVAL; + } + if (inst->session_type == MSM_VIDC_DECODER) + return msm_vdec_cmd(instance, dec); + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_decoder_cmd); + +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 || !b) + return -EINVAL; + + if (!V4L2_TYPE_IS_MULTIPLANAR(b->type) || !b->length || + (b->length > VIDEO_MAX_PLANES)) { + dprintk(VIDC_ERR, "%s: wrong input params\n", + __func__); + return -EINVAL; + } + + if (is_dynamic_output_buffer_mode(b, inst)) { + if (b->m.planes[0].reserved[0]) + inst->map_output_buffer = true; + + rc = map_and_register_buf(inst, b); + if (rc == -EEXIST) + return 0; + if (rc) + return rc; + } + + if (b->memory == V4L2_MEMORY_MMAP) { + dprintk(VIDC_DBG, "no cache opsi, mmap"); + goto do_qbuf; + } + + for (i = 0; i < b->length; ++i) { + if (!inst->map_output_buffer) + continue; + if (EXTRADATA_IDX(b->length) && + (i == EXTRADATA_IDX(b->length)) && + !b->m.planes[i].length) { + b->m.planes[i].m.userptr = 0; + continue; + } + + binfo = get_registered_buf(inst, b, i, &plane); + 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; + } + if (b->memory == V4L2_MEMORY_USERPTR) + b->m.planes[i].m.userptr = binfo->device_addr[i]; + dprintk(VIDC_DBG, "Queueing device address = %pa\n", + &binfo->device_addr[i]); + + if (inst->fmts[OUTPUT_PORT]->fourcc == + V4L2_PIX_FMT_HEVC_HYBRID && binfo->handle[i] && + b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + rc = msm_comm_smem_cache_operations(inst, + binfo->handle[i], SMEM_CACHE_INVALIDATE); + if (rc) { + dprintk(VIDC_ERR, + "Failed to inv caches: %d\n", rc); + goto err_invalid_buff; + } + } + + if (binfo->handle[i] && + (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) { + rc = msm_comm_smem_cache_operations(inst, + binfo->handle[i], SMEM_CACHE_CLEAN); + if (rc) { + dprintk(VIDC_ERR, + "Failed to clean caches: %d\n", rc); + goto err_invalid_buff; + } + } + } + +do_qbuf: + 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) + return -EINVAL; + + if (!V4L2_TYPE_IS_MULTIPLANAR(b->type) || !b->length || + (b->length > VIDEO_MAX_PLANES)) { + dprintk(VIDC_ERR, "%s: wrong input params\n", + __func__); + 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; + + if (b->memory == V4L2_MEMORY_MMAP) { + dprintk(VIDC_DBG, "Ignore cache ops for MMAP buffers"); + return rc; + } + + for (i = 0; i < b->length; i++) { + if (!inst->map_output_buffer) + continue; + if (EXTRADATA_IDX(b->length) && + (i == EXTRADATA_IDX(b->length)) && + !b->m.planes[i].m.userptr) { + 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]; + if (!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 && inst->map_output_buffer) { + dprintk(VIDC_ERR, + "%s: error - no buffer info found in registered list\n", + __func__); + return -EINVAL; + } + + if (is_dynamic_output_buffer_mode(b, inst)) { + if (!buffer_info) + return -EINVAL; + + buffer_info->dequeued = true; + + dprintk(VIDC_DBG, "[DEQUEUED]: fd[0] = %d\n", + buffer_info->fd[0]); + rc = unmap_and_deregister_buf(inst, buffer_info); + } else + rc = output_buffer_cache_invalidate(inst, buffer_info); + + 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_core_capability *capability = NULL; + + if (!inst || !fsize) { + dprintk(VIDC_ERR, "%s: invalid parameter: %p %p\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(void *alloc_ctx, 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->io_flags = 0; + 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->mem_ops = &vb2_dma_contig_memops; + q->drv_priv = inst; + return vb2_queue_init(q); +} + +static int setup_event_queue(void *inst, + struct video_device *pvdev) +{ + int rc = 0; + 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 rc; +} + +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); + +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: %p, %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->internalbufs); + 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); + + inst->session_type = session_type; + inst->state = MSM_VIDC_CORE_UNINIT_DONE; + inst->core = core; + inst->map_output_buffer = false; + + for (i = SESSION_MSG_INDEX(SESSION_MSG_START); + i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) { + init_completion(&inst->completions[i]); + } + inst->mem_client = msm_smem_new_client(SMEM_DMA, + &inst->core->resources); + if (!inst->mem_client) { + dprintk(VIDC_ERR, "Failed to create memory client\n"); + goto fail_mem_client; + } + if (session_type == MSM_VIDC_DECODER) { + msm_vdec_inst_init(inst); + msm_vdec_ctrl_init(inst); + } else if (session_type == MSM_VIDC_ENCODER) { + msm_venc_inst_init(inst); + msm_venc_ctrl_init(inst); + } + 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; + } + + 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); + if (rc) { + dprintk(VIDC_ERR, + "Failed to move video instance to init state\n"); + goto fail_init; + } + inst->debugfs_root = + msm_vidc_debugfs_init_inst(inst, core->debugfs_root); + + setup_event_queue(inst, &core->vdev[session_type].vdev); + + return inst; +fail_init: + vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq); + + mutex_lock(&core->lock); + list_del(&inst->list); + mutex_unlock(&core->lock); + +fail_bufq_output: + vb2_queue_release(&inst->bufq[CAPTURE_PORT].vb2_bufq); +fail_bufq_capture: + if (session_type == MSM_VIDC_DECODER) + msm_vdec_ctrl_deinit(inst); + else if (session_type == MSM_VIDC_ENCODER) + msm_venc_ctrl_deinit(inst); + msm_smem_delete_client(inst->mem_client); +fail_mem_client: + 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"); + } + + 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); + + debugfs_remove_recursive(inst->debugfs_root); + + mutex_lock(&inst->pending_getpropq.lock); + WARN_ON(!list_empty(&inst->pending_getpropq.list)); + mutex_unlock(&inst->pending_getpropq.lock); + } +} + +int msm_vidc_close(void *instance) +{ + struct msm_vidc_inst *inst = instance; + struct msm_vidc_inst *temp, *inst_dummy; + struct msm_vidc_core *core; + struct buffer_info *bi, *dummy; + int rc = 0; + int i; + + if (!inst || !inst->core) + return -EINVAL; + + v4l2_fh_del(&inst->event_handler); + v4l2_fh_exit(&inst->event_handler); + + 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) { + list_del(&bi->list); + + for (i = 0; i < min(bi->num_planes, VIDEO_MAX_PLANES); + i++) { + if (bi->handle[i]) + msm_comm_smem_free(inst, bi->handle[i]); + } + + kfree(bi); + } + } + mutex_unlock(&inst->registeredbufs.lock); + + core = inst->core; + + mutex_lock(&core->lock); + list_for_each_entry_safe(temp, inst_dummy, &core->instances, list) { + if (temp == inst) + list_del(&inst->list); + } + mutex_unlock(&core->lock); + + if (inst->session_type == MSM_VIDC_DECODER) + msm_vdec_ctrl_deinit(inst); + else if (inst->session_type == MSM_VIDC_ENCODER) + msm_venc_ctrl_deinit(inst); + + for (i = 0; i < MAX_PORT_NUM; i++) + vb2_queue_release(&inst->bufq[i].vb2_bufq); + + cleanup_instance(inst); + if (inst->state != MSM_VIDC_CORE_INVALID && + 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); + + msm_smem_delete_client(inst->mem_client); + + pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", + VIDC_MSG_PRIO2STRING(VIDC_INFO), inst); + kfree(inst); + + 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/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c new file mode 100644 index 000000000000..82bd7ab772e3 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -0,0 +1,4328 @@ +/* Copyright (c) 2012-2015, 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 <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <soc/qcom/subsystem_restart.h> +#include <asm/div64.h> +#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 IS_VALID_DCVS_SESSION(__cur_mbpf, __min_mbpf) \ + ((__cur_mbpf) >= (__min_mbpf)) + +#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 IS_SESSION_CMD_VALID(cmd) (((cmd) >= SESSION_MSG_START) && \ + ((cmd) <= SESSION_MSG_END)) +#define IS_SYS_CMD_VALID(cmd) (((cmd) >= SYS_MSG_START) && \ + ((cmd) <= SYS_MSG_END)) + +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 command_response cmd, void *data); + +static inline bool is_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_nominal_session(struct msm_vidc_inst *inst) +{ + return !!(inst->flags & VIDC_NOMINAL); +} + +enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst) +{ + if (inst->session_type == MSM_VIDC_DECODER) { + int rc = 0; + struct v4l2_control ctrl = { + .id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE + }; + rc = v4l2_g_ctrl(&inst->ctrl_handler, &ctrl); + if (!rc && ctrl.value) + return HAL_VIDEO_DECODER_SECONDARY; + } + return HAL_VIDEO_DECODER_PRIMARY; +} + +static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst) +{ + int output_port_mbs, capture_port_mbs; + output_port_mbs = 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) * inst->prop.fps; +} + +int msm_comm_get_inst_load(struct msm_vidc_inst *inst, + enum load_calc_quirks quirks) +{ + int load = 0; + if (!(inst->state >= MSM_VIDC_OPEN_DONE && + inst->state < MSM_VIDC_STOP_DONE)) + return 0; + + load = msm_comm_get_mbs_per_sec(inst); + + if (is_thumbnail_session(inst)) { + if (quirks & LOAD_CALC_IGNORE_THUMBNAIL_LOAD) + load = 0; + } + + if (is_turbo_session(inst)) { + if (!(quirks & LOAD_CALC_IGNORE_TURBO_LOAD)) + load = inst->core->resources.max_load; + } + + 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: %p\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; +} + +static 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_type(int fourcc) +{ + enum hal_video_codec codec; + dprintk(VIDC_DBG, "codec is %#x\n", fourcc); + 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_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 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; + + if (!core) { + dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, core); + return -EINVAL; + } + + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle: %p\n", + __func__, hdev); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) + ++vote_data_count; + + vote_data = kzalloc(sizeof(*vote_data) * vote_data_count, + GFP_TEMPORARY); + if (!vote_data) { + dprintk(VIDC_ERR, "%s: failed to allocate memory\n", __func__); + rc = -ENOMEM; + goto fail_alloc; + } + + list_for_each_entry(inst, &core->instances, list) { + int codec = 0; + + codec = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT]->fourcc : + inst->fmts[CAPTURE_PORT]->fourcc; + + vote_data[i].session = VIDC_VOTE_DATA_SESSION_VAL( + get_hal_codec_type(codec), + get_hal_domain(inst->session_type)); + vote_data[i].load = msm_comm_get_inst_load(inst, + LOAD_CALC_NO_QUIRKS); + 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 = %p, 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 = %p\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 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; + unsigned int index; + + if (!IS_SYS_CMD_VALID(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; + 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; + if (core->id == MSM_VIDC_CORE_VENUS && + (core->dec_codec_supported & HAL_VIDEO_CODEC_H264)) + core->dec_codec_supported |= + HAL_VIDEO_CODEC_MVC; + dprintk(VIDC_DBG, "supported_codecs: enc = %#x, dec = %#x\n", + core->enc_codec_supported, core->dec_codec_supported); + dprintk(VIDC_DBG, "ptr[%d] = %p\n", index, &(core->completions[index])); + complete(&(core->completions[index])); +} + +static void handle_session_release_buf_done(enum 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 || !response->data) { + dprintk(VIDC_ERR, "Invalid release_buf_done response\n"); + return; + } + + inst = (struct msm_vidc_inst *)response->session_id; + buffer = (struct hal_buffer_info *) response->data; + address = buffer->buffer_addr; + + mutex_lock(&inst->internalbufs.lock); + list_for_each_safe(ptr, next, &inst->internalbufs.list) { + buf = list_entry(ptr, struct internal_buf, list); + if (address == (u32)buf->handle->device_addr) { + dprintk(VIDC_DBG, "releasing scratch: %pa\n", + &buf->handle->device_addr); + buf_found = true; + } + } + mutex_unlock(&inst->internalbufs.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 == (u32)buf->handle->device_addr) { + dprintk(VIDC_DBG, "releasing persist: %pa\n", + &buf->handle->device_addr); + buf_found = true; + } + } + mutex_unlock(&inst->persistbufs.lock); + + if (!buf_found) + dprintk(VIDC_ERR, "invalid buffer received from firmware"); + if (IS_SESSION_CMD_VALID(cmd)) { + complete(&inst->completions[SESSION_MSG_INDEX(cmd)]); + } else { + dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd); + return; + } +} + +static void handle_sys_release_res_done( + enum 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(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: %p is in bad state can't change state\n", + inst); + goto exit; + } + dprintk(VIDC_DBG, "Moved inst: %p 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 command_response cmd, + struct msm_vidc_inst *inst) +{ + if (!inst) { + dprintk(VIDC_ERR, "Invalid(%p) instance id\n", inst); + return -EINVAL; + } + if (IS_SESSION_CMD_VALID(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 command_response cmd) +{ + int rc = 0; + + if (!IS_SESSION_CMD_VALID(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 timedout: %d\n", rc); + msm_comm_kill_session(inst); + rc = -EIO; + } else { + rc = 0; + } + return rc; +} + +static int wait_for_state(struct msm_vidc_inst *inst, + enum instance_state flipped_state, + enum instance_state desired_state, + enum command_response hal_cmd) +{ + int rc = 0; + if (IS_ALREADY_IN_STATE(flipped_state, desired_state)) { + dprintk(VIDC_INFO, "inst: %p 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 command_response cmd = 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 handle_session_init_done(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst = NULL; + struct hfi_device *hdev; + + if (response) { + struct vidc_hal_session_init_done *session_init_done = + (struct vidc_hal_session_init_done *) + response->data; + inst = (struct msm_vidc_inst *)response->session_id; + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid parameters (%p)\n", + __func__, inst); + return; + } + + hdev = inst->core->device; + + if (!response->status && session_init_done) { + inst->capability.width = session_init_done->width; + inst->capability.height = session_init_done->height; + inst->capability.frame_rate = + session_init_done->frame_rate; + inst->capability.scale_x = session_init_done->scale_x; + inst->capability.scale_y = session_init_done->scale_y; + inst->capability.hier_p = session_init_done->hier_p; + inst->capability.ltr_count = + session_init_done->ltr_count; + inst->capability.pixelprocess_capabilities = + call_hfi_op(hdev, get_core_capabilities); + inst->capability.mbs_per_frame = + session_init_done->mbs_per_frame; + inst->capability.capability_set = true; + inst->capability.buffer_mode[CAPTURE_PORT] = + session_init_done->alloc_mode_out; + inst->capability.secure_output2_threshold = + session_init_done->secure_output2_threshold; + } else { + 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); + } else { + dprintk(VIDC_ERR, + "Failed to get valid response for session init\n"); + } +} + +static void handle_event_change(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + struct v4l2_control control = {0}; + struct msm_vidc_cb_event *event_notify; + int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + int rc = 0; + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + event_notify = (struct msm_vidc_cb_event *) response->data; + switch (event_notify->hal_event_type) { + case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES: + event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; + control.id = + V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER; + rc = v4l2_g_ctrl(&inst->ctrl_handler, &control); + if (rc) + dprintk(VIDC_WARN, + "Failed to get Smooth streamng flag\n"); + if (!rc && control.value == true) + event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT; + 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: %p 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"); + return; + } + + /* + * 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__); + return; + } + + /* 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]); + + mutex_lock(&inst->sync_lock); + + /* 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; + } + } + mutex_unlock(&inst->registeredbufs.lock); + + /* + * 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->sync_lock); + + /*send event to client*/ + v4l2_event_queue_fh(&inst->event_handler, &buf_event); + return; + } + default: + break; + } + 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"); + if (msm_comm_get_stream_output_mode(inst) != + HAL_VIDEO_DECODER_SECONDARY) { + dprintk(VIDC_DBG, + "event_notify->height = %d event_notify->width = %d\n", + event_notify->height, + event_notify->width); + inst->prop.height[CAPTURE_PORT] = + event_notify->height; + inst->prop.width[CAPTURE_PORT] = + event_notify->width; + inst->prop.height[OUTPUT_PORT] = + event_notify->height; + inst->prop.width[OUTPUT_PORT] = + event_notify->width; + } + } + + if (inst->session_type == MSM_VIDC_DECODER) + msm_dcvs_init_load(inst); + rc = msm_vidc_check_session_supported(inst); + if (!rc) { + msm_vidc_queue_v4l2_event(inst, 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); + } + + return; + } else { + dprintk(VIDC_ERR, + "Failed to get valid response for event_change\n"); + } +} + +static void handle_session_prop_info(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct getprop_buf *getprop; + struct msm_vidc_inst *inst; + if (!response || !response->data) { + dprintk(VIDC_ERR, + "Failed to get valid response for prop info\n"); + return; + } + inst = (struct msm_vidc_inst *)response->session_id; + + getprop = kzalloc(sizeof(*getprop), GFP_KERNEL); + if (!getprop) { + dprintk(VIDC_ERR, "%s: getprop kzalloc failed\n", __func__); + return; + } + + getprop->data = kmemdup(response->data, response->size, GFP_KERNEL); + if (!getprop->data) { + dprintk(VIDC_ERR, "%s: kmemdup failed\n", __func__); + kfree(getprop); + return; + } + + 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); + return; +} + +static void handle_load_resource_done(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + if (response->status) { + dprintk(VIDC_ERR, + "Load resource response from FW : %#x\n", + response->status); + msm_comm_generate_session_error(inst); + } + } else + dprintk(VIDC_ERR, + "Failed to get valid response for load resource\n"); +} + +static void handle_start_done(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + signal_session_msg_receipt(cmd, inst); + } else { + dprintk(VIDC_ERR, + "Failed to get valid response for start\n"); + } +} + +static void handle_stop_done(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + signal_session_msg_receipt(cmd, inst); + } else { + dprintk(VIDC_ERR, + "Failed to get valid response for stop\n"); + } +} + +static void handle_release_res_done(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + signal_session_msg_receipt(cmd, inst); + } else { + dprintk(VIDC_ERR, + "Failed to get valid response for release resource\n"); + } +} +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 %pa\n", + &binfo->handle->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); + + return; +} + +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst) +{ + struct internal_buf *binfo; + struct hfi_device *hdev; + struct msm_smem *handle; + struct vidc_frame_data frame_data = {0}; + struct hal_buffer_requirements *output_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); + + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + if (binfo->buffer_ownership != DRIVER) + continue; + handle = binfo->handle; + frame_data.alloc_len = output_buf->buffer_size; + frame_data.filled_len = 0; + frame_data.offset = 0; + frame_data.device_addr = handle->device_addr; + frame_data.flags = 0; + frame_data.extradata_addr = handle->device_addr + + output_buf->buffer_size; + frame_data.buffer_type = HAL_BUFFER_OUTPUT; + 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 command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + int rc; + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + 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); + } + } + } + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE); + } else { + dprintk(VIDC_ERR, "Failed to get valid response for flush\n"); + } +} + +static void handle_session_error(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct hfi_device *hdev = NULL; + struct msm_vidc_inst *inst = NULL; + + if (!response) { + dprintk(VIDC_ERR, + "Failed to get valid response for session error\n"); + return; + } + + inst = (struct msm_vidc_inst *)response->session_id; + + if (!inst || !inst->session || !inst->core->device) { + dprintk(VIDC_ERR, + "Session (%p) not in a stable enough state to handle session error\n", + inst); + return; + } + + hdev = inst->core->device; + dprintk(VIDC_WARN, "Session error received for session %p\n", inst); + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + + if (response->status == VIDC_ERR_MAX_CLIENTS) { + dprintk(VIDC_WARN, + "send max clients reached error to client: %p\n", + inst); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_MAX_CLIENTS); + } else { + dprintk(VIDC_ERR, + "send session error to client: %p\n", + inst); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_SYS_ERROR); + } +} + +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 %p\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; + mutex_unlock(&inst->lock); + dprintk(VIDC_WARN, + "%s Send sys error for inst %p\n", __func__, inst); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_SYS_ERROR); + } + mutex_unlock(&core->lock); +} + +struct sys_err_handler_data { + struct msm_vidc_core *core; + struct delayed_work work; +}; + + +void hw_sys_error_handler(struct work_struct *work) +{ + struct msm_vidc_core *core = NULL; + struct hfi_device *hdev = NULL; + struct sys_err_handler_data *handler = NULL; + int rc = 0; + + handler = container_of(work, struct sys_err_handler_data, work.work); + if (!handler || !handler->core || !handler->core->device) { + dprintk(VIDC_ERR, "%s - invalid work or core handle\n", + __func__); + goto exit; + } + + core = handler->core; + hdev = core->device; + + mutex_lock(&core->lock); + /* + * Restart the firmware to bring out of bad state. + */ + if (core->state == VIDC_CORE_INVALID && hdev->resurrect_fw) { + rc = call_hfi_op(hdev, resurrect_fw, + hdev->hfi_device_data); + if (rc) { + dprintk(VIDC_ERR, + "%s - resurrect_fw failed: %d\n", + __func__, rc); + } + core->state = VIDC_CORE_LOADED; + } else { + dprintk(VIDC_DBG, + "fw unloaded after sys error, no need to resurrect\n"); + } + mutex_unlock(&core->lock); + +exit: + /* free sys error handler, allocated in handle_sys_err */ + kfree(handler); +} + +static void handle_sys_error(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_core *core = NULL; + struct sys_err_handler_data *handler = NULL; + + 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 %p\n", cmd, core); + msm_comm_clean_notify_client(core); + + handler = kzalloc(sizeof(*handler), GFP_KERNEL); + if (!handler) { + dprintk(VIDC_ERR, + "%s - failed to allocate sys error handler\n", + __func__); + return; + } + handler->core = core; + INIT_DELAYED_WORK(&handler->work, hw_sys_error_handler); + + /* + * Sleep for 5 sec to ensure venus has completed any + * pending cache operations. Without this sleep, we see + * device reset when firmware is unloaded after a sys + * error. + */ + schedule_delayed_work(&handler->work, msecs_to_jiffies(5000)); +} + +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: %p\n", inst); + rc = call_hfi_op(hdev, session_clean, + (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Session clean failed :%p\n", inst); + } + inst->session = NULL; + } + mutex_unlock(&inst->lock); +} + +static void handle_session_close(enum command_response cmd, void *data) +{ + struct msm_vidc_cb_cmd_done *response = data; + struct msm_vidc_inst *inst; + + if (response) { + inst = (struct msm_vidc_inst *)response->session_id; + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid params\n", __func__); + return; + } + signal_session_msg_receipt(cmd, inst); + show_stats(inst); + } else { + dprintk(VIDC_ERR, + "Failed to get valid response for session close\n"); + } +} + +static struct vb2_buffer *get_vb_from_v4l2_index(struct msm_vidc_inst* inst, + struct buf_queue *bufq, unsigned long dev_addr) +{ + + struct vb2_buffer *vb = NULL; + struct vb2_queue *q = NULL; + struct buffer_info* binfo = NULL; + int found = 0, plane = 0; + if (!inst || !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) { + binfo = get_registered_mmap_buf(inst, &vb->v4l2_buf, &plane); + if (binfo != NULL && binfo->device_addr[plane] == dev_addr) { + found = 1; + dprintk(VIDC_DBG, "Found v4l2_buf idx %d, addr %#lx\n", + vb->v4l2_buf.index, dev_addr); + 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 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->v4l2_planes[0].m.userptr == dev_addr && + vb->state == VB2_BUF_STATE_ACTIVE) { + found = 1; + dprintk(VIDC_DBG, "Found v4l2_buf index : %d\n", + vb->v4l2_buf.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 void handle_ebd(enum 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; + + if (!response) { + dprintk(VIDC_ERR, "Invalid response from vidc_hal\n"); + return; + } + + inst = (struct msm_vidc_inst *)response->session_id; + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid response from vidc_hal\n", + __func__); + return; + } + if (inst->bufq[OUTPUT_PORT].vb2_bufq.memory == V4L2_MEMORY_MMAP) { + vb = get_vb_from_v4l2_index(inst, &inst->bufq[OUTPUT_PORT], + response->clnt_data); + } else { + vb = get_vb_from_device_addr(&inst->bufq[OUTPUT_PORT], + response->clnt_data); + } + if (vb) { + vb->v4l2_planes[0].bytesused = response->input_done.filled_len; + vb->v4l2_planes[0].data_offset = response->input_done.offset; + if (vb->v4l2_planes[0].data_offset > vb->v4l2_planes[0].length) + dprintk(VIDC_INFO, "data_offset overflow length\n"); + if (vb->v4l2_planes[0].bytesused > vb->v4l2_planes[0].length) + dprintk(VIDC_INFO, "bytesused overflow length\n"); + if (vb->v4l2_buf.memory == V4L2_MEMORY_USERPTR && + vb->v4l2_planes[0].m.userptr != + response->input_done.packet_buffer) + dprintk(VIDC_INFO, "Unexpected buffer address\n"); + empty_buf_done = (struct vidc_hal_ebd *)&response->input_done; + if (empty_buf_done) { + if (empty_buf_done->status == VIDC_ERR_NOT_SUPPORTED) { + dprintk(VIDC_INFO, + "Failed : Unsupported input stream\n"); + vb->v4l2_buf.flags |= + V4L2_QCOM_BUF_INPUT_UNSUPPORTED; + } + if (empty_buf_done->status == VIDC_ERR_BITSTREAM_ERR) { + dprintk(VIDC_INFO, + "Failed : Corrupted input stream\n"); + vb->v4l2_buf.flags |= + V4L2_QCOM_BUF_DATA_CORRUPT; + } + if (empty_buf_done->status == + VIDC_ERR_START_CODE_NOT_FOUND) { + vb->v4l2_buf.flags |= + V4L2_MSM_VIDC_BUF_START_CODE_NOT_FOUND; + dprintk(VIDC_INFO, + "Failed: Start code not found\n"); + } + } + 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); + } +} + +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; + } + 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; + + rc = output_buffer_cache_invalidate(inst, binfo); + if (rc) + return rc; + + 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) { + rc = qbuf_dynamic_buf(inst, binfo); + if (!rc) + return rc; + } + return cnt; +} + +static void handle_dynamic_buffer(struct msm_vidc_inst *inst, + ion_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, + ion_phys_addr_t dev_addr) +{ + struct internal_buf *binfo; + struct msm_smem *handle; + bool found = false; + + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry(binfo, &inst->outputbufs.list, list) { + handle = binfo->handle; + if (handle && dev_addr == handle->device_addr) { + if (binfo->buffer_ownership == DRIVER) { + dprintk(VIDC_ERR, + "FW returned same buffer: %pa\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: %pa\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 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; + + if (!response) { + dprintk(VIDC_ERR, "Invalid response from vidc_hal\n"); + return; + } + inst = (struct msm_vidc_inst *)response->session_id; + 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) { + if (inst->bufq[CAPTURE_PORT].vb2_bufq.memory == V4L2_MEMORY_MMAP) { + vb = get_vb_from_v4l2_index(inst, &inst->bufq[CAPTURE_PORT], + fill_buf_done->packet_buffer1); + } else { + 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); + return; + } + if (vb) { + vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1; + vb->v4l2_planes[0].data_offset = fill_buf_done->offset1; + vb->v4l2_planes[0].reserved[2] = fill_buf_done->start_x_coord; + vb->v4l2_planes[0].reserved[3] = fill_buf_done->start_y_coord; + vb->v4l2_planes[0].reserved[4] = fill_buf_done->frame_width; + vb->v4l2_planes[0].reserved[5] = fill_buf_done->frame_height; + vb->v4l2_planes[0].reserved[6] = + inst->prop.width[CAPTURE_PORT]; + vb->v4l2_planes[0].reserved[7] = + inst->prop.height[CAPTURE_PORT]; + if (vb->v4l2_planes[0].data_offset > vb->v4l2_planes[0].length) + dprintk(VIDC_INFO, + "fbd:Overflow data_offset = %d; length = %d\n", + vb->v4l2_planes[0].data_offset, + vb->v4l2_planes[0].length); + if (vb->v4l2_planes[0].bytesused > vb->v4l2_planes[0].length) + dprintk(VIDC_INFO, + "fbd:Overflow bytesused = %d; length = %d\n", + vb->v4l2_planes[0].bytesused, + vb->v4l2_planes[0].length); + if (!(fill_buf_done->flags1 & + HAL_BUFFERFLAG_TIMESTAMPINVALID) && + fill_buf_done->filled_len1) { + 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->v4l2_buf.timestamp = + ns_to_timeval(time_usec * NSEC_PER_USEC); + vb->v4l2_buf.flags = 0; + extra_idx = + EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + vb->v4l2_planes[extra_idx].m.userptr = + (unsigned long)fill_buf_done->extra_data_buffer; + vb->v4l2_planes[extra_idx].bytesused = + vb->v4l2_planes[extra_idx].length; + vb->v4l2_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) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_READONLY; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_EOS; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG) + vb->v4l2_buf.flags &= ~V4L2_QCOM_BUF_FLAG_CODECCONFIG; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOSEQ) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_EOSEQ; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DECODEONLY) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_DECODEONLY; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DATA_CORRUPT; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DROP_FRAME) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_DROP_FRAME; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_MBAFF) + vb->v4l2_buf.flags |= V4L2_MSM_BUF_FLAG_MBAFF; + if (fill_buf_done->flags1 & + HAL_BUFFERFLAG_TS_DISCONTINUITY) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_TS_DISCONTINUITY; + if (fill_buf_done->flags1 & HAL_BUFFERFLAG_TS_ERROR) + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_TS_ERROR; + switch (fill_buf_done->picture_type) { + case HAL_PICTURE_IDR: + vb->v4l2_buf.flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME; + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + break; + case HAL_PICTURE_I: + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; + break; + case HAL_PICTURE_P: + vb->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; + break; + case HAL_PICTURE_B: + vb->v4l2_buf.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 (fill_buf_done->filled_len1) + msm_vidc_debugfs_update(inst, + MSM_VIDC_DEBUGFS_EVENT_FBD); + + if (extra_idx && extra_idx < VIDEO_MAX_PLANES) { + dprintk(VIDC_DBG, + "extradata: userptr = %p;" + " bytesused = %d; length = %d\n", + (u8 *)vb->v4l2_planes[extra_idx].m.userptr, + vb->v4l2_planes[extra_idx].bytesused, + vb->v4l2_planes[extra_idx].length); + } + 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\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); + + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); + } +} + +static void handle_seq_hdr_done(enum 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; + if (!response) { + dprintk(VIDC_ERR, "Invalid response from vidc_hal\n"); + return; + } + inst = (struct msm_vidc_inst *)response->session_id; + 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\n"); + return; + } + + vb->v4l2_planes[0].bytesused = fill_buf_done->filled_len1; + vb->v4l2_planes[0].data_offset = fill_buf_done->offset1; + + vb->v4l2_buf.flags = V4L2_QCOM_BUF_FLAG_CODECCONFIG; + vb->v4l2_buf.timestamp = ns_to_timeval(0); + + dprintk(VIDC_DBG, "Filled length = %d; offset = %d; flags %x\n", + vb->v4l2_planes[0].bytesused, + vb->v4l2_planes[0].data_offset, + vb->v4l2_buf.flags); + mutex_lock(&inst->bufq[CAPTURE_PORT].lock); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + mutex_unlock(&inst->bufq[CAPTURE_PORT].lock); +} + +void handle_cmd_response(enum command_response cmd, void *data) +{ + dprintk(VIDC_DBG, "Command response = %d\n", cmd); + switch (cmd) { + case SYS_INIT_DONE: + handle_sys_init_done(cmd, data); + break; + case RELEASE_RESOURCE_DONE: + handle_sys_release_res_done(cmd, data); + break; + case SESSION_INIT_DONE: + handle_session_init_done(cmd, data); + break; + case SESSION_PROPERTY_INFO: + handle_session_prop_info(cmd, data); + break; + case SESSION_LOAD_RESOURCE_DONE: + handle_load_resource_done(cmd, data); + break; + case SESSION_START_DONE: + handle_start_done(cmd, data); + break; + case SESSION_ETB_DONE: + handle_ebd(cmd, data); + break; + case SESSION_FTB_DONE: + handle_fbd(cmd, data); + break; + case SESSION_STOP_DONE: + handle_stop_done(cmd, data); + break; + case SESSION_RELEASE_RESOURCE_DONE: + handle_release_res_done(cmd, data); + break; + case SESSION_END_DONE: + case SESSION_ABORT_DONE: + handle_session_close(cmd, data); + break; + case VIDC_EVENT_CHANGE: + handle_event_change(cmd, data); + break; + case SESSION_FLUSH_DONE: + handle_session_flush(cmd, data); + break; + case SESSION_GET_SEQ_HDR_DONE: + handle_seq_hdr_done(cmd, data); + break; + case SYS_WATCHDOG_TIMEOUT: + handle_sys_error(cmd, data); + break; + case SYS_ERROR: + handle_sys_error(cmd, data); + break; + case SESSION_ERROR: + handle_session_error(cmd, data); + break; + case 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); +} + +int msm_comm_scale_clocks_load(struct msm_vidc_core *core, int num_mbs_per_sec) +{ + u32 codecs_enabled = 0; + int rc = 0; + struct hfi_device *hdev; + struct msm_vidc_inst *inst = NULL; + bool is_nominal = false; + struct msm_vidc_platform_resources *res; + + if (!core) { + dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, core); + return -EINVAL; + } + + hdev = core->device; + res = &core->resources; + + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle: %p\n", + __func__, hdev); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + int codec = 0; + + codec = inst->session_type == MSM_VIDC_DECODER ? + inst->fmts[OUTPUT_PORT]->fourcc : + inst->fmts[CAPTURE_PORT]->fourcc; + + codecs_enabled |= VIDC_VOTE_DATA_SESSION_VAL( + get_hal_codec_type(codec), + get_hal_domain(inst->session_type)); + + if (is_nominal_session(inst)) + is_nominal = true; + } + mutex_unlock(&core->lock); + + dprintk(VIDC_INFO, "num_mbs_per_sec = %d codecs_enabled %#x\n", + num_mbs_per_sec, codecs_enabled); + + if (is_nominal && num_mbs_per_sec) { + struct load_freq_table *table = res->load_freq_tbl; + u32 table_size = res->load_freq_tbl_size; + u32 low_freq = table[table_size - 1].freq; + int i; + + /* + * Parse the load frequency table from highest index and + * whenever there is a change in frequency detected, it is + * assumed as nominal frequency Check the current load + * against the load corresponding to nominal frequency and + * update num_mbs_per_sec accordingly. + */ + for (i = table_size - 1; i >= 0; i--) { + if (table[i].freq > low_freq) { + if (num_mbs_per_sec < table[i].load) { + num_mbs_per_sec = table[i].load; + dprintk(VIDC_DBG, + "updated num_mbs_per_sec: %d\n", + num_mbs_per_sec); + } + break; + } + } + } + + rc = call_hfi_op(hdev, scale_clocks, + hdev->hfi_device_data, num_mbs_per_sec, codecs_enabled); + 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); + 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; + + 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; + } + abort_completion = SESSION_MSG_INDEX(SESSION_ABORT_DONE); + init_completion(&inst->completions[abort_completion]); + 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 [%p]: %d\n", + __func__, inst, abort_completion); + rc = -EBUSY; + } else { + rc = 0; + } + + 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 %p\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 %p\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); + return; +} + +void msm_comm_handle_thermal_event() +{ + 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(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(SYS_INIT_DONE)); + 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__); + return rc; + } + change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE); + return rc; +} + +int msm_comm_load_fw(struct msm_vidc_core *core) +{ + int rc = 0; + struct hfi_device *hdev; + + if (!core || !core->device) + return -EINVAL; + 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->state < VIDC_CORE_LOADED) { + rc = call_hfi_op(hdev, load_fw, hdev->hfi_device_data); + if (rc) { + dprintk(VIDC_ERR, "Failed to load video firmware\n"); + mutex_unlock(&core->lock); + goto fail_load_fw; + } + core->state = VIDC_CORE_LOADED; + dprintk(VIDC_DBG, "Firmware downloaded\n"); + } + + if (core->state == VIDC_CORE_LOADED) { + init_completion(&core->completions + [SYS_MSG_INDEX(SYS_INIT_DONE)]); + 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); + mutex_unlock(&core->lock); + goto fail_core_init; + } + core->state = VIDC_CORE_INIT; + } + +core_already_inited: + mutex_unlock(&core->lock); + return rc; + +fail_core_init: + mutex_lock(&core->lock); + call_hfi_op(hdev, unload_fw, hdev->hfi_device_data); + core->state = VIDC_CORE_UNINIT; + mutex_unlock(&core->lock); +fail_load_fw: + return rc; +} + +static int msm_comm_init_core(struct msm_vidc_inst *inst) +{ + int rc = 0; + + if (!inst || !inst->core) + return -EINVAL; + + rc = msm_comm_load_fw(inst->core); + if (rc) { + dprintk(VIDC_ERR, "%s - firmware loading failed\n", __func__); + return rc; + } + + change_inst_state(inst, MSM_VIDC_CORE_INIT); + 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 firmware is configured to be always loaded in memory, + * then unload it only if the core has gone in to bad state. + */ + if (core->resources.early_fw_load && + core->state != VIDC_CORE_INVALID) { + goto core_already_uninited; + } + + if (list_empty(&core->instances)) { + 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. + */ +#if 0 + 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); +#endif + } + +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) +{ + 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: %p 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; + } + init_completion( + &inst->completions[SESSION_MSG_INDEX(SESSION_INIT_DONE)]); + + inst->session = call_hfi_op(hdev, session_init, hdev->hfi_device_data, + inst, get_hal_domain(inst->session_type), + get_hal_codec_type(fourcc)); + + if (!inst->session) { + dprintk(VIDC_ERR, + "Failed to call session init for: %p, %p, %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 (is_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; + struct msm_vidc_core *core; + 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; + } + + 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); + + if (num_mbs_per_sec > core->resources.max_load) { + dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n", + num_mbs_per_sec, core->resources.max_load); + 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: %p 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: %p is already in state: %d\n", + inst, inst->state); + goto exit; + } + init_completion( + &inst->completions[SESSION_MSG_INDEX(SESSION_START_DONE)]); + 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: %p is already in state: %d\n", + inst, inst->state); + goto exit; + } + dprintk(VIDC_DBG, "Send Stop to hal\n"); + init_completion( + &inst->completions[SESSION_MSG_INDEX(SESSION_STOP_DONE)]); + 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: %p is already in state: %d\n", + inst, inst->state); + goto exit; + } + dprintk(VIDC_DBG, + "Send release res to hal\n"); + init_completion( + &inst->completions[SESSION_MSG_INDEX(SESSION_RELEASE_RESOURCE_DONE)]); + 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: %p is already in state: %d\n", + inst, inst->state); + goto exit; + } + dprintk(VIDC_DBG, + "Send session close to hal\n"); + init_completion( + &inst->completions[SESSION_MSG_INDEX(SESSION_END_DONE)]); + rc = call_hfi_op(hdev, session_end, (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, + "Failed to send close\n"); + goto exit; + } + change_inst_state(inst, MSM_VIDC_CLOSE); +exit: + return rc; +} + +int msm_comm_suspend(int core_id) +{ + struct hfi_device *hdev; + struct msm_vidc_core *core; + int rc = 0; + + core = get_vidc_core(core_id); + if (!core) { + dprintk(VIDC_ERR, + "%s: Failed to find core for core_id = %d\n", + __func__, core_id); + return -EINVAL; + } + + hdev = (struct hfi_device *)core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle\n", __func__); + return -EINVAL; + } + + rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data); + if (rc) + dprintk(VIDC_WARN, "Failed to suspend\n"); + + return rc; +} + +static int get_flipped_state(int present_state, + int desired_state) +{ + int flipped_state = present_state; + if (flipped_state < MSM_VIDC_STOP + && desired_state > MSM_VIDC_STOP) { + flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state); + flipped_state &= 0xFFFE; + flipped_state = flipped_state - 1; + } else if (flipped_state > MSM_VIDC_STOP + && desired_state < MSM_VIDC_STOP) { + flipped_state = MSM_VIDC_STOP - + (flipped_state - MSM_VIDC_STOP + 1); + flipped_state &= 0xFFFE; + flipped_state = flipped_state - 1; + } + return flipped_state; +} + +struct hal_buffer_requirements *get_buff_req_buffer( + struct msm_vidc_inst *inst, enum hal_buffer buffer_type) +{ + int i; + for (i = 0; i < HAL_BUFFER_MAX; i++) { + if (inst->buff_req.buffer[i].buffer_type == buffer_type) + return &inst->buff_req.buffer[i]; + } + return NULL; +} + +static int set_output_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + int rc = 0; + struct msm_smem *handle; + struct internal_buf *binfo; + struct vidc_buffer_addr_info buffer_info = {0}; + u32 smem_flags = 0, buffer_size; + struct hal_buffer_requirements *output_buf, *extradata_buf; + int i; + struct hfi_device *hdev; + + 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; + + 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++) { + handle = msm_comm_smem_alloc(inst, + buffer_size, 1, smem_flags, + buffer_type, 0); + if (!handle) { + dprintk(VIDC_ERR, + "Failed to allocate output memory\n"); + rc = -ENOMEM; + goto err_no_mem; + } + rc = msm_comm_smem_cache_operations(inst, + handle, SMEM_CACHE_CLEAN); + if (rc) { + dprintk(VIDC_WARN, + "Failed to clean cache may cause undefined behavior\n"); + } + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto fail_kzalloc; + } + + binfo->handle = handle; + buffer_info.buffer_size = output_buf->buffer_size; + buffer_info.buffer_type = buffer_type; + binfo->buffer_type = buffer_type; + buffer_info.num_buffers = 1; + binfo->buffer_ownership = DRIVER; + buffer_info.align_device_addr = handle->device_addr; + buffer_info.extradata_addr = handle->device_addr + + output_buf->buffer_size; + if (extradata_buf) { + buffer_info.extradata_size = + extradata_buf->buffer_size; + } + dprintk(VIDC_DBG, "Output buffer address: %pa\n", + &buffer_info.align_device_addr); + dprintk(VIDC_DBG, "Output extradata address: %pa\n", + &buffer_info.extradata_addr); + 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: + kfree(binfo); +fail_kzalloc: + msm_comm_smem_free(inst, handle); +err_no_mem: + return rc; +} + +static inline char *get_internal_buffer_name(enum hal_buffer buffer_type) +{ + switch (buffer_type) { + 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"; + default: return "unknown"; + } +} + +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. May cause 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_internal_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_scratch_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + struct internal_buf *buf; + int rc = 0; + bool reused = false; + + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid params\n", __func__); + return false; + } + + mutex_lock(&inst->internalbufs.lock); + list_for_each_entry(buf, &inst->internalbufs.list, list) { + if (!buf->handle) { + reused = false; + break; + } + + if (buf->buffer_type != buffer_type) + continue; + + rc = set_internal_buf_on_fw(inst, buffer_type, + buf->handle, true); + if (rc) { + dprintk(VIDC_ERR, + "%s: session_set_buffers failed\n", __func__); + reused = false; + break; + } + reused = true; + } + mutex_unlock(&inst->internalbufs.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 msm_smem *handle; + struct internal_buf *binfo; + u32 smem_flags = 0; + 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++) { + handle = msm_comm_smem_alloc(inst, internal_bufreq->buffer_size, + 1, smem_flags, internal_bufreq->buffer_type, 0); + if (!handle) { + dprintk(VIDC_ERR, + "Failed to allocate scratch memory\n"); + rc = -ENOMEM; + goto err_no_mem; + } + + binfo = kzalloc(sizeof(*binfo), GFP_KERNEL); + if (!binfo) { + dprintk(VIDC_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto fail_kzalloc; + } + + binfo->handle = handle; + binfo->buffer_type = internal_bufreq->buffer_type; + + rc = set_internal_buf_on_fw(inst, internal_bufreq->buffer_type, + handle, 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: + kfree(binfo); +fail_kzalloc: + msm_comm_smem_free(inst, handle); +err_no_mem: + return rc; + +} + +static int set_scratch_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + struct hal_buffer_requirements *scratch_buf; + + scratch_buf = get_buff_req_buffer(inst, buffer_type); + if (!scratch_buf) { + dprintk(VIDC_DBG, + "This scratch buffer not required, buffer_type: %x\n", + buffer_type); + return 0; + } + dprintk(VIDC_DBG, + "scratch: num = %d, size = %d\n", + scratch_buf->buffer_count_actual, + scratch_buf->buffer_size); + + /* + * Try reusing existing scratch buffers first. + * If it's not possible to reuse, allocate new buffers. + */ + if (reuse_scratch_buffers(inst, buffer_type)) + return 0; + + return allocate_and_set_internal_bufs(inst, scratch_buf, + &inst->internalbufs); +} + +static int set_persist_buffers(struct msm_vidc_inst *inst, + enum hal_buffer buffer_type) +{ + struct hal_buffer_requirements *persist_buf; + + persist_buf = get_buff_req_buffer(inst, buffer_type); + if (!persist_buf) { + dprintk(VIDC_DBG, + "This persist buffer not required, buffer_type: %x\n", + buffer_type); + return 0; + } + + dprintk(VIDC_DBG, "persist: num = %d, size = %d\n", + persist_buf->buffer_count_actual, + persist_buf->buffer_size); + + mutex_lock(&inst->persistbufs.lock); + if (!list_empty(&inst->persistbufs.list)) { + dprintk(VIDC_ERR, "Persist buffers already allocated\n"); + mutex_unlock(&inst->persistbufs.lock); + return 0; + } + mutex_unlock(&inst->persistbufs.lock); + + return allocate_and_set_internal_bufs(inst, persist_buf, + &inst->persistbufs); +} + +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 = %p\n", inst); + return -EINVAL; + } + dprintk(VIDC_DBG, + "Trying to move inst: %p from: %#x to %#x\n", + inst, inst->state, state); + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %p\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, + 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, + 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, + 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, + 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, + SESSION_END_DONE); + if (rc || state <= get_flipped_state(inst->state, state)) + break; + 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 %d not recognized\n", flipped_state); + 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_comm_qbuf(struct vb2_buffer *vb) +{ + int rc = 0; + struct vb2_queue *q; + struct msm_vidc_inst *inst; + struct vb2_buf_entry *entry; + struct vidc_frame_data frame_data; + struct msm_vidc_core *core; + struct hfi_device *hdev; + int extra_idx = 0, plane = 0; + struct buffer_info* binfo; + dma_addr_t device_addr; + + if (!vb || !vb->vb2_queue) { + dprintk(VIDC_ERR, "%s: Invalid input: %p\n", + __func__, vb); + return -EINVAL; + } + + q = vb->vb2_queue; + inst = q->drv_priv; + if (!inst) { + dprintk(VIDC_ERR, "%s: Invalid input: %p\n", + __func__, vb); + return -EINVAL; + } + + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid input: %p, %p, %p\n", inst, core, vb); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s: Invalid input: %p\n", + __func__, hdev); + return -EINVAL; + } + + if (inst->state == MSM_VIDC_CORE_INVALID || + core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n"); + return -EINVAL; + } + if (inst->state != MSM_VIDC_START_DONE) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + dprintk(VIDC_ERR, "Out of memory\n"); + goto err_no_mem; + } + entry->vb = vb; + + mutex_lock(&inst->pendingq.lock); + list_add_tail(&entry->list, &inst->pendingq.list); + mutex_unlock(&inst->pendingq.lock); + } else { + int64_t time_usec = timeval_to_ns(&vb->v4l2_buf.timestamp); + if (vb->v4l2_buf.memory == V4L2_MEMORY_MMAP) { + binfo = get_registered_mmap_buf( + inst, &vb->v4l2_buf, &plane); + if (binfo == NULL) { + dprintk(VIDC_ERR, "unable to find binfo %d", vb->v4l2_buf.index); + rc = -EINVAL; + goto err_bad_input; + } + device_addr = binfo->device_addr[0]; + } else { + device_addr = vb->v4l2_planes[0].m.userptr; + } + do_div(time_usec, NSEC_PER_USEC); + memset(&frame_data, 0 , sizeof(struct vidc_frame_data)); + frame_data.alloc_len = vb->v4l2_planes[0].length; + frame_data.filled_len = vb->v4l2_planes[0].bytesused; + frame_data.offset = vb->v4l2_planes[0].data_offset; + frame_data.device_addr = device_addr; + frame_data.timestamp = time_usec; + frame_data.flags = 0; + frame_data.clnt_data = frame_data.device_addr; + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + (frame_data.filled_len > frame_data.alloc_len || + frame_data.offset > frame_data.alloc_len)) { + dprintk(VIDC_ERR, + "Buffer will overflow, not queueing it\n"); + rc = -EINVAL; + goto err_bad_input; + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + frame_data.buffer_type = HAL_BUFFER_INPUT; + if (vb->v4l2_buf.flags & V4L2_QCOM_BUF_FLAG_EOS) { + frame_data.flags |= HAL_BUFFERFLAG_EOS; + dprintk(VIDC_DBG, + "Received EOS on output capability\n"); + } + + if (vb->v4l2_buf.flags & + V4L2_MSM_BUF_FLAG_YUV_601_709_CLAMP) { + frame_data.flags |= + HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP; + dprintk(VIDC_DBG, + "Received buff with 601to709 clamp\n"); + } + + if (vb->v4l2_buf.flags & + V4L2_QCOM_BUF_FLAG_CODECCONFIG) { + frame_data.flags |= HAL_BUFFERFLAG_CODECCONFIG; + dprintk(VIDC_DBG, + "Received CODECCONFIG on output cap\n"); + } + + if (vb->v4l2_buf.flags & + V4L2_QCOM_BUF_FLAG_DECODEONLY) { + frame_data.flags |= HAL_BUFFERFLAG_DECODEONLY; + dprintk(VIDC_DBG, + "Received DECODEONLY on output cap\n"); + } + + if (vb->v4l2_buf.flags & + V4L2_QCOM_BUF_TIMESTAMP_INVALID) + frame_data.timestamp = LLONG_MAX; + + if (vb->v4l2_buf.flags & + V4L2_QCOM_BUF_TS_DISCONTINUITY) { + frame_data.flags |= + HAL_BUFFERFLAG_TS_DISCONTINUITY; + dprintk(VIDC_DBG, + "Received TS_DISCONTINUE on output\n"); + } + + if (vb->v4l2_buf.flags & V4L2_QCOM_BUF_TS_ERROR) { + frame_data.flags |= + HAL_BUFFERFLAG_TS_ERROR; + dprintk(VIDC_DBG, + "Received TS_ERROR on output cap\n"); + } + + extra_idx = + EXTRADATA_IDX(inst->fmts[OUTPUT_PORT]-> + num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES && + vb->v4l2_planes[extra_idx].m.userptr) { + frame_data.extradata_addr = + vb->v4l2_planes[extra_idx].m.userptr; + frame_data.flags |= HAL_BUFFERFLAG_EXTRADATA; + } + + dprintk(VIDC_DBG, + "Sending etb to hal: device_addr: %pa, alloc: %d, filled: %d, offset: %d, ts: %lld, flags = %#x, v4l2_buf index = %d\n", + &frame_data.device_addr, frame_data.alloc_len, + frame_data.filled_len, frame_data.offset, + frame_data.timestamp, frame_data.flags, + vb->v4l2_buf.index); + + msm_dcvs_check_and_scale_clocks(inst, true); + rc = call_hfi_op(hdev, session_etb, (void *) + inst->session, &frame_data); + if (!rc) + msm_vidc_debugfs_update(inst, + MSM_VIDC_DEBUGFS_EVENT_ETB); + dprintk(VIDC_DBG, "Sent etb to HAL\n"); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + struct vidc_seq_hdr seq_hdr; + frame_data.filled_len = 0; + frame_data.offset = 0; + frame_data.alloc_len = vb->v4l2_planes[0].length; + frame_data.buffer_type = + msm_comm_get_hal_output_buffer(inst); + + extra_idx = + EXTRADATA_IDX(inst->fmts[CAPTURE_PORT]->num_planes); + if (extra_idx && extra_idx < VIDEO_MAX_PLANES && + vb->v4l2_planes[extra_idx].m.userptr) { + frame_data.extradata_addr = + vb->v4l2_planes[extra_idx].m.userptr; + frame_data.extradata_size = + vb->v4l2_planes[extra_idx].length; + } + + dprintk(VIDC_DBG, + "Sending ftb to hal: device_addr: %pa, alloc: %d, buffer_type: %d, ts: %lld, flags = %#x, v4l2_buf index = %d\n", + &frame_data.device_addr, frame_data.alloc_len, + frame_data.buffer_type, frame_data.timestamp, + frame_data.flags, vb->v4l2_buf.index); + + if (atomic_read(&inst->seq_hdr_reqs) && + inst->session_type == MSM_VIDC_ENCODER) { + seq_hdr.seq_hdr = vb->v4l2_planes[0]. + m.userptr; + seq_hdr.seq_hdr_len = vb->v4l2_planes[0].length; + rc = call_hfi_op(hdev, session_get_seq_hdr, + (void *) inst->session, &seq_hdr); + if (!rc) { + inst->vb2_seq_hdr = vb; + dprintk(VIDC_DBG, "Seq_hdr: %p\n", + inst->vb2_seq_hdr); + } + atomic_dec(&inst->seq_hdr_reqs); + } else { + msm_dcvs_check_and_scale_clocks(inst, false); + rc = call_hfi_op(hdev, session_ftb, + (void *) inst->session, &frame_data); + if (!rc) + msm_vidc_debugfs_update(inst, + MSM_VIDC_DEBUGFS_EVENT_FTB); + } + } else { + dprintk(VIDC_ERR, + "This capability is not supported: %d\n", + q->type); + rc = -EINVAL; + } + } +err_bad_input: + if (rc) + dprintk(VIDC_ERR, "Failed to queue buffer\n"); +err_no_mem: + return rc; +} + +int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst) +{ + struct buffer_requirements buf_req; + int rc = 0; + int 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, "%s Error rc:%d\n", __func__, rc); + return rc; + } + buf_req = hprop.buf_req; + memcpy(&inst->buff_req, &buf_req, + sizeof(struct buffer_requirements)); + for (i = 0; i < HAL_BUFFER_MAX; i++) { + dprintk(VIDC_DBG, + "buffer type: %d, count : %d, size: %d\n", + inst->buff_req.buffer[i].buffer_type, + inst->buff_req.buffer[i].buffer_count_actual, + inst->buff_req.buffer[i].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; + } + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID) { + dprintk(VIDC_ERR, + "Core is in bad state can't query get_bufreqs()\n"); + return -EAGAIN; + } + hdev = inst->core->device; + mutex_lock(&inst->sync_lock); + if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) { + dprintk(VIDC_ERR, + "%s Not in proper state\n", __func__); + rc = -EAGAIN; + goto exit; + } + init_completion( + &inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)]); + switch (ptype) { + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + rc = call_hfi_op(hdev, session_get_property, + (void *) inst->session, ptype); + if (rc) { + dprintk(VIDC_ERR, "%s Failed: PROFILE_LEVEL_INFO\n", + __func__); + rc = -EAGAIN; + goto exit; + } + break; + case HAL_PARAM_GET_BUFFER_REQUIREMENTS: + rc = call_hfi_op(hdev, session_get_buf_req, + (void *) inst->session); + if (rc) { + dprintk(VIDC_ERR, "Failed to get property\n"); + rc = -EAGAIN; + goto exit; + } + break; + default: + rc = -EAGAIN; + dprintk(VIDC_ERR, "%s id:%d not supported\n", __func__, ptype); + break; + } + rc = wait_for_completion_timeout( + &inst->completions[SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)], + msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, + "%s: Wait interrupted or timed out [%p]: %d\n", + __func__, inst, + SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)); + inst->state = MSM_VIDC_CORE_INVALID; + msm_comm_kill_session(inst); + rc = -EIO; + goto exit; + } + + 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); + rc = 0; + } else { + dprintk(VIDC_ERR, "%s getprop list empty\n", __func__); + rc = -EINVAL; + } + mutex_unlock(&inst->pending_getpropq.lock); +exit: + mutex_unlock(&inst->sync_lock); + 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 = %p\n", inst); + return -EINVAL; + } + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %p\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev); + return -EINVAL; + } + mutex_lock(&inst->outputbufs.lock); + list_for_each_entry_safe(buf, dummy, &inst->outputbufs.list, list) { + handle = buf->handle; + 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->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->handle); + 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->internalbufs.lock); + + list_for_each_entry(buf, &inst->internalbufs.list, list) { + if (!buf->handle) { + dprintk(VIDC_ERR, "%s: invalid buf handle\n", __func__); + mutex_unlock(&inst->internalbufs.lock); + goto not_sufficient; + } + if (buf->buffer_type == buffer_type && + buf->handle->size >= bufreq->buffer_size) + count++; + } + mutex_unlock(&inst->internalbufs.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 = %p\n", inst); + return -EINVAL; + } + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %p\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %p\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->internalbufs.lock); + list_for_each_entry_safe(buf, dummy, &inst->internalbufs.list, list) { + if (!buf->handle) { + dprintk(VIDC_ERR, "%s - buf->handle NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + handle = buf->handle; + 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; + init_completion(&inst->completions[SESSION_MSG_INDEX + (SESSION_RELEASE_BUFFER_DONE)]); + 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->internalbufs.lock); + rc = wait_for_sess_signal_receipt(inst, + SESSION_RELEASE_BUFFER_DONE); + if (rc) { + change_inst_state(inst, + MSM_VIDC_CORE_INVALID); + msm_comm_kill_session(inst); + } + mutex_lock(&inst->internalbufs.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, buf->handle); + kfree(buf); + } + +exit: + mutex_unlock(&inst->internalbufs.lock); + return rc; +} + +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 = %p\n", inst); + return -EINVAL; + } + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %p\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %p\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->handle; + 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; + init_completion( + &inst->completions[SESSION_MSG_INDEX + (SESSION_RELEASE_BUFFER_DONE)]); + 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, + 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, buf->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: %p\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_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH); + if (rc) + goto error; + + rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1); + if (rc) + goto error; + + rc = set_scratch_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2); + 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_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST); + if (rc) + goto error; + + rc = set_persist_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1); + 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); + 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->v4l2_planes[0].bytesused = 0; + vb->v4l2_planes[0].data_offset = 0; + + mutex_lock(&inst->bufq[port].lock); + 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); + return; +} + +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 = %p 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 list_head *ptr, *next; + struct vb2_buf_entry *temp; + struct mutex *lock; + struct msm_vidc_core *core; + struct hfi_device *hdev; + if (!inst) { + dprintk(VIDC_ERR, + "Invalid instance pointer = %p\n", inst); + return -EINVAL; + } + core = inst->core; + if (!core) { + dprintk(VIDC_ERR, + "Invalid core pointer = %p\n", core); + return -EINVAL; + } + hdev = core->device; + if (!hdev) { + dprintk(VIDC_ERR, "Invalid device pointer = %p\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) { + dprintk(VIDC_ERR, + "Core %p and inst %p 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); + rc = call_hfi_op(hdev, session_flush, inst->session, + HAL_FLUSH_OUTPUT); + if (!rc && msm_comm_get_stream_output_mode(inst) == + HAL_VIDEO_DECODER_SECONDARY) + rc = call_hfi_op(hdev, session_flush, inst->session, + HAL_FLUSH_OUTPUT2); + + } 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_safe(ptr, next, &inst->pendingq.list) { + temp = + list_entry(ptr, struct vb2_buf_entry, list); + if (temp->vb->v4l2_buf.type == + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + lock = &inst->bufq[CAPTURE_PORT].lock; + else + lock = &inst->bufq[OUTPUT_PORT].lock; + temp->vb->v4l2_planes[0].bytesused = 0; + mutex_lock(lock); + vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE); + mutex_unlock(lock); + list_del(&temp->list); + 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)) + 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_vidc_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_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; + 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: %p\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; + enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD | + LOAD_CALC_IGNORE_THUMBNAIL_LOAD; + + if (inst->state == MSM_VIDC_OPEN_DONE) { + 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 > inst->core->resources.max_load) { + dprintk(VIDC_ERR, + "H/W is overloaded. needed: %d max: %d\n", + num_mbs_per_sec, + inst->core->resources.max_load); + 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; + } else { + 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/output_height > x_min) { + dprintk(VIDC_ERR, + "Unsupported height downscale ratio %d vs %d\n", + input_height/output_height, x_min); + return -ENOTSUPP; + } + } else { + if (input_height/output_height > x_max) { + 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/output_width > y_min) { + dprintk(VIDC_ERR, + "Unsupported width downscale ratio %d vs %d\n", + input_width/output_width, y_min); + return -ENOTSUPP; + } + } else { + if (input_width/output_width > y_max) { + 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_core_capability *capability; + int rc = 0; + struct hfi_device *hdev; + struct msm_vidc_core *core; + + 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); + 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 && inst->capability.capability_set) { + 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] + * inst->prop.width[CAPTURE_PORT] > + capability->width.max * capability->height.max) { + dprintk(VIDC_ERR, + "Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n", + inst->prop.width[CAPTURE_PORT], + inst->prop.height[CAPTURE_PORT], + capability->width.max, capability->height.max); + rc = -ENOTSUPP; + } + } + if (rc) { + change_inst_state(inst, MSM_VIDC_CORE_INVALID); + dprintk(VIDC_ERR, + "%s: Resolution unsupported\n", __func__); + } + return rc; +} + +static void msm_comm_generate_session_error(struct msm_vidc_inst *inst) +{ + enum command_response cmd = SESSION_ERROR; + struct msm_vidc_cb_cmd_done response = {0}; + + dprintk(VIDC_WARN, "msm_comm_generate_session_error\n"); + 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 command_response cmd = 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); + } else { + dprintk(VIDC_WARN, + "Inactive session %p, triggering an internal session error\n", + inst); + msm_comm_generate_session_error(inst); + + } + + return rc; +} + +static inline int power_on_for_smem(struct msm_vidc_inst *inst) +{ + struct hfi_device *hdev = NULL; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s: invalid inst handle\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + rc = call_hfi_op(hdev, power_enable, hdev->hfi_device_data); + if (rc) + dprintk(VIDC_ERR, "%s: failed to power on fw\n", __func__); + return rc; +} + +struct msm_smem *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 *m = NULL; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid inst: %p\n", __func__, inst); + return NULL; + } + mutex_lock(&inst->core->lock); + if (power_on_for_smem(inst)) + goto err_power_on; + + m = msm_smem_alloc(inst->mem_client, size, align, + flags, buffer_type, map_kernel); +err_power_on: + mutex_unlock(&inst->core->lock); + return m; +} + +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: %p %p\n", __func__, inst, mem); + return; + } + mutex_lock(&inst->core->lock); + if (power_on_for_smem(inst)) + goto err_power_on; + + msm_smem_free(inst->mem_client, mem); +err_power_on: + mutex_unlock(&inst->core->lock); +} + +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: %p %p\n", __func__, inst, mem); + return -EINVAL; + } + return msm_smem_cache_operations(inst->mem_client, mem, cache_ops); +} + +struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst, + int fd, u32 offset, enum hal_buffer buffer_type) +{ + struct msm_smem *m = NULL; + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s: invalid inst: %p\n", __func__, inst); + return NULL; + } + mutex_lock(&inst->core->lock); + if (power_on_for_smem(inst)) + goto err_power_on; + + m = msm_smem_user_to_kernel(inst->mem_client, + fd, offset, buffer_type); +err_power_on: + mutex_unlock(&inst->core->lock); + return m; +} + +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; + + call_hfi_op(hdev, unload_fw, hdev->hfi_device_data); + dprintk(VIDC_DBG, "Firmware unloaded\n"); + } + 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}; + 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; + hal_fmt.buffer_type = buffer_type; + + switch (fourcc) { + case V4L2_PIX_FMT_NV12: + dprintk(VIDC_DBG, "set color format: nv12\n"); + hal_fmt.format = HAL_COLOR_FORMAT_NV12; + break; + case V4L2_PIX_FMT_NV21: + dprintk(VIDC_DBG, "set color format: nv21\n"); + hal_fmt.format = HAL_COLOR_FORMAT_NV21; + break; + case V4L2_PIX_FMT_NV12_UBWC: + dprintk(VIDC_DBG, "set color format: nv12_ubwc\n"); + hal_fmt.format = HAL_COLOR_FORMAT_NV12_UBWC; + break; + case V4L2_PIX_FMT_NV12_TP10_UBWC: + dprintk(VIDC_DBG, "set color format: 10bit nv12_ubwc\n"); + hal_fmt.format = HAL_COLOR_FORMAT_NV12_TP10_UBWC; + break; + default: + dprintk(VIDC_ERR, "%s default\n", __func__); + rc = -ENOTSUPP; + goto exit; + } + + 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"); + +exit: + return rc; +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h new file mode 100644 index 000000000000..9a1bf957319f --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2012-2014, 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; +}; + +enum load_calc_quirks { + LOAD_CALC_NO_QUIRKS = 0, + LOAD_CALC_IGNORE_TURBO_LOAD = 1 << 0, + LOAD_CALC_IGNORE_THUMBNAIL_LOAD = 1 << 1, +}; + +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 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); +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); +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_vidc_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_ID2CLASS(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); +struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst, + size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel); +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); +struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst, + int fd, u32 offset, enum hal_buffer buffer_type); +enum hal_video_codec get_hal_codec_type(int fourcc); +int msm_comm_load_fw(struct msm_vidc_core *core); +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); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c new file mode 100644 index 000000000000..3e9ef063d0a4 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c @@ -0,0 +1,617 @@ +/* Copyright (c) 2014 - 2015, 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 int msm_dcvs_check_supported(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; + + height = inst->prop.height[CAPTURE_PORT]; + width = inst->prop.width[CAPTURE_PORT]; + 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: %p\n", __func__, core); + return -EINVAL; + } + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->state >= MSM_VIDC_START_DONE && + inst->state < MSM_VIDC_STOP_DONE) + active_instances++; + } + mutex_unlock(&core->lock); + return active_instances; +} + +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) { + rc = msm_dcvs_check_supported(inst); + if (!rc) { + inst->dcvs_mode = true; + dprintk(VIDC_DBG, + "%s: session DCVS supported, enc_dcvs_mode = %d\n", + __func__, inst->dcvs_mode); + } else { + inst->dcvs_mode = false; + dprintk(VIDC_DBG, + "%s: session DCVS not supported, enc_dcvs_mode = %d\n", + __func__, inst->dcvs_mode); + } + + 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) + return; + + rc = msm_dcvs_check_supported(inst); + if (!rc) { + 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: %p\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; + + 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; + + return fw_out_qsize; +} + +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; + + dprintk(VIDC_DBG, "Init DCVS Load\n"); + + if (!inst || !inst->core) { + dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, inst); + return; + } + + core = inst->core; + dcvs = &inst->dcvs; + + dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS); + + if (dcvs->load >= DCVS_NOMINAL_LOAD) { + dcvs->load_low = DCVS_NOMINAL_LOAD; + dcvs->load_high = core->resources.max_load; + } + + 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 = DCVS_MIN_DISPLAY_BUFF; + 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: %p\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: %p\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 %p\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) + 1; + 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->threshold_disp_buf_high = + dcvs->threshold_disp_buf_low + + DCVS_BUFFER_SAFEGUARD; + } + 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_MIN_DISPLAY_BUFF && + buffers_outside_fw == DCVS_MIN_DISPLAY_BUFF) { + dcvs->transition_turbo = true; + } else if (buffers_outside_fw > DCVS_MIN_DISPLAY_BUFF && + (buffers_outside_fw - + (prev_buf_count - buffers_outside_fw)) + < DCVS_MIN_DISPLAY_BUFF){ + 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); + 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) + + (fbd ? 0 : 1); + + 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); + 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; +} + +bool msm_dcvs_enc_check(struct msm_vidc_inst *inst) +{ + int num_mbs_per_frame = 0; + bool dcvs_check_passed = false, is_codec_supported = false; + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid params\n", __func__); + return dcvs_check_passed; + } + + is_codec_supported = + (inst->fmts[CAPTURE_PORT]->fourcc == V4L2_PIX_FMT_H264) || + (inst->fmts[CAPTURE_PORT]->fourcc == V4L2_PIX_FMT_H264_NO_SC); + + num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst); + if (msm_vidc_enc_dcvs_mode && is_codec_supported && + inst->dcvs.is_power_save_mode && + IS_VALID_DCVS_SESSION(num_mbs_per_frame, + DCVS_MIN_SUPPORTED_MBPERFRAME)) { + dcvs_check_passed = true; + } + return dcvs_check_passed; +} + +static int msm_dcvs_check_supported(struct msm_vidc_inst *inst) +{ + int rc = 0; + int num_mbs_per_frame = 0; + int instance_count = 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; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__); + return -EINVAL; + } + + core = inst->core; + dcvs = &inst->dcvs; + instance_count = msm_dcvs_count_active_instances(core); + + if (instance_count == 1 && inst->session_type == MSM_VIDC_DECODER) { + num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst); + output_buf_req = get_buff_req_buffer(inst, + msm_comm_get_hal_output_buffer(inst)); + + is_codec_supported = + (inst->fmts[OUTPUT_PORT]->fourcc == + V4L2_PIX_FMT_H264) || + (inst->fmts[OUTPUT_PORT]->fourcc == + V4L2_PIX_FMT_HEVC) || + (inst->fmts[OUTPUT_PORT]->fourcc == + V4L2_PIX_FMT_VP8) || + (inst->fmts[OUTPUT_PORT]->fourcc == + V4L2_PIX_FMT_H264_NO_SC); + if (!is_codec_supported || + !IS_VALID_DCVS_SESSION(num_mbs_per_frame, + DCVS_MIN_SUPPORTED_MBPERFRAME)) + return -ENOTSUPP; + + if (!output_buf_req) { + dprintk(VIDC_ERR, + "%s: No buffer requirement for buffer type %x\n", + __func__, HAL_BUFFER_OUTPUT); + return -EINVAL; + } + } else if (instance_count == 1 && + inst->session_type == MSM_VIDC_ENCODER) { + if (!msm_dcvs_enc_check(inst) || + !inst->dcvs.is_additional_buff_added) + return -ENOTSUPP; + } else { + rc = -ENOTSUPP; + /* + * 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 rc; +} + +int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst, + bool is_input_buff) +{ + int extra_buffer = 0; + + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return -EINVAL; + } + + if (inst->session_type == MSM_VIDC_ENCODER) { + if (msm_dcvs_enc_check(inst)) { + if (is_input_buff && !inst->dcvs.is_input_buff_added) + extra_buffer = DCVS_ENC_EXTRA_INPUT_BUFFERS; + else if (!is_input_buff && + !inst->dcvs.is_output_buff_added) + extra_buffer = DCVS_ENC_EXTRA_OUTPUT_BUFFERS; + } + } + return extra_buffer; +} + +void msm_dcvs_set_buff_req_handled(struct msm_vidc_inst *inst, + bool is_input_buff) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s Invalid args\n", __func__); + return; + } + + if (inst->session_type == MSM_VIDC_ENCODER) { + if (msm_dcvs_enc_check(inst)) { + if (is_input_buff && !inst->dcvs.is_input_buff_added) + inst->dcvs.is_input_buff_added = true; + else if (!is_input_buff && + !inst->dcvs.is_output_buff_added) + inst->dcvs.is_output_buff_added = true; + + if (inst->dcvs.is_input_buff_added && + inst->dcvs.is_output_buff_added) { + inst->dcvs.is_additional_buff_added = true; + dprintk(VIDC_PROF, + "ENC_DCVS: additional i/p o/p buffer added"); + } + } + } +} + +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/msm_vidc_dcvs.h b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.h new file mode 100644 index 000000000000..b239c6084314 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2014, 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" + +/* Minimum number of display buffers */ +#define DCVS_MIN_DISPLAY_BUFF 4 +/* Low threshold for encoder dcvs */ +#define DCVS_ENC_LOW_THR 4 +/* High threshold for encoder dcvs */ +#define DCVS_ENC_HIGH_THR 9 +/* extra i/p buffers in case of encoder dcvs */ +#define DCVS_ENC_EXTRA_INPUT_BUFFERS 2 +/* extra o/p buffers in case of encoder dcvs */ +#define DCVS_ENC_EXTRA_OUTPUT_BUFFERS 2 +/* Default threshold to reduce the core frequency */ +#define DCVS_NOMINAL_THRESHOLD 8 +/* Default threshold to increase the core frequency */ +#define DCVS_TURBO_THRESHOLD 4 +/* Instance max load above which DCVS kicks in */ +#define DCVS_NOMINAL_LOAD NUM_MBS_PER_SEC(1088, 1920, 60) +/* Considering one safeguard buffer */ +#define DCVS_BUFFER_SAFEGUARD 1 +/* Supported DCVS MBs per frame */ +#define DCVS_MIN_SUPPORTED_MBPERFRAME NUM_MBS_PER_FRAME(2160, 3840) + +void msm_dcvs_init(struct msm_vidc_inst *inst); +void msm_dcvs_init_load(struct msm_vidc_inst *inst); +bool msm_dcvs_enc_check(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, + bool is_input_buff); +void msm_dcvs_set_buff_req_handled(struct msm_vidc_inst *inst, + bool is_input_buff); +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/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c new file mode 100644 index 000000000000..c7015d85f6fd --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -0,0 +1,403 @@ +/* Copyright (c) 2012-2015, 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_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; +int msm_vidc_fw_coverage = 0; +int msm_vidc_vpe_csc_601_to_709 = 0; +int msm_vidc_dec_dcvs_mode = 1; +int msm_vidc_enc_dcvs_mode = 1; +int msm_vidc_sys_idle_indicator = 0; +int msm_vidc_firmware_unload_delay = 15000; +int msm_vidc_thermal_mitigation_disabled = 0; + +#define MAX_DBG_BUF_SIZE 4096 + +struct debug_buffer { + char ptr[MAX_DBG_BUF_SIZE]; + char *curr; + u32 filled_size; +}; + +static struct debug_buffer dbg_buf; + +#define INIT_DBG_BUF(__buf) ({ \ + __buf.curr = __buf.ptr;\ + __buf.filled_size = 0; \ +}) + +#define DYNAMIC_BUF_OWNER(__binfo) ({ \ + atomic_read(&__binfo->ref_count) == 2 ? "video driver" : "firmware";\ +}) + +static int core_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...) +{ + va_list args; + u32 size; + va_start(args, fmt); + size = vscnprintf(buffer->curr, MAX_DBG_BUF_SIZE - 1, fmt, args); + va_end(args); + buffer->curr += size; + buffer->filled_size += size; + return size; +} + +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; + int i = 0; + if (!core || !core->device) { + dprintk(VIDC_ERR, "Invalid params, core: %p\n", core); + return 0; + } + hdev = core->device; + INIT_DBG_BUF(dbg_buf); + write_str(&dbg_buf, "===============================\n"); + write_str(&dbg_buf, "CORE %d: %p\n", core->id, core); + write_str(&dbg_buf, "===============================\n"); + write_str(&dbg_buf, "state: %d\n", core->state); + write_str(&dbg_buf, "base addr: %#x\n", + call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, + FW_BASE_ADDRESS)); + write_str(&dbg_buf, "register_base: %#x\n", + call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, + FW_REGISTER_BASE)); + write_str(&dbg_buf, "register_size: %u\n", + call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, + FW_REGISTER_SIZE)); + write_str(&dbg_buf, "irq: %u\n", + call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, + FW_IRQ)); + for (i = SYS_MSG_START; i < SYS_MSG_END; i++) { + write_str(&dbg_buf, "completions[%d]: %s\n", i, + completion_done(&core->completions[SYS_MSG_INDEX(i)]) ? + "pending" : "done"); + } + return simple_read_from_buffer(buf, count, ppos, + dbg_buf.ptr, dbg_buf.filled_size); +} + +static const struct file_operations core_info_fops = { + .open = core_info_open, + .read = core_info_read, +}; + +static int trigger_ssr_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) { + u32 ssr_trigger_val; + int rc; + struct msm_vidc_core *core = filp->private_data; + rc = sscanf(buf, "%d", &ssr_trigger_val); + if (rc < 0) { + dprintk(VIDC_WARN, "returning error err %d\n", rc); + rc = -EINVAL; + } else { + msm_vidc_trigger_ssr(core, ssr_trigger_val); + rc = count; + } + return rc; +} + +static const struct file_operations ssr_fops = { + .open = trigger_ssr_open, + .write = trigger_ssr_write, +}; + +struct dentry *msm_vidc_debugfs_init_drv(void) +{ + bool ok = false; + struct dentry *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, S_IRUGO | S_IWUSR, \ + 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, "enable_vpe_csc_601_709", + &msm_vidc_vpe_csc_601_to_709) && + __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); + +#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: %p\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", S_IRUGO, dir, core, &core_info_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } + if (!debugfs_create_file("trigger_ssr", S_IWUSR, + 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) +{ + file->private_data = inode->i_private; + return 0; +} + +static int publish_unreleased_reference(struct msm_vidc_inst *inst) +{ + 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) { + write_str(&dbg_buf, "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)) { + write_str(&dbg_buf, + "\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); + } + return 0; +} + +static ssize_t inst_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_vidc_inst *inst = file->private_data; + int i, j; + if (!inst) { + dprintk(VIDC_ERR, "Invalid params, core: %p\n", inst); + return 0; + } + INIT_DBG_BUF(dbg_buf); + write_str(&dbg_buf, "===============================\n"); + write_str(&dbg_buf, "INSTANCE: %p (%s)\n", inst, + inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder"); + write_str(&dbg_buf, "===============================\n"); + write_str(&dbg_buf, "core: %p\n", inst->core); + write_str(&dbg_buf, "height: %d\n", inst->prop.height[CAPTURE_PORT]); + write_str(&dbg_buf, "width: %d\n", inst->prop.width[CAPTURE_PORT]); + write_str(&dbg_buf, "fps: %d\n", inst->prop.fps); + write_str(&dbg_buf, "state: %d\n", inst->state); + write_str(&dbg_buf, "secure: %d\n", !!(inst->flags & VIDC_SECURE)); + write_str(&dbg_buf, "-----------Formats-------------\n"); + for (i = 0; i < MAX_PORT_NUM; i++) { + write_str(&dbg_buf, "capability: %s\n", i == OUTPUT_PORT ? + "Output" : "Capture"); + write_str(&dbg_buf, "name : %s\n", inst->fmts[i]->name); + write_str(&dbg_buf, "planes : %d\n", inst->fmts[i]->num_planes); + write_str( + &dbg_buf, "type: %s\n", inst->fmts[i]->type == OUTPUT_PORT ? + "Output" : "Capture"); + switch (inst->buffer_mode_set[i]) { + case HAL_BUFFER_MODE_STATIC: + write_str(&dbg_buf, "buffer mode : %s\n", "static"); + break; + case HAL_BUFFER_MODE_RING: + write_str(&dbg_buf, "buffer mode : %s\n", "ring"); + break; + case HAL_BUFFER_MODE_DYNAMIC: + write_str(&dbg_buf, "buffer mode : %s\n", "dynamic"); + break; + default: + write_str(&dbg_buf, "buffer mode : unsupported\n"); + } + + write_str(&dbg_buf, "count: %u\n", + inst->bufq[i].vb2_bufq.num_buffers); + + for (j = 0; j < inst->fmts[i]->num_planes; j++) + write_str(&dbg_buf, "size for plane %d: %u\n", j, + inst->bufq[i].vb2_bufq.plane_sizes[j]); + + if (i < MAX_PORT_NUM - 1) + write_str(&dbg_buf, "\n"); + } + write_str(&dbg_buf, "-------------------------------\n"); + for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) { + write_str(&dbg_buf, "completions[%d]: %s\n", i, + completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ? + "pending" : "done"); + } + write_str(&dbg_buf, "ETB Count: %d\n", inst->count.etb); + write_str(&dbg_buf, "EBD Count: %d\n", inst->count.ebd); + write_str(&dbg_buf, "FTB Count: %d\n", inst->count.ftb); + write_str(&dbg_buf, "FBD Count: %d\n", inst->count.fbd); + + publish_unreleased_reference(inst); + + return simple_read_from_buffer(buf, count, ppos, + dbg_buf.ptr, dbg_buf.filled_size); +} + +static const struct file_operations inst_info_fops = { + .open = inst_info_open, + .read = inst_info_read, +}; + +struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, + struct dentry *parent) +{ + struct dentry *dir = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + if (!inst) { + dprintk(VIDC_ERR, "Invalid params, inst: %p\n", inst); + goto failed_create_dir; + } + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", 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; + } + if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } + inst->debug.pdata[FRAME_PROCESSING].sampling = true; +failed_create_dir: + return dir; +} + +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/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h new file mode 100644 index 000000000000..a72691077250 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h @@ -0,0 +1,176 @@ +/* Copyright (c) 2012-2014, 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 <linux/debugfs.h> +#include <linux/delay.h> +#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: " + +/* 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 int msm_vidc_fw_coverage; +extern int msm_vidc_vpe_csc_601_to_709; +extern int msm_vidc_dec_dcvs_mode; +extern int msm_vidc_enc_dcvs_mode; +extern int msm_vidc_sys_idle_indicator; +extern int msm_vidc_firmware_unload_delay; +extern int msm_vidc_thermal_mitigation_disabled; + +#define VIDC_MSG_PRIO2STRING(__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; \ + } \ + \ + __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); \ + } else if (msm_vidc_debug_out == VIDC_OUT_FTRACE) { \ + trace_printk(KERN_DEBUG 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_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/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h new file mode 100644 index 000000000000..c1bdeda472b8 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -0,0 +1,382 @@ +/* Copyright (c) 2012-2015, 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 <linux/atomic.h> +#include <linux/list.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/completion.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <linux/msm-bus.h> +#include <linux/msm-bus-board.h> +#include <soc/qcom/ocmem.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ctrls.h> +#include <media/videobuf2-core.h> +#include <media/msm_vidc.h> +#include <media/msm_media_info.h> + +#include "vidc_hfi_api.h" +#include "vidc_hfi_api.h" + +#define MSM_VIDC_DRV_NAME "msm_vidc_driver" +#define MSM_VIDC_VERSION KERNEL_VERSION(1, 0, 0); +#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 + +/* 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 VIDC_EVENT_CHANGE +#define SYS_MSG_END SYS_DEBUG +#define SESSION_MSG_START SESSION_LOAD_RESOURCE_DONE +#define SESSION_MSG_END SESSION_PROPERTY_INFO +#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_LOADED, + 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); +} + +enum buffer_owner { + DRIVER, + FIRMWARE, + CLIENT, + MAX_OWNER +}; + +struct internal_buf { + struct list_head list; + enum hal_buffer buffer_type; + struct msm_smem *handle; + 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; +}; + +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; +}; + +struct buf_queue { + struct vb2_queue vb2_bufq; + struct mutex lock; +}; + +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; + bool is_output_buff_added; + bool is_input_buff_added; + bool is_additional_buff_added; +}; + +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 = 1 << 0, + VIDC_TURBO = 1 << 1, + VIDC_THUMBNAIL = 1 << 2, + VIDC_NOMINAL = 1 << 3, +}; + +struct msm_vidc_core_capability { + struct hal_capability_supported width; + struct hal_capability_supported height; + struct hal_capability_supported frame_rate; + u32 pixelprocess_capabilities; + struct hal_capability_supported scale_x; + struct hal_capability_supported scale_y; + struct hal_capability_supported hier_p; + struct hal_capability_supported ltr_count; + struct hal_capability_supported mbs_per_frame; + struct hal_capability_supported secure_output2_threshold; + u32 capability_set; + enum buffer_mode_type buffer_mode[MAX_PORT_NUM]; + u32 buffer_size_limit; +}; + +struct msm_vidc_core { + struct list_head list; + struct mutex lock; + int id; + void *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; + struct delayed_work fw_unload_work; +}; + +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 internalbufs; + struct msm_vidc_list persistbufs; + struct msm_vidc_list pending_getpropq; + struct msm_vidc_list outputbufs; + struct msm_vidc_list registeredbufs; + struct buffer_requirements buff_req; + void *mem_client; + 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; + struct dentry *debugfs_root; + struct vb2_buffer *vb2_seq_hdr; + void *priv; + struct msm_vidc_debug debug; + struct buf_count count; + struct dcvs_stats dcvs; + enum msm_vidc_modes flags; + struct msm_vidc_core_capability capability; + enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM]; + bool map_output_buffer; + atomic_t seq_hdr_reqs; + struct v4l2_ctrl **ctrls; + bool dcvs_mode; +}; + +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; + u32 menu_skip_mask; + u32 flags; + const char * const *qmenu; +}; + +void handle_cmd_response(enum 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 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]; + ion_phys_addr_t device_addr[VIDEO_MAX_PLANES]; + struct msm_smem *handle[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 buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list, + ion_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 output_buffer_cache_invalidate(struct msm_vidc_inst *inst, + struct buffer_info *binfo); +int qbuf_dynamic_buf(struct msm_vidc_inst *inst, + struct buffer_info *binfo); +struct buffer_info *get_registered_mmap_buf(struct msm_vidc_inst *inst, + struct v4l2_buffer *b, int *plane); +int unmap_and_deregister_buf(struct msm_vidc_inst *inst, + struct buffer_info *binfo); + +void msm_comm_handle_thermal_event(void); +void *msm_smem_new_client(enum smem_type mtype, + void *platform_resources); +struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags, + enum hal_buffer buffer_type, int map_kernel); +void msm_smem_free(void *clt, struct msm_smem *mem); +void msm_smem_delete_client(void *clt); +int msm_smem_cache_operations(void *clt, struct msm_smem *mem, + enum smem_cache_ops); +struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, + enum hal_buffer buffer_type); +struct context_bank_info *msm_smem_get_context_bank(void *clt, + bool is_secure, enum hal_buffer buffer_type); +void msm_vidc_fw_unload_handler(struct work_struct *work); +struct msm_smem* msm_smem_map_dma_buf(void* smem_client, + struct dma_buf* dbuf, enum hal_buffer type); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c new file mode 100644 index 000000000000..79ceaedbacf7 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -0,0 +1,963 @@ +/* Copyright (c) 2012-2015, 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 <asm/dma-iommu.h> +#include <linux/iommu.h> +#include <linux/qcom_iommu.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <media/videobuf2-dma-contig.h> +#include "msm_vidc_debug.h" +#include "msm_vidc_resources.h" +#include "msm_vidc_res_parse.h" +#include "venus_boot.h" + +enum clock_properties { + CLOCK_PROP_HAS_SCALING = 1 << 0, +}; + +static size_t get_u32_array_num_elements(struct platform_device *pdev, + char *name) +{ + struct device_node *np = pdev->dev.of_node; + 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 inline enum imem_type read_imem_type(struct platform_device *pdev) +{ + bool is_compatible(char *compat) + { + return !!of_find_compatible_node(NULL, NULL, compat); + } + + return is_compatible("qcom,msm-ocmem") ? IMEM_OCMEM : + is_compatible("qcom,msm-vmem") ? IMEM_VMEM : + IMEM_NONE; + +} + +int read_hfi_type(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int rc = 0; + const char *hfi_name = NULL; + + if (np) { + rc = of_property_read_string(np, "qcom,hfi", &hfi_name); + if (rc) { + dprintk(VIDC_ERR, + "Failed to read hfi from device tree\n"); + goto err_hfi_read; + } + if (!strcasecmp(hfi_name, "venus")) + rc = VIDC_HFI_VENUS; + else if (!strcasecmp(hfi_name, "q6")) + rc = VIDC_HFI_Q6; + else + rc = -EINVAL; + } else + rc = VIDC_HFI_Q6; + +err_hfi_read: + return rc; +} + +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_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) +{ + int i = 0; + for (i = 0; i < res->bus_set.count; i++) { + if (res->bus_set.bus_tbl[i].pdata) + msm_bus_cl_clear_pdata(res->bus_set.bus_tbl[i].pdata); + } +} + +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]; + + kfree(rinfo->name); + rinfo->name = NULL; + } + + /* The regulator table is one the few allocs that aren't managed, hence + * free it manually */ + kfree(res->regulator_set.regulator_tbl); + 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_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, "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, + "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_freq_table(struct msm_vidc_platform_resources *res) +{ + int rc = 0; + int num_elements = 0; + struct platform_device *pdev = res->pdev; + + /* A comparator to compare loads (needed later on) */ + 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; + } + + 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, "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_bus_vectors(struct msm_vidc_platform_resources *res) +{ + struct platform_device *pdev = res->pdev; + struct device_node *child_node, *bus_node; + struct bus_set *buses = &res->bus_set; + int rc = 0, c = 0; + u32 num_buses = 0; + + bus_node = of_find_node_by_name(pdev->dev.of_node, + "qcom,msm-bus-clients"); + if (!bus_node) { + /* Not a required property */ + dprintk(VIDC_DBG, "qcom,msm-bus-clients not found\n"); + rc = 0; + goto err_bad_node; + } + + for_each_child_of_node(bus_node, child_node) + ++num_buses; + + buses->bus_tbl = devm_kzalloc(&pdev->dev, sizeof(*buses->bus_tbl) * + num_buses, GFP_KERNEL); + if (!buses->bus_tbl) { + dprintk(VIDC_ERR, "%s: Failed to allocate memory\n", __func__); + rc = -ENOMEM; + goto err_bad_node; + } + + buses->count = num_buses; + c = 0; + + for_each_child_of_node(bus_node, child_node) { + bool passive = false; + u32 configs = 0; + struct bus_info *bus = &buses->bus_tbl[c]; + + passive = of_property_read_bool(child_node, "qcom,bus-passive"); + rc = of_property_read_u32(child_node, "qcom,bus-configs", + &configs); + if (rc) { + dprintk(VIDC_ERR, + "Failed to read qcom,bus-configs in %s: %d\n", + child_node->name, rc); + break; + } + + bus->passive = passive; + bus->sessions_supported = configs; + bus->pdata = msm_bus_pdata_from_node(pdev, child_node); + if (IS_ERR_OR_NULL(bus->pdata)) { + rc = PTR_ERR(bus->pdata) ?: -EBADHANDLE; + dprintk(VIDC_ERR, "Failed to get bus pdata: %d\n", rc); + break; + } + + dprintk(VIDC_DBG, "Bus %s supports: %x, passive: %d\n", + bus->pdata->name, bus->sessions_supported, + passive); + ++c; + } + + if (c < num_buses) { + for (c--; c >= 0; c--) + msm_bus_cl_clear_pdata(buses->bus_tbl[c].pdata); + + goto err_bad_node; + } + +err_bad_node: + 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, "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; + + 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; + struct device_node *regulator_node = NULL; + struct regulator_info *rinfo = NULL; + void *temp = NULL; + + /* 1) 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; + + /* 2) 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; + } + + /* 3) expand our table */ + temp = krealloc(regulators->regulator_tbl, + sizeof(*regulators->regulator_tbl) * + (regulators->count + 1), GFP_KERNEL); + if (!temp) { + rc = -ENOMEM; + dprintk(VIDC_ERR, + "Failed to alloc memory for regulator table\n"); + goto err_reg_tbl_alloc; + } + + regulators->regulator_tbl = temp; + regulators->count++; + + /* 4) populate regulator info */ + rinfo = ®ulators->regulator_tbl[regulators->count - 1]; + rinfo->name = kstrndup(domains_property->name, + supply - domains_property->name, GFP_KERNEL); + if (!rinfo->name) { + rc = -ENOMEM; + dprintk(VIDC_ERR, + "Failed to alloc memory for regulator name\n"); + goto err_reg_name_alloc; + } + + 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) { + /* Devices such as Q6 might not have any control over clocks + * hence have none specified, which is ok. */ + 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->count = res->load_freq_tbl_size; + vc->load_freq_tbl = res->load_freq_tbl; + } else { + vc->count = 0; + vc->load_freq_tbl = NULL; + } + + 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_WARN, "Failed to read firmware name: %d\n", rc); + + 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_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_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; + } +#if 0 + rc = msm_vidc_load_bus_vectors(res); +#else + rc = 0; +#endif + if (rc) { + dprintk(VIDC_ERR, "Failed to load bus vectors: %d\n", rc); + goto err_load_bus_vectors; + } + + 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 = 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; + } + + 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->early_fw_load = of_property_read_bool(pdev->dev.of_node, + "qcom,early-fw-load"); + dprintk(VIDC_DBG, "Early fw load = %s\n", + res->early_fw_load ? "yes" : "no"); + return rc; +err_load_max_hw_load: + 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_bus_vectors(res); +err_load_bus_vectors: + 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, bool old_smmu) +{ + int rc = 0; + int order = 0; + bool disable_htw = true; + + if (!cb || !cb->dev) { + dprintk(VIDC_ERR, + "%s: Invalid Input params\n", __func__); + return -EINVAL; + } + + if (cb->is_secure && old_smmu) { + cb->mapping = arm_iommu_create_mapping(&msm_iommu_sec_bus_type, + cb->addr_range.start, cb->addr_range.size, order); + } else { + cb->mapping = arm_iommu_create_mapping(&platform_bus_type, + cb->addr_range.start, cb->addr_range.size, order); + } + + if (IS_ERR_OR_NULL(cb->mapping)) { + dprintk(VIDC_ERR, "%s - failed to create mapping\n", __func__); + rc = PTR_ERR(cb->mapping) ?: -ENODEV; + goto remove_cb; + } + + rc = arm_iommu_attach_device(cb->dev, cb->mapping); + if (rc) { + dprintk(VIDC_ERR, "%s - Couldn't arm_iommu_attach_device\n", + __func__); + goto release_mapping; + } + +#if 0 + rc = iommu_domain_set_attr(cb->mapping->domain, + DOMAIN_ATTR_COHERENT_HTW_DISABLE, &disable_htw); +#else + (void) disable_htw; + rc = 0; +#endif + if (rc) { + dprintk(VIDC_ERR, "%s - disable coherent HTW failed: %s %d\n", + __func__, dev_name(cb->dev), rc); + goto detach_device; + } + + cb->alloc_ctx = vb2_dma_contig_init_ctx(cb->dev); + if (IS_ERR_OR_NULL(cb->alloc_ctx)) { + dprintk(VIDC_ERR, "%s - Failed to init dma contig ctx\n", + __func__); + rc = PTR_ERR(cb->alloc_ctx) ?: -ENOMEM; + goto detach_device; + } + + dprintk(VIDC_DBG, "Attached %s and created mapping\n", dev_name(cb->dev)); + dprintk(VIDC_DBG, + "Context bank name: %s, buffer_type: %#x, is_secure: %d, address range start: %#x, size: %#x, dev: %p, mapping: %p, alloc_ctx: %p", + cb->name, cb->buffer_type, cb->is_secure, cb->addr_range.start, + cb->addr_range.size, cb->dev, cb->mapping, cb->alloc_ctx); + + return rc; + +detach_device: + arm_iommu_detach_device(cb->dev); +release_mapping: + arm_iommu_release_mapping(cb->mapping); +remove_cb: + return rc; +} + +static int msm_vidc_populate_context_bank(struct device *dev, + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + struct context_bank_info *cb = NULL; + struct device_node *np = NULL; + struct device_node *phandle = NULL; + bool old_smmu = false; + + if (!dev || !res) { + 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, &res->context_banks); + + phandle = of_parse_phandle(np, "qcom,vidc-domain-phandle", 0); + if (phandle) { + dprintk(VIDC_DBG, "QSMMU entry found\n"); + old_smmu = true; + rc = of_property_read_string(phandle, "label", &cb->name); + if (rc) { + dprintk(VIDC_ERR, + "Failed to read cb label from phandle\n"); + goto err_setup_cb; + } + cb->dev = msm_iommu_get_ctx(cb->name); + if (IS_ERR_OR_NULL(cb->dev)) { + dprintk(VIDC_ERR, + "Failed to get context bank device for %s\n", + cb->name); + rc = PTR_ERR(cb->dev) ?: -ENODEV; + goto err_setup_cb; + } + + rc = of_property_read_u32_array(phandle, + "qcom,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(phandle, + "qcom,secure-domain"); + + rc = of_property_read_u32(np, "qcom,vidc-partition-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; + } + + } else { + cb->dev = dev; + 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, "secure-addr-range"); + 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 (dev %p) address start = %x address size = %x buffer_type = %x\n", + cb->name, cb->dev, cb->addr_range.start, + cb->addr_range.size, cb->buffer_type); + + rc = msm_vidc_setup_context_bank(cb, old_smmu); + if (rc) { + dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc); + goto err_setup_cb; + } + + return 0; + +err_setup_cb: + list_del(&cb->list); + return rc; +} + +int msm_vidc_probe_sub_devices(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; + } + + dprintk(VIDC_DBG, "Probing %s\n", dev_name(&pdev->dev)); + 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->resources); + if (rc) + dprintk(VIDC_ERR, "Failed to probe context bank\n"); + else + dprintk(VIDC_DBG, "Successfully probed context bank\n"); + } + return rc; +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h new file mode 100644 index 000000000000..2ab53b4d6112 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h @@ -0,0 +1,28 @@ + +/* Copyright (c) 2012-2014, 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 <linux/of.h> +#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 msm_vidc_probe_sub_devices(struct platform_device *pdev); +#endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h new file mode 100644 index 000000000000..af5e70e5ad23 --- /dev/null +++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h @@ -0,0 +1,147 @@ +/* Copyright (c) 2013-2015, 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 <linux/platform_device.h> +#include <media/msm_vidc.h> +#define MAX_BUFFER_TYPES 32 + + +struct load_freq_table { + u32 load; + u32 freq; + u32 supported_codecs; +}; + +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 dma_iommu_mapping *mapping; + void *alloc_ctx; +}; + +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; /* == has_scaling iff count != 0 */ + bool has_gating; +}; + +struct clock_set { + struct clock_info *clock_tbl; + u32 count; +}; + +struct bus_info { + struct msm_bus_scale_pdata *pdata; + u32 priv; + u32 sessions_supported; /* bitmask */ + bool passive; +}; + +struct bus_set { + struct bus_info *bus_tbl; + u32 count; +}; + +enum imem_type { + IMEM_NONE, + IMEM_OCMEM, + IMEM_VMEM, + IMEM_MAX, +}; + +struct msm_vidc_platform_resources { + phys_addr_t firmware_base; + phys_addr_t register_base; + uint32_t register_size; + uint32_t irq; + struct load_freq_table *load_freq_tbl; + uint32_t load_freq_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; + struct list_head context_banks; + bool early_fw_load; + bool thermal_mitigable; + const char *fw_name; + const char *hfi_version; +}; + +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/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c new file mode 100644 index 000000000000..8f697c514911 --- /dev/null +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -0,0 +1,1230 @@ +/* Copyright (c) 2013-2015, 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 <linux/slab.h> +#include <linux/iommu.h> +//#include <linux/msm_iommu_domains.h> +#include <soc/qcom/subsystem_restart.h> +#include "hfi_packetization.h" +#include "msm_vidc_debug.h" +#include "q6_hfi.h" +#include "vidc_hfi_api.h" + + +#if defined(CONFIG_MSM_QDSP6_APR) || defined(CONFIG_MSM_QDSP6_APRV2) +#include <linux/qdsp6v2/apr.h> + +static struct hal_device_data hal_ctxt; + +static int write_queue(void *info, u8 *packet) +{ + u32 packet_size_in_words, new_write_idx; + struct q6_iface_q_info *qinfo; + u32 empty_space, read_idx; + u32 *write_ptr; + + if (!info || !packet) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + qinfo = (struct q6_iface_q_info *) info; + + packet_size_in_words = (*(u32 *)packet) >> 2; + + if (!packet_size_in_words) { + dprintk(VIDC_ERR, "Zero packet size\n"); + return -ENODATA; + } + + read_idx = qinfo->read_idx; + + empty_space = (qinfo->write_idx >= read_idx) ? + (qinfo->q_size - (qinfo->write_idx - read_idx)) : + (read_idx - qinfo->write_idx); + if (empty_space <= packet_size_in_words) { + dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n", + empty_space, packet_size_in_words); + return -ENOTEMPTY; + } + + new_write_idx = (qinfo->write_idx + packet_size_in_words); + write_ptr = (u32 *)(qinfo->buffer + (qinfo->write_idx << 2)); + if (new_write_idx < qinfo->q_size) { + memcpy(write_ptr, packet, packet_size_in_words << 2); + } else { + new_write_idx -= qinfo->q_size; + memcpy(write_ptr, packet, (packet_size_in_words - + new_write_idx) << 2); + memcpy((void *)qinfo->buffer, + packet + ((packet_size_in_words - new_write_idx) << 2), + new_write_idx << 2); + } + qinfo->write_idx = new_write_idx; + return 0; +} + +static int read_queue(void *info, u8 *packet) +{ + u32 packet_size_in_words, new_read_idx; + u32 *read_ptr; + struct q6_iface_q_info *qinfo; + + if (!info || !packet) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + qinfo = (struct q6_iface_q_info *) info; + + if (qinfo->read_idx == qinfo->write_idx) + return -EPERM; + + read_ptr = (u32 *)(qinfo->buffer + (qinfo->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 = qinfo->read_idx + packet_size_in_words; + if (new_read_idx < qinfo->q_size) { + memcpy(packet, read_ptr, + packet_size_in_words << 2); + } else { + new_read_idx -= qinfo->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->buffer, + new_read_idx << 2); + } + + qinfo->read_idx = new_read_idx; + return 0; +} + +static int q6_hfi_iface_eventq_write(struct q6_hfi_device *device, void *pkt) +{ + struct q6_iface_q_info *q_info; + int rc = 0; + unsigned long flags = 0; + + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + q_info = &device->event_queue; + if (!q_info->buffer) { + dprintk(VIDC_ERR, "cannot write to shared Q\n"); + rc = -ENODATA; + goto err_q_write; + } + + spin_lock_irqsave(&q_info->lock, flags); + rc = write_queue(q_info, (u8 *)pkt); + if (rc) + dprintk(VIDC_ERR, "q6_hfi_iface_eventq_write: queue_full\n"); + + spin_unlock_irqrestore(&q_info->lock, flags); +err_q_write: + return rc; +} + +static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt) +{ + int rc = 0; + struct q6_iface_q_info *q_info; + unsigned long flags = 0; + + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + q_info = &device->event_queue; + + if (!q_info->buffer) { + dprintk(VIDC_ERR, "cannot read from shared Q\n"); + rc = -ENODATA; + goto read_error; + } + + spin_lock_irqsave(&q_info->lock, flags); + rc = read_queue(q_info, (u8 *)pkt); + if (rc) { + dprintk(VIDC_INFO, "q6_hfi_iface_eventq_read:queue_empty\n"); + rc = -ENODATA; + } + spin_unlock_irqrestore(&q_info->lock, flags); + +read_error: + return rc; +} + +static void q6_hfi_core_work_handler(struct work_struct *work) +{ + int rc = 0; + struct q6_hfi_device *device = container_of( + work, struct q6_hfi_device, vidc_worker); + u8 packet[VIDC_IFACEQ_MED_PKT_SIZE]; + + /* need to consume all the messages from the firmware */ + do { + rc = q6_hfi_iface_eventq_read(device, packet); + if (!rc) + hfi_process_msg_packet(device->callback, + device->device_id, + (struct vidc_hal_msg_pkt_hdr *) packet, + &device->sess_head, &device->session_lock); + } while (!rc); + + if (rc != -ENODATA) + dprintk(VIDC_ERR, "Failed to read from event queue\n"); +} + +static int q6_hfi_init_resources(struct q6_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + if (!device || !res) { + dprintk(VIDC_ERR, "Invalid device or resources\n"); + return -EINVAL; + } + + device->res = res; + return 0; +} + +static void *q6_hfi_add_device(u32 device_id, + hfi_cmd_response_callback callback) +{ + struct q6_hfi_device *hdevice = NULL; + + if (!callback) { + dprintk(VIDC_ERR, "Invalid Paramters\n"); + return NULL; + } + + hdevice = (struct q6_hfi_device *) + kzalloc(sizeof(struct q6_hfi_device), GFP_KERNEL); + if (!hdevice) { + dprintk(VIDC_ERR, "failed to allocate new device\n"); + goto err_alloc; + } + + hdevice->device_id = device_id; + hdevice->callback = callback; + + dprintk(VIDC_DBG, "q6_hfi_add_device device_id %d\n", device_id); + + INIT_WORK(&hdevice->vidc_worker, q6_hfi_core_work_handler); + hdevice->vidc_workq = create_singlethread_workqueue( + "msm_vidc_workerq_q6"); + if (!hdevice->vidc_workq) { + dprintk(VIDC_ERR, ": create workq failed\n"); + goto error_createq; + } + + if (!hal_ctxt.dev_count) + INIT_LIST_HEAD(&hal_ctxt.dev_head); + + INIT_LIST_HEAD(&hdevice->list); + list_add_tail(&hdevice->list, &hal_ctxt.dev_head); + hal_ctxt.dev_count++; + + return (void *) hdevice; +error_createq: + kfree(hdevice); +err_alloc: + return NULL; +} + +static void *q6_hfi_get_device(u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + struct q6_hfi_device *device; + int rc = 0; + + if (!callback) { + dprintk(VIDC_ERR, "%s Invalid params: %p\n", + __func__, callback); + return NULL; + } + + device = q6_hfi_add_device(device_id, &handle_cmd_response); + if (!device) { + dprintk(VIDC_ERR, "Failed to create HFI device\n"); + return NULL; + } + + rc = q6_hfi_init_resources(device, res); + if (rc) { + if (rc != -EPROBE_DEFER) + dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); + goto err_fail_init_res; + } + + device->pkt_ops = hfi_get_pkt_ops_handle(HFI_PACKETIZATION_LEGACY); + if (!device->pkt_ops) { + dprintk(VIDC_ERR, "Failed to get pkt_ops handle\n"); + goto err_fail_init_res; + } + + return device; + +err_fail_init_res: + q6_hfi_delete_device(device); + return ERR_PTR(rc); +} + +void q6_hfi_delete_device(void *device) +{ + struct q6_hfi_device *close, *tmp, *dev; + + if (device) { + dev = (struct q6_hfi_device *) device; + list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) { + if (close->device_id == dev->device_id) { + hal_ctxt.dev_count--; + list_del(&close->list); + destroy_workqueue(close->vidc_workq); + kfree(close); + break; + } + } + + } +} + +static inline void q6_hfi_add_apr_hdr(struct q6_hfi_device *dev, + struct apr_hdr *hdr, u32 pkt_size) +{ + hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(sizeof(struct apr_hdr)), + APR_PKT_VER); + + hdr->src_svc = ((struct apr_svc *)dev->apr)->id; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->dest_svc = APR_SVC_VIDC; + hdr->src_port = 0; + hdr->dest_port = 0; + hdr->pkt_size = pkt_size; + hdr->token = 0; + hdr->opcode = VIDEO_HFI_CMD_ID; +} + +static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv) +{ + struct q6_hfi_device *device = priv; + struct hfi_msg_event_notify_packet pkt = {0}; + void *payload = NULL; + int rc = 0; + + if (!data || !device) { + dprintk(VIDC_ERR, "%s - Invalid arguments\n", __func__); + return -EINVAL; + } + + dprintk(VIDC_DBG, "%s opcode = %u payload size = %u\n", __func__, + data->opcode, data->payload_size); + + if (data->opcode == RESET_EVENTS) { + dprintk(VIDC_ERR, "%s Received subsystem reset event: %d\n", + __func__, data->reset_event); + pkt.packet_type = HFI_MSG_EVENT_NOTIFY; + pkt.size = sizeof(pkt); + pkt.event_id = HFI_EVENT_SYS_ERROR; + pkt.event_data1 = data->opcode; + pkt.event_data2 = data->reset_event; + payload = &pkt; + } else if (data->payload_size > 0) { + payload = data->payload; + } else { + dprintk(VIDC_ERR, "%s - Invalid payload size\n", __func__); + return -EINVAL; + } + + rc = q6_hfi_iface_eventq_write(device, payload); + if (rc) { + dprintk(VIDC_ERR, "%s failed to write to event queue\n", + __func__); + return rc; + } + queue_work(device->vidc_workq, &device->vidc_worker); + return 0; +} + +static void q6_release_event_queue(struct q6_hfi_device *device) +{ + kfree(device->event_queue.buffer); + device->event_queue.buffer = NULL; + device->event_queue.q_size = 0; + device->event_queue.read_idx = 0; + device->event_queue.write_idx = 0; +} + +static int q6_init_event_queue(struct q6_hfi_device *dev) +{ + struct q6_iface_q_info *iface_q; + + if (!dev) { + dprintk(VIDC_ERR, "Invalid device\n"); + return -EINVAL; + } + + iface_q = &dev->event_queue; + iface_q->buffer = kzalloc(Q6_IFACEQ_QUEUE_SIZE, GFP_KERNEL); + if (!iface_q->buffer) { + dprintk(VIDC_ERR, "iface_q alloc failed\n"); + q6_release_event_queue(dev); + return -ENOMEM; + } else { + iface_q->q_size = Q6_IFACEQ_QUEUE_SIZE / 4; + iface_q->read_idx = 0; + iface_q->write_idx = 0; + spin_lock_init(&iface_q->lock); + } + return 0; +} + +static int q6_hfi_core_init(void *device) +{ + struct q6_apr_cmd_sys_init_packet apr; + int rc = 0; + struct q6_hfi_device *dev = device; + + if (!dev) { + dprintk(VIDC_ERR, "%s: invalid argument\n", __func__); + return -ENODEV; + } + + INIT_LIST_HEAD(&dev->sess_head); + mutex_init(&dev->session_lock); + + if (!dev->event_queue.buffer) { + rc = q6_init_event_queue(dev); + if (rc) { + dprintk(VIDC_ERR, "q6_init_event_queue failed\n"); + goto err_core_init; + } + } else { + dprintk(VIDC_ERR, "queue buffer exists\n"); + rc = -EEXIST; + goto err_core_init; + } + + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, sys_init, &apr.pkt, HFI_VIDEO_ARCH_OX); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sys init pkt\n"); + goto err_core_init; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_core_init: + return rc; +} + +static int q6_hfi_core_release(void *device) +{ + struct q6_hfi_device *dev = device; + + if (!dev) { + dprintk(VIDC_ERR, "%s: invalid argument\n", __func__); + return -ENODEV; + } + q6_release_event_queue(dev); + + dprintk(VIDC_DBG, "HAL exited\n"); + return 0; +} + +static void *q6_hfi_session_init(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type) +{ + struct q6_apr_cmd_sys_session_init_packet apr; + struct hal_session *new_session; + struct q6_hfi_device *dev = device; + int rc = 0; + + if (!dev) { + dprintk(VIDC_ERR, "%s: invalid argument\n", __func__); + return NULL; + } + + new_session = (struct hal_session *) + kzalloc(sizeof(struct hal_session), GFP_KERNEL); + if (!new_session) { + dprintk(VIDC_ERR, "new session fail: Out of memory\n"); + return NULL; + } + new_session->session_id = session_id; + new_session->is_decoder = (session_type == HAL_VIDEO_DOMAIN_DECODER); + new_session->device = dev; + + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + if (call_hfi_pkt_op(dev, session_init, + &apr.pkt, new_session, session_type, codec_type)) { + dprintk(VIDC_ERR, "session_init: failed to create packet\n"); + goto err_session_init; + } + /* + * Add session id to the list entry and then send the apr pkt. + * This will avoid scenarios where apr_send_pkt is taking more + * time and Q6 is returning an ack even before the session id + * gets added to the session list. + */ + mutex_lock(&dev->session_lock); + list_add_tail(&new_session->list, &dev->sess_head); + mutex_unlock(&dev->session_lock); + + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + /* Delete the session id as the send pkt is not successful */ + mutex_lock(&dev->session_lock); + list_del(&new_session->list); + mutex_unlock(&dev->session_lock); + rc = -EBADE; + goto err_session_init; + } + + return new_session; + +err_session_init: + kfree(new_session); + return NULL; +} + +static int q6_hal_send_session_cmd(void *sess, + int pkt_type) +{ + struct q6_apr_session_cmd_pkt apr; + int rc = 0; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "%s: invalid arguments\n", __func__); + return -EINVAL; + } + dev = session->device; + + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, session_cmd, &apr.pkt, pkt_type, session); + if (rc) { + dprintk(VIDC_ERR, "send session cmd: create pkt failed\n"); + goto err_create_pkt; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_end(void *session) +{ + return q6_hal_send_session_cmd(session, + HFI_CMD_SYS_SESSION_END); +} + +static int q6_hfi_session_abort(void *session) +{ + return q6_hal_send_session_cmd(session, + HFI_CMD_SYS_SESSION_ABORT); +} + +static int q6_hfi_session_clean(void *session) +{ + struct hal_session *sess_close; + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + sess_close = session; + dprintk(VIDC_DBG, "deleted the session: %p\n", + sess_close->session_id); + mutex_lock(&((struct q6_hfi_device *) + sess_close->device)->session_lock); + list_del(&sess_close->list); + mutex_unlock(&((struct q6_hfi_device *) + sess_close->device)->session_lock); + kfree(sess_close); + return 0; +} + +static int q6_hfi_session_set_buffers(void *sess, + struct vidc_buffer_addr_info *buffer_info) +{ + struct q6_apr_cmd_session_set_buffers_packet *apr; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + int rc = 0; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !buffer_info || !session->device) { + dprintk(VIDC_ERR, "%s: invalid arguments\n", __func__); + return -EINVAL; + } + dev = session->device; + + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) + return 0; + apr = (struct q6_apr_cmd_session_set_buffers_packet *)packet; + + q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE); + + + rc = call_hfi_pkt_op(dev, session_set_buffers, + &apr->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); + rc = apr_send_pkt(dev->apr, (uint32_t *)apr); + if (rc != apr->hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_release_buffers(void *sess, + struct vidc_buffer_addr_info *buffer_info) +{ + struct q6_apr_cmd_session_release_buffer_packet *apr; + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + int rc = 0; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !buffer_info || !session->device) { + dprintk(VIDC_ERR, "%s: invalid arguments\n", __func__); + return -EINVAL; + } + + dev = session->device; + + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) + return 0; + + apr = (struct q6_apr_cmd_session_release_buffer_packet *) packet; + + q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE); + + rc = call_hfi_pkt_op(dev, session_release_buffers, + &apr->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); + rc = apr_send_pkt(dev->apr, (uint32_t *)apr); + + if (rc != apr->hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_load_res(void *sess) +{ + return q6_hal_send_session_cmd(sess, + HFI_CMD_SESSION_LOAD_RESOURCES); +} + +static int q6_hfi_session_release_res(void *sess) +{ + return q6_hal_send_session_cmd(sess, + HFI_CMD_SESSION_RELEASE_RESOURCES); +} + +static int q6_hfi_session_start(void *sess) +{ + return q6_hal_send_session_cmd(sess, + HFI_CMD_SESSION_START); +} + +static int q6_hfi_session_stop(void *sess) +{ + return q6_hal_send_session_cmd(sess, + HFI_CMD_SESSION_STOP); +} + +static int q6_hfi_session_etb(void *sess, + struct vidc_frame_data *input_frame) +{ + int rc = 0; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !input_frame || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + dev = session->device; + + if (session->is_decoder) { + struct q6_apr_cmd_session_empty_buffer_compressed_packet apr; + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, session_etb_decoder, + &apr.pkt, session, input_frame); + if (rc) { + dprintk(VIDC_ERR, + "Session etb decoder: failed to create pkt\n"); + goto err_create_pkt; + } + dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER\n"); + dprintk(VIDC_DBG, "addr = %pa ts = %lld\n", + &input_frame->device_addr, input_frame->timestamp); + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; + } else { + struct + q6_apr_cmd_session_empty_buffer_uncompressed_plane0_packet apr; + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, session_etb_encoder, + &apr.pkt, session, input_frame); + if (rc) { + dprintk(VIDC_ERR, + "Session etb encoder: failed to create pkt\n"); + goto err_create_pkt; + } + dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER\n"); + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; + } +err_create_pkt: + return rc; +} + +static int q6_hfi_session_ftb(void *sess, + struct vidc_frame_data *output_frame) +{ + struct q6_apr_cmd_session_fill_buffer_packet apr; + int rc = 0; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !output_frame || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + dev = session->device; + + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, session_ftb, + &apr.pkt, session, output_frame); + if (rc) { + dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n"); + goto err_create_pkt; + } + + dprintk(VIDC_INFO, "Q OUTPUT BUFFER\n"); + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_parse_seq_hdr(void *sess, + struct vidc_seq_hdr *seq_hdr) +{ + struct q6_apr_cmd_session_parse_sequence_header_packet *apr; + int rc = 0; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !seq_hdr || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + dev = session->device; + + apr = (struct q6_apr_cmd_session_parse_sequence_header_packet *) packet; + + q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_SMALL_PKT_SIZE); + + rc = call_hfi_pkt_op(dev, session_parse_seq_header, + &apr->pkt, session, seq_hdr); + if (rc) { + dprintk(VIDC_ERR, + "Session parse seq hdr: failed to create pkt\n"); + goto err_create_pkt; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)apr); + if (rc != apr->hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_get_seq_hdr(void *sess, + struct vidc_seq_hdr *seq_hdr) +{ + struct q6_apr_cmd_session_get_sequence_header_packet *apr; + int rc = 0; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !seq_hdr || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + dev = session->device; + + apr = (struct q6_apr_cmd_session_get_sequence_header_packet *) packet; + + q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_SMALL_PKT_SIZE); + + rc = call_hfi_pkt_op(dev, session_get_seq_hdr, + &apr->pkt, session, seq_hdr); + if (rc) { + dprintk(VIDC_ERR, "Session get seqhdr: failed to create pkt\n"); + goto err_create_pkt; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)apr); + if (rc != apr->hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_get_buf_req(void *sess) +{ + struct q6_apr_cmd_session_get_property_packet apr; + int rc = 0; + struct hal_session *session = sess; + + struct q6_hfi_device *dev; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + dev = session->device; + + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, session_get_buf_req, + &apr.pkt, session); + if (rc) { + dprintk(VIDC_ERR, "Session get bufreq: failed to create pkt\n"); + goto err_create_pkt; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; +err_create_pkt: + return rc; +} + +static int q6_hfi_session_flush(void *sess, enum hal_flush flush_mode) +{ + struct q6_apr_cmd_session_flush_packet apr; + int rc = 0; + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + dev = session->device; + + q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr)); + + rc = call_hfi_pkt_op(dev, session_flush, + &apr.pkt, session, flush_mode); + if (rc) { + dprintk(VIDC_ERR, "Session flush: failed to create pkt\n"); + goto err_create_pkt; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); + if (rc != apr.hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; + +err_create_pkt: + return rc; +} + +static int q6_hfi_session_set_property(void *sess, + enum hal_property ptype, void *pdata) +{ + u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; + struct q6_apr_cmd_session_set_property_packet *apr = + (struct q6_apr_cmd_session_set_property_packet *) &packet; + struct hal_session *session = sess; + int rc = 0; + struct q6_hfi_device *dev; + + if (!session || !pdata || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + if (ptype == HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER) { + dprintk(VIDC_WARN, "Smoothstreaming is not supported\n"); + return -ENOTSUPP; + } + + dev = session->device; + dprintk(VIDC_DBG, "in set_prop,with prop id: %#x\n", ptype); + + q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE); + + rc = call_hfi_pkt_op(dev, session_set_property, + &apr->pkt, session, ptype, pdata); + if (rc) { + dprintk(VIDC_ERR, "set property: failed to create packet\n"); + goto err_create_pkt; + } + + rc = apr_send_pkt(dev->apr, (uint32_t *)apr); + if (rc != apr->hdr.pkt_size) { + dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n", + __func__, rc); + rc = -EBADE; + } else + rc = 0; + +err_create_pkt: + return rc; +} + +static int q6_hfi_session_get_property(void *sess, + enum hal_property ptype) +{ + struct hal_session *session = sess; + struct q6_hfi_device *dev; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + dev = session->device; + + dprintk(VIDC_DBG, "IN func: , with property id: %d\n", ptype); + + switch (ptype) { + case HAL_CONFIG_FRAME_RATE: + break; + case HAL_PARAM_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: + break; + case HAL_CONFIG_REALTIME: + break; + case HAL_PARAM_BUFFER_COUNT_ACTUAL: + break; + case HAL_PARAM_NAL_STREAM_FORMAT_SELECT: + break; + case HAL_PARAM_VDEC_OUTPUT_ORDER: + break; + case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE: + break; + case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO: + break; + case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER: + break; + case HAL_PARAM_VDEC_MULTI_STREAM: + break; + case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT: + break; + case HAL_PARAM_DIVX_FORMAT: + break; + case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING: + break; + case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER: + break; + case HAL_CONFIG_VDEC_MB_ERROR_MAP: + break; + case HAL_CONFIG_VENC_REQUEST_IFRAME: + break; + case HAL_PARAM_VENC_MPEG4_SHORT_HEADER: + break; + case HAL_PARAM_VENC_MPEG4_AC_PREDICTION: + break; + case HAL_CONFIG_VENC_TARGET_BITRATE: + break; + case HAL_PARAM_PROFILE_LEVEL_CURRENT: + break; + case HAL_PARAM_VENC_H264_ENTROPY_CONTROL: + break; + case HAL_PARAM_VENC_RATE_CONTROL: + break; + case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION: + break; + case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION: + break; + case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL: + break; + case HAL_PARAM_VENC_SESSION_QP: + break; + case HAL_CONFIG_VENC_INTRA_PERIOD: + break; + case HAL_CONFIG_VENC_IDR_PERIOD: + break; + case HAL_CONFIG_VPE_OPERATIONS: + break; + case HAL_PARAM_VENC_INTRA_REFRESH: + break; + case HAL_PARAM_VENC_MULTI_SLICE_CONTROL: + break; + case HAL_CONFIG_VPE_DEINTERLACE: + break; + case HAL_SYS_DEBUG_CONFIG: + 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_SLICE_DELIVERY_MODE: + 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_VENC_LOW_LATENCY: + default: + dprintk(VIDC_INFO, "DEFAULT: Calling %#x\n", ptype); + break; + } + return 0; +} + +static int q6_hfi_load_fw(void *dev) +{ + int rc = 0; + struct q6_hfi_device *device = dev; + + if (!device) + return -EINVAL; + + trace_msm_v4l2_vidc_fw_load_start("msm_v4l2_vidc adsp_fw load start"); + if (!device->resources.fw.cookie) + device->resources.fw.cookie = subsystem_get("adsp"); + + if (IS_ERR_OR_NULL(device->resources.fw.cookie)) { + dprintk(VIDC_ERR, "Failed to download firmware\n"); + rc = -ENOMEM; + goto fail_subsystem_get; + } + + /*Set Q6 to loaded state*/ + apr_set_q6_state(APR_SUBSYS_LOADED); + + device->apr = apr_register("ADSP", "VIDC", + (apr_fn)q6_hfi_apr_callback, + 0xFFFFFFFF, + device); + + if (device->apr == NULL) { + dprintk(VIDC_ERR, "Failed to register with QDSP6\n"); + rc = -EINVAL; + goto fail_apr_register; + } + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc adsp_fw load end"); + return rc; +fail_apr_register: + subsystem_put(device->resources.fw.cookie); + device->resources.fw.cookie = NULL; +fail_subsystem_get: + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc adsp_fw load end"); + return rc; +} + +static void q6_hfi_unload_fw(void *hfi_device_data) +{ + struct q6_hfi_device *device = hfi_device_data; + + if (!device) + return; + + if (device->resources.fw.cookie) { + subsystem_put(device->resources.fw.cookie); + device->resources.fw.cookie = NULL; + } + + if (device->apr) { + if (apr_deregister(device->apr)) + dprintk(VIDC_ERR, "Failed to deregister APR\n"); + device->apr = NULL; + } +} + +static int q6_hfi_get_stride_scanline(int color_fmt, + int width, int height, int *stride, int *scanlines) { + *stride = VENUS_Y_STRIDE(color_fmt, width); + *scanlines = VENUS_Y_SCANLINES(color_fmt, height); + return 0; +} + +static void q6_init_hfi_callbacks(struct hfi_device *hdev) +{ + hdev->core_init = q6_hfi_core_init; + hdev->core_release = q6_hfi_core_release; + hdev->session_init = q6_hfi_session_init; + hdev->session_end = q6_hfi_session_end; + hdev->session_abort = q6_hfi_session_abort; + hdev->session_clean = q6_hfi_session_clean; + hdev->session_set_buffers = q6_hfi_session_set_buffers; + hdev->session_release_buffers = q6_hfi_session_release_buffers; + hdev->session_load_res = q6_hfi_session_load_res; + hdev->session_release_res = q6_hfi_session_release_res; + hdev->session_start = q6_hfi_session_start; + hdev->session_stop = q6_hfi_session_stop; + hdev->session_etb = q6_hfi_session_etb; + hdev->session_ftb = q6_hfi_session_ftb; + hdev->session_parse_seq_hdr = q6_hfi_session_parse_seq_hdr; + hdev->session_get_seq_hdr = q6_hfi_session_get_seq_hdr; + hdev->session_get_buf_req = q6_hfi_session_get_buf_req; + hdev->session_flush = q6_hfi_session_flush; + hdev->session_set_property = q6_hfi_session_set_property; + hdev->session_get_property = q6_hfi_session_get_property; + hdev->load_fw = q6_hfi_load_fw; + hdev->unload_fw = q6_hfi_unload_fw; + hdev->get_stride_scanline = q6_hfi_get_stride_scanline; +} + + +int q6_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: %p %p %p\n", + hdev, res, callback); + rc = -EINVAL; + goto err_hfi_init; + } + hdev->hfi_device_data = q6_hfi_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_hfi_init; + } + + q6_init_hfi_callbacks(hdev); + +err_hfi_init: + return rc; +} + +#else +int q6_hfi_initialize(struct hfi_device *hdev, u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + return -ENODEV; +} + +void q6_hfi_delete_device(void *device) +{ + /* Nothing to do! */ +} +#endif diff --git a/drivers/media/platform/msm/vidc/q6_hfi.h b/drivers/media/platform/msm/vidc/q6_hfi.h new file mode 100644 index 000000000000..7cee75a7b553 --- /dev/null +++ b/drivers/media/platform/msm/vidc/q6_hfi.h @@ -0,0 +1,133 @@ +/* Copyright (c) 2013-2014, 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 __Q6_HFI_H__ +#define __Q6_HFI_H__ + +#include "vidc_hfi.h" + +#if defined(CONFIG_MSM_QDSP6_APR) || defined(CONFIG_MSM_QDSP6_APRV2) +#include <linux/qdsp6v2/apr.h> +#include "vidc_hfi_helper.h" +#include "msm_vidc_resources.h" + +#define Q6_IFACEQ_QUEUE_SIZE (8 * 1024) + +/* client to Q6 communication path : forward path */ +#define VIDEO_HFI_CMD_ID 0x00012ECC + +/* Q6 to client ACK msg: reverse path*/ +#define VIDEO_HFI_MSG_ID 0x00012ECD + +/* Q6 to client event notifications */ +#define VIDEO_HFI_EVT_ID 0x00012ECE + +struct q6_resources { + struct msm_vidc_fw fw; +}; + +struct q6_iface_q_info { + spinlock_t lock; + u32 q_size; + u32 read_idx; + u32 write_idx; + u8 *buffer; +}; + +struct q6_hfi_device { + struct list_head list; + struct list_head sess_head; + struct q6_iface_q_info event_queue; + struct workqueue_struct *vidc_workq; + struct work_struct vidc_worker; + u32 device_id; + msm_vidc_callback callback; + struct q6_resources resources; + struct msm_vidc_platform_resources *res; + void *apr; + struct mutex session_lock; + struct hfi_packetization_ops *pkt_ops; +}; + +struct q6_apr_cmd_sys_init_packet { + struct apr_hdr hdr; + struct hfi_cmd_sys_init_packet pkt; +}; + +struct q6_apr_cmd_sys_session_init_packet { + struct apr_hdr hdr; + struct hfi_cmd_sys_session_init_packet pkt; +}; + +struct q6_apr_session_cmd_pkt { + struct apr_hdr hdr; + struct vidc_hal_session_cmd_pkt pkt; +}; + +struct q6_apr_cmd_session_set_buffers_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_set_buffers_packet pkt; +}; + +struct q6_apr_cmd_session_release_buffer_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_release_buffer_packet pkt; +}; + +struct q6_apr_cmd_session_empty_buffer_compressed_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_empty_buffer_compressed_packet pkt; +}; + +struct q6_apr_cmd_session_empty_buffer_uncompressed_plane0_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet pkt; +}; + +struct q6_apr_cmd_session_fill_buffer_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_fill_buffer_packet pkt; +}; + +struct q6_apr_cmd_session_parse_sequence_header_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_parse_sequence_header_packet pkt; +}; + +struct q6_apr_cmd_session_get_sequence_header_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_get_sequence_header_packet pkt; +}; + +struct q6_apr_cmd_session_get_property_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_get_property_packet pkt; +}; + +struct q6_apr_cmd_session_flush_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_flush_packet pkt; +}; + +struct q6_apr_cmd_session_set_property_packet { + struct apr_hdr hdr; + struct hfi_cmd_session_set_property_packet pkt; +}; + +#endif /* APR */ +int q6_hfi_initialize(struct hfi_device *hdev, u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback); + +void q6_hfi_delete_device(void *device); + +#endif /*#ifndef __Q6_HFI_H__ */ diff --git a/drivers/media/platform/msm/vidc/venus_boot.c b/drivers/media/platform/msm/vidc/venus_boot.c new file mode 100644 index 000000000000..63ef98cf2b9a --- /dev/null +++ b/drivers/media/platform/msm/vidc/venus_boot.c @@ -0,0 +1,520 @@ +/* Copyright (c) 2014-2015, 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 <asm/dma-iommu.h> +#include <asm/page.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iommu.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +//#include <linux/msm_iommu_domains.h> +#include <linux/of.h> +#include <linux/platform_device.h> +//#include <linux/qcom_iommu.h> +#include <linux/regulator/consumer.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <soc/qcom/subsystem_notif.h> +#include <soc/qcom/subsystem_restart.h> +#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 dma_iommu_mapping *mapping; + 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); + } +} + +#if 0 +static int venus_setup_cb(struct device *dev, + u32 size) +{ + int order = 0; + dma_addr_t va_start = 0x0; + size_t va_size = size; + + venus_data->mapping = arm_iommu_create_mapping( + &platform_bus_type, va_start, va_size, order); + if (IS_ERR_OR_NULL(venus_data->mapping)) { + dprintk(VIDC_ERR, "%s: failed to create mapping for %s\n", + __func__, dev_name(dev)); + return -ENODEV; + } + dprintk(VIDC_DBG, + "%s Attached device %p and created mapping %p for %s\n", + __func__, dev, venus_data->mapping, dev_name(dev)); + return 0; +} +#endif + +static int pil_venus_mem_setup(size_t size) +{ + int rc = 0; + + if (!venus_data->mapping) { + size = round_up(size, SZ_4K); +#if 0 + 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; + } +#endif + venus_data->fw_sz = size; + } + + return rc; +} + +static int pil_venus_auth_and_reset(void) +{ + int rc; + + /* Need to enable this for new SMMU to set the device attribute */ + bool disable_htw = true; + 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 = 1; + } + + 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 temp, pa = fw_bias; + +#if 0 + rc = arm_iommu_attach_device(dev, venus_data->mapping); + if (rc) { + dprintk(VIDC_ERR, + "Failed to attach iommu for %s : %d\n", + dev_name(dev), rc); + goto release_mapping; + } + + /* Enable this for new SMMU to set the device attribute */ + if (iommu_domain_set_attr(venus_data->mapping->domain, + DOMAIN_ATTR_COHERENT_HTW_DISABLE, + &disable_htw)) { + dprintk(VIDC_ERR, + "%s: Failed to disable COHERENT_HTW: %s\n", + __func__, dev_name(dev)); + goto err_iommu_map; + } + + 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->mapping->domain, + venus_data->fw_iova, pa, venus_data->fw_sz, + IOMMU_READ|IOMMU_WRITE|IOMMU_NOEXEC|IOMMU_PRIV); + temp = iommu_iova_to_phys(venus_data->mapping->domain, + venus_data->fw_iova); + + if (temp != pa) { + dprintk(VIDC_ERR, + "%s : iova_to_phys didn't match what we mapped! (mapped: %p, got: %p)\n", + dev_name(dev), &pa, &temp); + } else { + 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_iommu_map; + } +#endif + } + /* Bring Arm9 out of reset */ + writel_relaxed(0, reg_base + VENUS_WRAPPER_SW_RESET); + + venus_data->is_booted = 1; + return 0; + +#if 0 +err_iommu_map: + if (iommu_present) + arm_iommu_detach_device(dev); +release_mapping: + if (iommu_present) + arm_iommu_release_mapping(venus_data->mapping); +#endif +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)) { +#if 0 + iommu_unmap(venus_data->mapping->domain, venus_data->fw_iova, + venus_data->fw_sz); + arm_iommu_detach_device(venus_data->iommu_ctx_bank_dev); +#endif + } + /* + * 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 = 0; + + 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/venus_boot.h b/drivers/media/platform/msm/vidc/venus_boot.h new file mode 100644 index 000000000000..cbcfab7107cf --- /dev/null +++ b/drivers/media/platform/msm/vidc/venus_boot.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2014, 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/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c new file mode 100644 index 000000000000..e997c082af65 --- /dev/null +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -0,0 +1,4331 @@ +/* Copyright (c) 2012-2015, 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 <asm/dma-iommu.h> +#include <asm/memory.h> +//#include <linux/coresight-stm.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iommu.h> +#include <linux/iopoll.h> +#include <linux/of.h> +//#include <linux/qcom_iommu.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/qcom_scm.h> +#include <soc/qcom/smem.h> +#include <soc/qcom/subsystem_restart.h> +#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 + +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 +}; + +struct tzbsp_video_set_state_req { + u32 state; /*shoud be tzbsp_video_state enum value*/ + u32 spare; /*reserved for future, should be zero*/ +}; + +static void venus_hfi_pm_hndlr(struct work_struct *work); +static DECLARE_DELAYED_WORK(venus_hfi_pm_work, venus_hfi_pm_hndlr); +static int venus_hfi_power_enable(void *dev); +static inline int venus_hfi_power_on( + struct venus_hfi_device *device); +static int venus_hfi_disable_regulators(struct venus_hfi_device *device); +static int venus_hfi_enable_regulators(struct venus_hfi_device *device); +static inline int venus_hfi_prepare_enable_clks( + struct venus_hfi_device *device); +static inline void venus_hfi_disable_unprepare_clks( + struct venus_hfi_device *device); +static void venus_hfi_flush_debug_queue( + struct venus_hfi_device *device, u8 *packet); +static int venus_hfi_initialize_packetization(struct venus_hfi_device *device); + +static inline void venus_hfi_set_state(struct venus_hfi_device *device, + enum venus_hfi_state state) +{ + mutex_lock(&device->write_lock); + mutex_lock(&device->read_lock); + device->state = state; + mutex_unlock(&device->write_lock); + mutex_unlock(&device->read_lock); +} + +static inline bool venus_hfi_core_in_valid_state( + struct venus_hfi_device *device) +{ + return device->state != VENUS_STATE_DEINIT; +} + +static void venus_hfi_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 venus_hfi_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; + + /* Ideally we should acquire device->session_lock. If we acquire + * we may go to deadlock with inst->*_lock between two threads. + * Ex : in the forward path we acquire inst->internalbufs.lock and + * session_lock and in the reverse path, we acquire session_lock and + * internalbufs.lock. So this may introduce deadlock. So we are not + * doing that. On virtio it is less likely to run two sessions + * concurrently. So it should be fine */ + + session = hfi_process_get_session( + &device->sess_head, 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 venus_hfi_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); + + } + } + WARN_ON(!regulator_is_enabled(rinfo->regulator)); + return rc; +} + +static int venus_hfi_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 venus_hfi_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 = venus_hfi_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) + venus_hfi_acquire_regulator(rinfo); + + return rc; +} + +static int venus_hfi_acquire_regulators(struct venus_hfi_device *device) +{ + int rc = 0; + struct regulator_info *rinfo; + + dprintk(VIDC_DBG, "Enabling regulators\n"); + + venus_hfi_for_each_regulator(device, rinfo) { + if (rinfo->has_hw_power_collapse) { + /* + * Once driver has the control, it restores the + * previous state of regulator. Hence driver no + * need to call regulator_enable for these. + */ + rc = venus_hfi_acquire_regulator(rinfo); + if (rc) { + dprintk(VIDC_WARN, + "Failed: Aqcuire control: %s\n", + rinfo->name); + break; + } + } + } + return rc; +} + +static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set) +{ + struct hfi_queue_header *queue; + u32 packet_size_in_words, new_write_idx; + struct vidc_iface_q_info *qinfo; + u32 empty_space, read_idx; + u32 *write_ptr; + + if (!info || !packet || !rx_req_is_set) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + qinfo = (struct vidc_iface_q_info *) info; + if (!qinfo || !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: %p\n", __func__, qinfo); + venus_hfi_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; + *rx_req_is_set = (1 == queue->qhdr_rx_req) ? 1 : 0; + /*Memory barrier to make sure write index is updated before an + * interupt is raised on venus.*/ + mb(); + return 0; +} + +static void venus_hfi_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; + if (&device->session_lock) { + mutex_lock(&device->session_lock); + session = hfi_process_get_session( + &device->sess_head, sys_idle->session_id); + mutex_unlock(&device->session_lock); + } + 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 venus_hfi_read_queue(void *info, 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; + struct vidc_iface_q_info *qinfo; + int rc = 0; + + if (!info || !packet || !pb_tx_req_is_set) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + qinfo = (struct vidc_iface_q_info *) info; + if (!qinfo || !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 = (1 == queue->qhdr_tx_req) ? 1 : 0; + + if (msm_vidc_debug & VIDC_PKT) { + dprintk(VIDC_PKT, "%s: %p\n", __func__, qinfo); + venus_hfi_dump_packet(packet); + } + + return rc; +} + +static int venus_hfi_alloc(struct venus_hfi_device *dev, void *mem, + u32 size, u32 align, u32 flags, u32 usage) +{ + struct vidc_mem_addr *vmem = NULL; + struct msm_smem *alloc = NULL; + int rc = 0; + + if (!dev || !dev->hal_client || !mem || !size) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + + vmem = (struct vidc_mem_addr *)mem; + dprintk(VIDC_INFO, "start to alloc size: %d, flags: %d\n", size, flags); + + venus_hfi_power_enable(dev); + + alloc = msm_smem_alloc(dev->hal_client, size, align, flags, usage, 1); + if (!alloc) { + dprintk(VIDC_ERR, "Alloc failed\n"); + rc = -ENOMEM; + goto fail_smem_alloc; + } + dprintk(VIDC_DBG, "venus_hfi_alloc: ptr = %p, size = %d\n", + alloc->kvaddr, size); + rc = msm_smem_cache_operations(dev->hal_client, alloc, + SMEM_CACHE_CLEAN); + if (rc) { + dprintk(VIDC_WARN, "Failed to clean cache\n"); + dprintk(VIDC_WARN, "This may result in undefined behavior\n"); + } + vmem->mem_size = alloc->size; + vmem->mem_data = alloc; + vmem->align_virtual_addr = alloc->kvaddr; + vmem->align_device_addr = alloc->device_addr; + return rc; +fail_smem_alloc: + return rc; +} + +static void venus_hfi_free(struct venus_hfi_device *dev, struct msm_smem *mem) +{ + if (!dev || !mem) { + dprintk(VIDC_ERR, "invalid param %p %p\n", dev, mem); + return; + } + + if (venus_hfi_power_on(dev)) + dprintk(VIDC_ERR, "%s: Power on failed\n", __func__); + + msm_smem_free(dev->hal_client, mem); +} + +static void venus_hfi_write_register( + struct venus_hfi_device *device, u32 reg, u32 value) +{ + u32 hwiosymaddr = reg; + u8 *base_addr; + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return; + } + if (device->clk_state != ENABLED_PREPARED) { + dprintk(VIDC_WARN, + "HFI Write register failed : Clocks are OFF\n"); + return; + } + + base_addr = device->hal_data->register_base; + dprintk(VIDC_DBG, "Base addr: %p, written to: %#x, Value: %#x...\n", + base_addr, hwiosymaddr, value); + base_addr += hwiosymaddr; + writel_relaxed(value, base_addr); + wmb(); +} + +static int venus_hfi_read_register(struct venus_hfi_device *device, u32 reg) +{ + int rc = 0; + u8 *base_addr; + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return -EINVAL; + } + if (device->clk_state != ENABLED_PREPARED) { + dprintk(VIDC_WARN, + "HFI Read register failed : Clocks are OFF\n"); + return -EINVAL; + } + base_addr = device->hal_data->register_base; + + rc = readl_relaxed(base_addr + reg); + rmb(); + dprintk(VIDC_DBG, "Base addr: %p, read from: %#x, value: %#x...\n", + base_addr, reg, rc); + + return rc; +} + +static void venus_hfi_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++) { + venus_hfi_write_register(device, + reg_set->reg_tbl[i].reg, + reg_set->reg_tbl[i].value); + } +} + +static int venus_hfi_core_start_cpu(struct venus_hfi_device *device) +{ + u32 ctrl_status = 0, count = 0, rc = 0; + int max_tries = 100; + venus_hfi_write_register(device, + VIDC_WRAPPER_INTR_MASK, + VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK); + + while (!ctrl_status && count < max_tries) { + ctrl_status = venus_hfi_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) + rc = -ETIME; + return rc; +} + +static void venus_hfi_iommu_detach(void *dev) +{ + struct context_bank_info *cb; + struct venus_hfi_device *device = dev; + + if (!device || !device->res) { + dprintk(VIDC_ERR, "Invalid paramter: %p\n", device); + return; + } + + list_for_each_entry(cb, &device->res->context_banks, list) { + if (cb->dev) + arm_iommu_detach_device(cb->dev); + if (cb->mapping) + arm_iommu_release_mapping(cb->mapping); + } +} + +#define BUS_LOAD(__w, __h, __fps) (\ + /* Something's fishy if the width & height aren't macroblock aligned */\ + BUILD_BUG_ON_ZERO(!IS_ALIGNED(__h, 16) || !IS_ALIGNED(__w, 16)) ?: \ + (__h >> 4) * (__w >> 4) * __fps) + +static const u32 venus_hfi_bus_table[] = { + BUS_LOAD(0, 0, 0), + BUS_LOAD(640, 480, 30), + BUS_LOAD(640, 480, 60), + BUS_LOAD(1280, 736, 30), + BUS_LOAD(1280, 736, 60), + BUS_LOAD(1920, 1088, 30), + BUS_LOAD(1920, 1088, 60), + BUS_LOAD(3840, 2176, 24), + BUS_LOAD(4096, 2176, 24), + BUS_LOAD(3840, 2176, 30), +}; + +static int venus_hfi_get_bus_vector(struct venus_hfi_device *device, + struct bus_info *bus, int load) +{ + int num_rows = ARRAY_SIZE(venus_hfi_bus_table); + int i, j; + + for (i = 0; i < num_rows; i++) { + if (load <= venus_hfi_bus_table[i]) + break; + } + + j = clamp(i, 0, num_rows - 1); + + /* Ensure bus index remains within the supported range, + * as specified in the device dtsi file */ + j = clamp(j, 0, bus->pdata->num_usecases - 1); + + dprintk(VIDC_DBG, "Required bus = %d\n", j); + return j; +} + +static bool venus_hfi_is_session_supported(unsigned long sessions_supported, + enum vidc_bus_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; +} + +static int venus_hfi_vote_bus(struct bus_info *bus, unsigned int bus_vector) +{ + int rc = msm_bus_scale_client_update_request(bus->priv, bus_vector); + if (!rc) { + dprintk(VIDC_PROF, "%s bus %s (%s) to vector %d\n", + bus_vector ? "Voting" : "Unvoting", + bus->pdata->name, + bus->passive ? "passive" : "active", + bus_vector); + } + + return rc; +} + +static int venus_hfi_vote_passive_buses(void *dev, + struct vidc_bus_vote_data *data, int num_data) +{ + struct venus_hfi_device *device = dev; + struct bus_info *bus = NULL; + int rc = 0; + + /* + * Neither of these parameters are used (or will be useful in future). + * Just keeping these so that the API is consistent with _vote_active\ + * _buses(). + */ + (void)data; + (void)num_data; + + venus_hfi_for_each_bus(device, bus) { + /* Reject active buses, as those are driven by instance load */ + if (!bus->passive) + continue; + + /* + * XXX: Should probably check *_is_session_supported() prior + * to voting but probably overkill at this point. So skip the + * check for now. + */ + rc = venus_hfi_vote_bus(bus, 1); + if (rc) { + dprintk(VIDC_ERR, + "Failed voting for passive bus %s: %d\n", + bus->pdata->name, rc); + goto vote_fail; + } + } + +vote_fail: + return rc; +} + +static int venus_hfi_vote_active_buses(void *dev, + struct vidc_bus_vote_data *data, int num_data) +{ + struct { + struct bus_info *bus; + int load; + } *aggregate_load_table; + int rc = 0, i = 0, num_bus = 0; + struct venus_hfi_device *device = dev; + struct bus_info *bus = NULL; + struct vidc_bus_vote_data *cached_vote_data = NULL; + + if (!dev) { + dprintk(VIDC_ERR, "Invalid device\n"); + return -EINVAL; + } else if (!num_data) { + /* Meh nothing to do */ + return 0; + } else if (!data) { + dprintk(VIDC_ERR, "Invalid voting data\n"); + return -EINVAL; + } + + /* (Re-)alloc memory to store the new votes (in case we internally + * re-vote after power collapse, which is transparent to client) */ + cached_vote_data = krealloc(device->bus_load.vote_data, num_data * + sizeof(*cached_vote_data), GFP_KERNEL); + if (!cached_vote_data) { + dprintk(VIDC_ERR, "Can't alloc memory to cache bus votes\n"); + rc = -ENOMEM; + goto err_no_mem; + } + + /* Alloc & init the load table */ + num_bus = device->res->bus_set.count; + aggregate_load_table = kzalloc(sizeof(*aggregate_load_table) * num_bus, + GFP_TEMPORARY); + if (!aggregate_load_table) { + dprintk(VIDC_ERR, "The world is ending (no more memory)\n"); + rc = -ENOMEM; + goto err_no_mem; + } + + i = 0; + venus_hfi_for_each_bus(device, bus) + aggregate_load_table[i++].bus = bus; + + /* Aggregate the loads for each bus */ + for (i = 0; i < num_data; ++i) { + int j = 0; + + for (j = 0; j < num_bus; ++j) { + bool matches = venus_hfi_is_session_supported( + aggregate_load_table[j].bus-> + sessions_supported, + data[i].session); + + if (matches) { + aggregate_load_table[j].load += + data[i].load; + } + } + } + + /* Now vote for each bus */ + for (i = 0; i < num_bus; ++i) { + int bus_vector = 0; + struct bus_info *bus = aggregate_load_table[i].bus; + int load = aggregate_load_table[i].load; + + /* Passive buses aren't meant to be scaled by load */ + if (bus->passive) + continue; + + /* Let's avoid voting for imem if allocation failed. + * There's no clean way presently to check which buses are + * associated with imem. So do a crude check for the bus name, + * which relies on the buses being named appropriately. */ + if (!device->resources.imem.type && strnstr(bus->pdata->name, + "ocmem", strlen(bus->pdata->name))) { + dprintk(VIDC_DBG, "Skipping voting for %s (no imem)\n", + bus->pdata->name); + continue; + } + + bus_vector = venus_hfi_get_bus_vector(device, bus, load); + rc = venus_hfi_vote_bus(bus, bus_vector); + if (rc) { + dprintk(VIDC_ERR, "Failed voting for bus %s @ %d: %d\n", + bus->pdata->name, bus_vector, rc); + /* Ignore error and try to vote for the rest */ + rc = 0; + } + } + + /* Cache the votes */ + for (i = 0; i < num_data; ++i) + cached_vote_data[i] = data[i]; + + device->bus_load.vote_data = cached_vote_data; + device->bus_load.vote_data_count = num_data; + + kfree(aggregate_load_table); +err_no_mem: + return rc; + +} + +static int venus_hfi_unvote_buses_of_type(struct venus_hfi_device *device, + bool only_passive) +{ + struct bus_info *bus = NULL; + int rc = 0; + + venus_hfi_for_each_bus(device, bus) { + int local_rc = 0; + + if (bus->passive != only_passive) + continue; + + local_rc = venus_hfi_vote_bus(bus, 0); + if (local_rc) { + rc = rc ?: local_rc; + dprintk(VIDC_ERR, + "Failed unvoting passive bus %s: %d\n", + bus->pdata->name, rc); + } + } + + return rc; +} + +static int venus_hfi_unvote_passive_buses(void *dev) +{ + return venus_hfi_unvote_buses_of_type(dev, true); +} + +static int venus_hfi_unvote_active_buses(void *dev) +{ + return venus_hfi_unvote_buses_of_type(dev, false); +} + +static int venus_hfi_unvote_buses(void *dev) +{ + venus_hfi_unvote_active_buses(dev); + venus_hfi_unvote_passive_buses(dev); + + return 0; +} + +static int venus_hfi_vote_buses(void *dev, + struct vidc_bus_vote_data *data, int num_data) +{ + int rc = venus_hfi_vote_passive_buses(dev, data, num_data); + rc = rc ?: venus_hfi_vote_active_buses(dev, data, num_data); + + if (rc) + goto fail_vote; + + return 0; +fail_vote: + venus_hfi_unvote_buses(dev); + return rc; +} + +static int venus_hfi_iface_cmdq_write_nolock(struct venus_hfi_device *device, + void *pkt); + +static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device, + void *pkt) +{ + int result = -EPERM; + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params"); + return -EINVAL; + } + mutex_lock(&device->write_lock); + result = venus_hfi_iface_cmdq_write_nolock(device, pkt); + mutex_unlock(&device->write_lock); + return result; +} + +static int venus_hfi_core_set_resource(void *device, + struct vidc_resource_hdr *resource_hdr, void *resource_value, + bool locked) +{ + struct hfi_cmd_sys_set_resource_packet *pkt; + u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; + int rc = 0; + struct venus_hfi_device *dev; + + if (!device || !resource_hdr || !resource_value) { + dprintk(VIDC_ERR, "set_res: Invalid Params\n"); + return -EINVAL; + } + + dev = device; + pkt = (struct hfi_cmd_sys_set_resource_packet *) packet; + + rc = call_hfi_pkt_op(dev, 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 = locked ? venus_hfi_iface_cmdq_write(dev, pkt) : + venus_hfi_iface_cmdq_write_nolock(dev, pkt); + if (rc) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static int venus_hfi_core_release_resource(void *device, + struct vidc_resource_hdr *resource_hdr) +{ + struct hfi_cmd_sys_release_resource_packet pkt; + int rc = 0; + struct venus_hfi_device *dev; + + if (!device || !resource_hdr) { + dprintk(VIDC_ERR, "Inv-Params in rel_res\n"); + return -EINVAL; + } else { + dev = device; + } + + rc = call_hfi_pkt_op(dev, sys_release_resource, + &pkt, resource_hdr); + if (rc) { + dprintk(VIDC_ERR, "release_res: failed to create packet\n"); + goto err_create_pkt; + } + + if (venus_hfi_iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static DECLARE_COMPLETION(pc_prep_done); +static DECLARE_COMPLETION(release_resources_done); + +static int venus_hfi_alloc_imem(void *dev, unsigned long size) +{ + struct imem *imem = NULL; + struct venus_hfi_device *device = dev; + int rc = 0; + + if (!device || !size) + 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_OCMEM: + { + struct ocmem_buf *ocmem_buffer = + ocmem_allocate(OCMEM_VIDEO, size); + if (IS_ERR_OR_NULL(ocmem_buffer)) { + rc = PTR_ERR(ocmem_buffer) ?: -ENOMEM; + goto imem_alloc_failed; + } + + imem->ocmem.buf = ocmem_buffer; + break; + } + case IMEM_VMEM: + { + phys_addr_t vmem_buffer = 0; + + rc = vmem_allocate(size, &vmem_buffer); + if (rc) { + goto imem_alloc_failed; + } else if (!vmem_buffer) { + rc = -ENOMEM; + goto imem_alloc_failed; + } + + imem->vmem = vmem_buffer; + 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 venus_hfi_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_OCMEM: + rc = ocmem_free(OCMEM_VIDEO, imem->ocmem.buf); + if (rc) { + dprintk(VIDC_ERR, "Failed to free ocmem\n"); + goto imem_free_failed; + } + + 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 venus_hfi_set_imem(struct venus_hfi_device *device, + struct imem *imem, bool locked) +{ + struct vidc_resource_hdr rhdr; + phys_addr_t addr = 0; + int rc = 0; + + if (!device || !device->res || !imem) { + dprintk(VIDC_ERR, "Invalid params, core: %p, imem: %p\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_OCMEM: + rhdr.resource_id = VIDC_RESOURCE_OCMEM; + addr = imem->ocmem.buf->addr; + /* Just for sanity */ + if (imem->ocmem.buf->len != rhdr.size) { + dprintk(VIDC_ERR, + "ocmem buffer size unexpectedly small (expected %d, have %lu)\n", + rhdr.size, imem->ocmem.buf->len); + rc = -EINVAL; + goto imem_set_failed; + } + + break; + case IMEM_VMEM: + rhdr.resource_id = VIDC_RESOURCE_VMEM; + addr = imem->vmem; + break; + default: + dprintk(VIDC_ERR, "IMEM of type %d unsupported\n", imem->type); + rc = -ENOTSUPP; + goto imem_set_failed; + } + + BUG_ON(!addr); + + rc = venus_hfi_core_set_resource(device, &rhdr, (void *)addr, locked); + 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 = venus_hfi_vote_buses(device, device->bus_load.vote_data, + device->bus_load.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 venus_hfi_unset_imem(struct venus_hfi_device *device) +{ + struct vidc_resource_hdr rhdr; + struct imem *imem = NULL; + int rc = 0; + phys_addr_t addr = 0; + + if (!device) { + dprintk(VIDC_ERR, "%s Invalid params, device: %p\n", + __func__, device); + rc = -EINVAL; + goto imem_unset_failed; + } + + mutex_lock(&device->write_lock); + mutex_lock(&device->read_lock); + rc = venus_hfi_core_in_valid_state(device); + mutex_unlock(&device->read_lock); + mutex_unlock(&device->write_lock); + if (!rc) { + dprintk(VIDC_WARN, "Core is in bad state, won't unset imem\n"); + rc = -EIO; + goto imem_unset_failed; + } + + imem = &device->resources.imem; + switch (imem->type) { + case IMEM_OCMEM: + rhdr.resource_id = VIDC_RESOURCE_OCMEM; + addr = imem->ocmem.buf->addr; + break; + case IMEM_VMEM: + rhdr.resource_id = VIDC_RESOURCE_VMEM; + addr = imem->vmem; + break; + default: + dprintk(VIDC_ERR, "IMEM of type %d unsupported\n", imem->type); + rc = -ENOTSUPP; + goto imem_unset_failed; + } + + if (!addr) { + dprintk(VIDC_INFO, "Trying to unset IMEM which is not set\n"); + rc = -EINVAL; + goto imem_unset_failed; + } + + rhdr.resource_handle = imem; /* cookie */ + rhdr.size = device->res->imem_size; + + init_completion(&release_resources_done); + + rc = venus_hfi_core_release_resource(device, &rhdr); + if (rc) { + dprintk(VIDC_ERR, "Failed to unset imem on driver\n"); + goto imem_unset_failed; + } + + if (!wait_for_completion_timeout(&release_resources_done, + msecs_to_jiffies(msm_vidc_hw_rsp_timeout))) { + dprintk(VIDC_ERR, + "Wait timedout in releasing IMEM\n"); + rc = -EIO; + goto imem_unset_failed; + } + +imem_unset_failed: + return rc; +} + +static int venus_hfi_alloc_set_imem(struct venus_hfi_device *device, + bool locked) +{ + int rc = 0; + + rc = venus_hfi_alloc_imem(device, device->res->imem_size); + if (rc) { + dprintk(VIDC_ERR, "Failed to allocate imem: %d\n", rc); + goto alloc_failed; + } + + rc = venus_hfi_set_imem(device, &device->resources.imem, locked); + if (rc) { + dprintk(VIDC_ERR, "Failed to set imem to core: %d\n", rc); + goto set_failed; + } + + return 0; +set_failed: + venus_hfi_free_imem(device); +alloc_failed: + return rc; +} + +static int venus_hfi_unset_free_imem(struct venus_hfi_device *device) +{ + int rc = 0; + + rc = venus_hfi_unset_imem(device); + if (rc) { + dprintk(VIDC_WARN, "Failed to unset imem: %d\n", rc); + goto unset_failed; + } + + rc = venus_hfi_free_imem(device); + if (rc) { + dprintk(VIDC_WARN, "Failed to free imem: %d\n", rc); + goto free_failed; + } + +unset_failed: +free_failed: + return rc; +} + +static inline int venus_hfi_tzbsp_set_video_state(enum tzbsp_video_state state) +{ + int ret; + + ret = qcom_scm_set_video_state(state, 0); + if (ret) { + dprintk(VIDC_ERR, "Failed scm_call %d\n", ret); + return ret; + } + + dprintk(VIDC_DBG, "Set state %d\n", state); + + return 0; +} + +static inline int venus_hfi_reset_core(struct venus_hfi_device *device) +{ + int rc = 0; + venus_hfi_write_register(device, VIDC_CTRL_INIT, 0x1); + rc = venus_hfi_core_start_cpu(device); + if (rc) + dprintk(VIDC_ERR, "Failed to start core\n"); + return rc; +} + +static struct clock_info *venus_hfi_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 venus_hfi_get_clock_rate(struct clock_info *clock, + int num_mbs_per_sec, int codecs_enabled) +{ + int num_rows = clock->count; + struct load_freq_table *table = clock->load_freq_tbl; + unsigned long freq = table[0].freq; + int i; + + if (!num_mbs_per_sec && num_rows > 1) + return table[num_rows - 1].freq; + + for (i = 0; i < num_rows; i++) { + bool matches = venus_hfi_is_session_supported( + table[i].supported_codecs, codecs_enabled); + if (!matches) + continue; + + if (num_mbs_per_sec > table[i].load) + break; + + freq = table[i].freq; + } + + return freq; +} + +static unsigned long venus_hfi_get_core_clock_rate(void *dev) +{ + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + struct clock_info *vc; + + if (!device) { + dprintk(VIDC_ERR, "%s Invalid args: %p\n", __func__, device); + return -EINVAL; + } + + vc = venus_hfi_get_clock(device, "core_clk"); + if (vc) + return clk_get_rate(vc->clk); + else + return 0; +} + +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; + } + dprintk(VIDC_INFO, "%s\n", __func__); + + if (device->power_enabled) { + rc = flush_delayed_work(&venus_hfi_pm_work); + dprintk(VIDC_INFO, "%s flush delayed work %d\n", __func__, rc); + } + return 0; +} + +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; + } + + if (device->packetization_type == HFI_PACKETIZATION_3XX) + prop = HAL_VIDEO_DYNAMIC_BUF_MODE; + + return prop; +} + +static int venus_hfi_halt_axi(struct venus_hfi_device *device) +{ + u32 reg; + int rc = 0; + if (!device) { + dprintk(VIDC_ERR, "Invalid input: %p\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->clk_state != ENABLED_PREPARED) { + dprintk(VIDC_WARN, + "Clocks are OFF, skipping AXI HALT\n"); + return -EINVAL; + } + + /* Halt AXI and AXI IMEM VBIF Access */ + reg = venus_hfi_read_register(device, VENUS_VBIF_AXI_HALT_CTRL0); + reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ; + venus_hfi_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 inline int venus_hfi_power_off(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return -EINVAL; + } + if (!device->power_enabled) { + dprintk(VIDC_DBG, "Power already disabled\n"); + return 0; + } + + rc = venus_hfi_halt_axi(device); + if (rc) { + dprintk(VIDC_WARN, "Failed to halt AXI\n"); + return 0; + } + + dprintk(VIDC_DBG, "Entering power collapse\n"); + rc = venus_hfi_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; + } + /* + * For some regulators, driver might have transfered the control to HW. + * So before touching any clocks, driver should get the regulator + * control back. Acquire regulators also makes sure that the regulators + * are turned ON. So driver can touch the clocks safely. + */ + + rc = venus_hfi_acquire_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable gdsc in %s Err code = %d\n", + __func__, rc); + goto err_acquire_regulators; + } + venus_hfi_disable_unprepare_clks(device); + rc = venus_hfi_disable_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to disable gdsc\n"); + goto err_disable_regulators; + } + + venus_hfi_unvote_buses(device); + device->power_enabled = false; + dprintk(VIDC_INFO, "Venus power collapsed\n"); + + return rc; + +err_disable_regulators: + if (venus_hfi_prepare_enable_clks(device)) + dprintk(VIDC_ERR, "Failed prepare_enable_clks\n"); + if (venus_hfi_hand_off_regulators(device)) + dprintk(VIDC_ERR, "Failed hand_off_regulators\n"); +err_acquire_regulators: + if (venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME)) + dprintk(VIDC_ERR, "Failed TZBSP_RESUME\n"); +err_tzbsp_suspend: + return rc; +} + +static inline int venus_hfi_power_on(struct venus_hfi_device *device) +{ + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return -EINVAL; + } + if (device->power_enabled) + return 0; + + dprintk(VIDC_DBG, "Resuming from power collapse\n"); + rc = venus_hfi_vote_buses(device, device->bus_load.vote_data, + device->bus_load.vote_data_count); + if (rc) { + dprintk(VIDC_ERR, "Failed to scale buses\n"); + goto err_vote_buses; + } + + /* At this point driver has the control for all regulators */ + rc = venus_hfi_enable_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable GDSC in %s Err code = %d\n", + __func__, rc); + goto err_enable_gdsc; + } + + rc = venus_hfi_prepare_enable_clks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks\n"); + goto err_enable_clk; + } + + /* Reboot the firmware */ + rc = venus_hfi_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; + } + + rc = venus_hfi_hand_off_regulators(device); + if (rc) + dprintk(VIDC_WARN, "Failed to handoff control to HW %d\n", rc); + + /* + * Re-program all of the registers that get reset as a result of + * regulator_disable() and _enable() + */ + venus_hfi_set_registers(device); + + venus_hfi_write_register(device, VIDC_UC_REGION_ADDR, + (u32)device->iface_q_table.align_device_addr); + venus_hfi_write_register(device, VIDC_UC_REGION_SIZE, SHARED_QSIZE); + venus_hfi_write_register(device, VIDC_CPU_CS_SCIACMDARG2, + (u32)device->iface_q_table.align_device_addr); + + if (device->sfr.align_device_addr) + venus_hfi_write_register(device, VIDC_SFR_ADDR, + (u32)device->sfr.align_device_addr); + if (device->qdss.align_device_addr) + venus_hfi_write_register(device, VIDC_MMAP_ADDR, + (u32)device->qdss.align_device_addr); + + /* Wait for boot completion */ + rc = venus_hfi_reset_core(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to reset venus core\n"); + goto err_reset_core; + } + + /* + * Set the flag here to skip venus_hfi_power_on() which is + * being called again via *_alloc_set_imem() if imem is enabled + */ + device->power_enabled = true; + + /* + * write_lock is already acquired at this point, so to avoid + * recursive lock in cmdq_write function, call nolock version + * of alloc_icmem + */ + WARN_ON(!mutex_is_locked(&device->write_lock)); + rc = venus_hfi_alloc_set_imem(device, false); + if (rc) { + dprintk(VIDC_ERR, "Failed to allocate IMEM"); + goto err_alloc_imem; + } + + dprintk(VIDC_INFO, "Resumed from power collapse\n"); + return rc; + +err_alloc_imem: +err_reset_core: + venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND); +err_set_video_state: + venus_hfi_disable_unprepare_clks(device); +err_enable_clk: + venus_hfi_disable_regulators(device); +err_enable_gdsc: + venus_hfi_unvote_buses(device); +err_vote_buses: + device->power_enabled = false; + dprintk(VIDC_ERR, "Failed to resume from power collapse\n"); + return rc; +} + +static int venus_hfi_power_enable(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return -EINVAL; + } + mutex_lock(&device->write_lock); + rc = venus_hfi_power_on(device); + if (rc) + dprintk(VIDC_ERR, "%s: Failed to enable power\n", __func__); + mutex_unlock(&device->write_lock); + + return rc; +} + +static int venus_hfi_scale_clocks(void *dev, int load, int codecs_enabled) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + struct clock_info *cl; + + if (!device) { + dprintk(VIDC_ERR, "Invalid args: %p\n", device); + return -EINVAL; + } + device->clk_load = load; + device->codecs_enabled = codecs_enabled; + + venus_hfi_for_each_clock(device, cl) { + if (cl->count) {/* has_scaling */ + unsigned long rate = venus_hfi_get_clock_rate(cl, load, + codecs_enabled); + 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; + } + + dprintk(VIDC_PROF, "Scaling clock %s to %lu\n", + cl->name, rate); + } + } + + return rc; +} + +static int venus_hfi_iface_cmdq_write_nolock(struct venus_hfi_device *device, + void *pkt) +{ + u32 rx_req_is_set = 0; + struct vidc_iface_q_info *q_info; + struct vidc_hal_cmd_pkt_hdr *cmd_packet; + int result = -EPERM; + + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); + return -EINVAL; + } + WARN(!mutex_is_locked(&device->write_lock), + "Cmd queue write lock must be acquired"); + if (!venus_hfi_core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%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; + } + + venus_hfi_sim_modify_cmd_packet((u8 *)pkt, device); + if (!venus_hfi_write_queue(q_info, (u8 *)pkt, &rx_req_is_set)) { + + if (venus_hfi_power_on(device)) { + dprintk(VIDC_ERR, "%s: Power on failed\n", __func__); + goto err_q_write; + } + if (venus_hfi_scale_clocks(device, device->clk_load, + device->codecs_enabled)) { + dprintk(VIDC_ERR, "Clock scaling failed\n"); + goto err_q_write; + } + if (rx_req_is_set) + venus_hfi_write_register( + device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + + if (device->res->sw_power_collapsible) { + dprintk(VIDC_DBG, + "Cancel and queue delayed work again\n"); + 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, "venus_hfi_iface_cmdq_write:queue_full\n"); + } +err_q_write: +err_q_null: + return result; +} + +static int venus_hfi_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; + } + mutex_lock(&device->read_lock); + if (!venus_hfi_core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__); + rc = -EINVAL; + goto read_error_null; + } + + if (device->iface_queues[VIDC_IFACEQ_MSGQ_IDX]. + q_array.align_virtual_addr == 0) { + dprintk(VIDC_ERR, "cannot read from shared MSG Q's\n"); + rc = -ENODATA; + goto read_error_null; + } + + q_info = &device->iface_queues[VIDC_IFACEQ_MSGQ_IDX]; + if (!venus_hfi_read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) { + venus_hfi_hal_sim_modify_msg_packet((u8 *)pkt, device); + if (tx_req_is_set) + venus_hfi_write_register( + device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + rc = 0; + } else + rc = -ENODATA; + +read_error_null: + mutex_unlock(&device->read_lock); + return rc; +} + +static int venus_hfi_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; + } + mutex_lock(&device->read_lock); + if (!venus_hfi_core_in_valid_state(device)) { + dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__); + rc = -EINVAL; + goto dbg_error_null; + } + if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX]. + q_array.align_virtual_addr == 0) { + dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n"); + rc = -ENODATA; + goto dbg_error_null; + } + q_info = &device->iface_queues[VIDC_IFACEQ_DBGQ_IDX]; + if (!venus_hfi_read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) { + if (tx_req_is_set) + venus_hfi_write_register( + device, VIDC_CPU_IC_SOFTINT, + 1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT); + rc = 0; + } else + rc = -ENODATA; + +dbg_error_null: + mutex_unlock(&device->read_lock); + return rc; +} + +static void venus_hfi_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 venus_hfi_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; + + mutex_lock(&device->write_lock); + mutex_lock(&device->read_lock); + if (device->qdss.mem_data) { + 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(device->hal_client, + false, HAL_BUFFER_INTERNAL_CMD_QUEUE); + + for (i = 0; cb && i < num_entries; i++) { + iommu_unmap(cb->mapping->domain, + mem_map[i].virtual_addr, + mem_map[i].size); + } + + venus_hfi_free(device, device->qdss.mem_data); + } + venus_hfi_free(device, device->iface_q_table.mem_data); + venus_hfi_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.mem_data = 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.mem_data = NULL; + device->iface_q_table.align_virtual_addr = NULL; + device->iface_q_table.align_device_addr = 0; + + device->qdss.mem_data = NULL; + device->qdss.align_virtual_addr = NULL; + device->qdss.align_device_addr = 0; + + device->sfr.mem_data = NULL; + device->sfr.align_virtual_addr = NULL; + device->sfr.align_device_addr = 0; + + device->mem_addr.mem_data = NULL; + device->mem_addr.align_virtual_addr = NULL; + device->mem_addr.align_device_addr = 0; + + msm_smem_delete_client(device->hal_client); + device->hal_client = NULL; + mutex_unlock(&device->read_lock); + mutex_unlock(&device->write_lock); +} + +static int venus_hfi_get_qdss_iommu_virtual_addr(struct venus_hfi_device *dev, + struct hfi_mem_map *mem_map, struct dma_iommu_mapping *mapping) +{ + 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; + phys_addr_t phys; + + if (!num_entries) + return -ENODATA; + + for (i = 0; i < num_entries; i++) { + if (mapping) { + rc = iommu_map(mapping->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; + } + phys = iommu_iova_to_phys(mapping->domain, iova); + dprintk(VIDC_ERR, + "%s: iova_to_phys : mapped: %#x, got: %pa\n", + __func__, qdss_addr_tbl[i].start, + &phys); + } 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; mapping && i >= 0; i--) { + iommu_unmap(mapping->domain, + mem_map[i].virtual_addr, + mem_map[i].size); + } + } + return rc; +} + +static int venus_hfi_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 = venus_hfi_alloc(dev, (void *) mem_addr, q_size, 1, 0, + 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; + iface_q->q_array.mem_data = NULL; + 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); + venus_hfi_set_queue_hdr_defaults(iface_q->q_hdr); + } + if ((msm_vidc_fw_debug_mode & HFI_DEBUG_MODE_QDSS) && num_entries) { + rc = venus_hfi_alloc(dev, (void *) mem_addr, + ALIGNED_QDSS_SIZE, 1, 0, + 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 = venus_hfi_alloc(dev, (void *) mem_addr, + ALIGNED_SFR_SIZE, 1, 0, + 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 ((ion_phys_addr_t)q_hdr->qhdr_start_addr != + iface_q->q_array.align_device_addr) { + dprintk(VIDC_ERR, "Invalid CMDQ device address (%pa)\n", + &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 ((ion_phys_addr_t)q_hdr->qhdr_start_addr != + iface_q->q_array.align_device_addr) { + dprintk(VIDC_ERR, "Invalid MSGQ device address (%pa)\n", + &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 ((ion_phys_addr_t)q_hdr->qhdr_start_addr != + iface_q->q_array.align_device_addr) { + dprintk(VIDC_ERR, "Invalid DBGQ device address (%pa)\n", + &iface_q->q_array.align_device_addr); + } + + value = (u32)dev->iface_q_table.align_device_addr; + if ((ion_phys_addr_t)value != + dev->iface_q_table.align_device_addr) { + dprintk(VIDC_ERR, + "Invalid iface_q_table device address (%pa)\n", + &dev->iface_q_table.align_device_addr); + } + venus_hfi_write_register(dev, VIDC_UC_REGION_ADDR, value); + venus_hfi_write_register(dev, VIDC_UC_REGION_SIZE, SHARED_QSIZE); + venus_hfi_write_register(dev, VIDC_CPU_CS_SCIACMDARG2, value); + venus_hfi_write_register(dev, VIDC_CPU_CS_SCIACMDARG1, 0x01); + if (dev->qdss.mem_data) { + 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 ((ion_phys_addr_t)qdss->mem_map_table_base_addr != + mem_map_table_base_addr) { + dprintk(VIDC_ERR, + "Invalid mem_map_table_base_addr (%#lx)\n", + mem_map_table_base_addr); + } + mem_map = (struct hfi_mem_map *)(qdss + 1); + cb = msm_smem_get_context_bank(dev->hal_client, false, + HAL_BUFFER_INTERNAL_CMD_QUEUE); + + if (!cb) { + dprintk(VIDC_ERR, + "%s: failed to get context bank\n", __func__); + return -EINVAL; + } + + rc = venus_hfi_get_qdss_iommu_virtual_addr(dev, + mem_map, cb->mapping); + if (rc) { + dprintk(VIDC_ERR, + "IOMMU mapping failed, Freeing qdss memdata\n"); + venus_hfi_free(dev, dev->qdss.mem_data); + dev->qdss.mem_data = NULL; + dev->qdss.align_virtual_addr = NULL; + dev->qdss.align_device_addr = 0; + } + value = (u32)dev->qdss.align_device_addr; + if ((ion_phys_addr_t)value != + dev->qdss.align_device_addr) { + dprintk(VIDC_ERR, "Invalid qdss device address (%pa)\n", + &dev->qdss.align_device_addr); + } + if (dev->qdss.align_device_addr) + venus_hfi_write_register(dev, VIDC_MMAP_ADDR, value); + } + + vsfr = (struct hfi_sfr_struct *) dev->sfr.align_virtual_addr; + vsfr->bufSize = ALIGNED_SFR_SIZE; + value = (u32)dev->sfr.align_device_addr; + if ((ion_phys_addr_t)value != + dev->sfr.align_device_addr) { + dprintk(VIDC_ERR, "Invalid sfr device address (%pa)\n", + &dev->sfr.align_device_addr); + } + if (dev->sfr.align_device_addr) + venus_hfi_write_register(dev, VIDC_SFR_ADDR, value); + return 0; +fail_alloc_queue: + return -ENOMEM; +} + +static int venus_hfi_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 (venus_hfi_iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int venus_hfi_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 (venus_hfi_iface_cmdq_write(device, pkt)) { + dprintk(VIDC_WARN, "Failed to send coverage pkt to f/w\n"); + return -ENOTEMPTY; + } + return 0; +} + +static int venus_hfi_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 (venus_hfi_iface_cmdq_write(device, pkt)) + return -ENOTEMPTY; + return 0; +} + +static int venus_hfi_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 (venus_hfi_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) { + dev = device; + } else { + dprintk(VIDC_ERR, "Invalid device\n"); + return -ENODEV; + } + + venus_hfi_set_state(dev, VENUS_STATE_INIT); + + dev->intr_status = 0; + + mutex_lock(&dev->session_lock); + 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); + mutex_unlock(&dev->session_lock); + + venus_hfi_set_registers(dev); + + if (!dev->hal_client) { + dev->hal_client = msm_smem_new_client(SMEM_DMA, dev->res); + if (dev->hal_client == NULL) { + dprintk(VIDC_ERR, "Failed to alloc ION_Client\n"); + rc = -ENODEV; + goto err_core_init; + } + + dprintk(VIDC_DBG, "Dev_Virt: %pa, Reg_Virt: %p\n", + &dev->hal_data->firmware_base, + dev->hal_data->register_base); + + rc = venus_hfi_interface_queues_init(dev); + if (rc) { + dprintk(VIDC_ERR, "failed to init queues\n"); + rc = -ENOMEM; + goto err_core_init; + } + } else { + dprintk(VIDC_ERR, "hal_client exists\n"); + rc = -EEXIST; + goto err_core_init; + } + enable_irq(dev->hal_data->irq); + venus_hfi_write_register(dev, VIDC_CTRL_INIT, 0x1); + rc = venus_hfi_core_start_cpu(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 (venus_hfi_iface_cmdq_write(dev, &pkt)) { + rc = -ENOTEMPTY; + goto err_core_init; + } + + rc = call_hfi_pkt_op(dev, sys_image_version, &version_pkt); + if (rc || venus_hfi_iface_cmdq_write(dev, &version_pkt)) + dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n"); + + return rc; +err_core_init: + venus_hfi_set_state(dev, VENUS_STATE_DEINIT); + disable_irq_nosync(dev->hal_data->irq); + return rc; +} + +static int venus_hfi_core_release(void *device) +{ + struct venus_hfi_device *dev; + int rc = 0; + + if (device) { + dev = device; + } else { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + if (dev->hal_client) { + if (venus_hfi_power_enable(device)) { + dprintk(VIDC_ERR, + "%s: Power enable failed\n", __func__); + return -EIO; + } + + mutex_lock(&dev->resource_lock); + rc = venus_hfi_unset_free_imem(dev); + mutex_unlock(&dev->resource_lock); + if (rc) + dprintk(VIDC_ERR, + "Failed to unset and free imem in core release: %d\n", + rc); + if (!(dev->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) + disable_irq_nosync(dev->hal_data->irq); + dev->intr_status = 0; + } + venus_hfi_set_state(dev, VENUS_STATE_DEINIT); + + dprintk(VIDC_INFO, "HAL exited\n"); + return 0; +} + +static int venus_hfi_get_q_size(struct venus_hfi_device *dev, + unsigned int q_index) +{ + struct hfi_queue_header *queue; + struct vidc_iface_q_info *q_info; + u32 write_ptr, read_ptr; + u32 rc = 0; + + if (q_index >= VIDC_IFACEQ_NUMQ) { + dprintk(VIDC_ERR, "Invalid q index: %d\n", q_index); + return -ENOENT; + } + + q_info = &dev->iface_queues[q_index]; + if (!q_info) { + dprintk(VIDC_ERR, "cannot read shared Q's\n"); + return -ENOENT; + } + + queue = (struct hfi_queue_header *)q_info->q_hdr; + if (!queue) { + dprintk(VIDC_ERR, "queue not present\n"); + return -ENOENT; + } + + write_ptr = (u32)queue->qhdr_write_idx; + read_ptr = (u32)queue->qhdr_read_idx; + rc = read_ptr - write_ptr; + return rc; +} + +static void venus_hfi_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 = venus_hfi_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: %p: times: %d interrupt_status: %d\n", + device, device->reg_count, intr_status); + } else { + device->spur_count++; + dprintk(VIDC_INFO, + "SPURIOUS_INTR for device: %p: times: %d interrupt_status: %d\n", + device, device->spur_count, intr_status); + } + + venus_hfi_write_register(device, VIDC_CPU_CS_A2HSOFTINTCLR, 1); + venus_hfi_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) { + dev = device; + } else { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + 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 (venus_hfi_iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + 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) { + dev = device; + } else { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + 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 (venus_hfi_iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + 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; + + 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) { + dprintk(VIDC_ERR, "set property: failed to create packet\n"); + return -EINVAL; + } + + if (venus_hfi_iface_cmdq_write(session->device, pkt)) + return -ENOTEMPTY; + + 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; + + 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 (venus_hfi_iface_cmdq_write(session->device, &pkt)) { + rc = -ENOTEMPTY; + dprintk(VIDC_ERR, "%s cmdq_write error\n", __func__); + } +err_create_pkt: + return rc; +} + +static void venus_hfi_set_default_sys_properties( + struct venus_hfi_device *device) +{ + if (venus_hfi_sys_set_debug(device, msm_vidc_fw_debug)) + dprintk(VIDC_WARN, "Setting fw_debug msg ON failed\n"); + if (venus_hfi_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 (venus_hfi_sys_set_power_control(device, msm_vidc_fw_low_power_mode)) + dprintk(VIDC_WARN, "Setting h/w power collapse ON failed\n"); +} + +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; + venus_hfi_flush_debug_queue(sess_close->device, NULL); + dprintk(VIDC_DBG, "deleted the session: %p\n", + sess_close); + mutex_lock(&device->session_lock); + list_del(&sess_close->list); + kfree(sess_close); + mutex_unlock(&device->session_lock); + return 0; +} + +static void *venus_hfi_session_init(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type) +{ + struct hfi_cmd_sys_session_init_packet pkt; + struct hal_session *new_session; + struct venus_hfi_device *dev; + + if (device) { + dev = device; + } else { + dprintk(VIDC_ERR, "invalid device\n"); + return NULL; + } + + new_session = (struct hal_session *) + kzalloc(sizeof(struct hal_session), GFP_KERNEL); + if (!new_session) { + dprintk(VIDC_ERR, "new session fail: Out of memory\n"); + return NULL; + } + new_session->session_id = session_id; + new_session->is_decoder = (session_type == HAL_VIDEO_DOMAIN_DECODER); + new_session->device = dev; + + mutex_lock(&dev->session_lock); + list_add_tail(&new_session->list, &dev->sess_head); + mutex_unlock(&dev->session_lock); + + venus_hfi_set_default_sys_properties(device); + + if (call_hfi_pkt_op(dev, session_init, &pkt, + new_session, session_type, codec_type)) { + dprintk(VIDC_ERR, "session_init: failed to create packet\n"); + goto err_session_init_fail; + } + + if (venus_hfi_iface_cmdq_write(dev, &pkt)) + goto err_session_init_fail; + return (void *) new_session; + +err_session_init_fail: + venus_hfi_session_clean(new_session); + return NULL; +} + +static int venus_hfi_send_session_cmd(void *session_id, + int pkt_type) +{ + struct vidc_hal_session_cmd_pkt pkt; + int rc = 0; + struct hal_session *session = session_id; + struct venus_hfi_device *device; + + if (!session || !session->device) { + dprintk(VIDC_ERR, "invalid session"); + return -ENODEV; + } + device = session->device; + + rc = call_hfi_pkt_op(device, session_cmd, + &pkt, pkt_type, session); + if (rc) { + dprintk(VIDC_ERR, "send session cmd: create pkt failed\n"); + goto err_create_pkt; + } + + if (venus_hfi_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; + if (!session) { + dprintk(VIDC_ERR, "Invalid Params %s\n", __func__); + return -EINVAL; + } + sess = session; + if (msm_vidc_fw_coverage) { + if (venus_hfi_sys_set_coverage(sess->device, + msm_vidc_fw_coverage)) + dprintk(VIDC_WARN, "Fw_coverage msg ON failed\n"); + } + return venus_hfi_send_session_cmd(session, + HFI_CMD_SYS_SESSION_END); +} + +static int venus_hfi_session_abort(void *session) +{ + venus_hfi_flush_debug_queue( + ((struct hal_session *)session)->device, NULL); + return venus_hfi_send_session_cmd(session, + HFI_CMD_SYS_SESSION_ABORT); +} + +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; + + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) + return 0; + + 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 (venus_hfi_iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + 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; + + if (buffer_info->buffer_type == HAL_BUFFER_INPUT) + return 0; + + 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 (venus_hfi_iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + return rc; +} + +static int venus_hfi_session_load_res(void *sess) +{ + return venus_hfi_send_session_cmd(sess, + HFI_CMD_SESSION_LOAD_RESOURCES); +} + +static int venus_hfi_session_release_res(void *sess) +{ + return venus_hfi_send_session_cmd(sess, + HFI_CMD_SESSION_RELEASE_RESOURCES); +} + +static int venus_hfi_session_start(void *sess) +{ + return venus_hfi_send_session_cmd(sess, + HFI_CMD_SESSION_START); +} + +static int venus_hfi_session_stop(void *sess) +{ + return venus_hfi_send_session_cmd(sess, + HFI_CMD_SESSION_STOP); +} + +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; + + 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; + } + dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER\n"); + if (venus_hfi_iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; + } 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; + } + dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER\n"); + if (venus_hfi_iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; + } +err_create_pkt: + return rc; +} + +static int venus_hfi_session_ftb(void *sess, + struct vidc_frame_data *output_frame) +{ + struct hfi_cmd_session_fill_buffer_packet pkt; + 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; + + 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 (venus_hfi_iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + 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; + + 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 (venus_hfi_iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + 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; + + 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 (venus_hfi_iface_cmdq_write(session->device, pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + 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; + + 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 (venus_hfi_iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + 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; + + 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 (venus_hfi_iface_cmdq_write(session->device, &pkt)) + rc = -ENOTEMPTY; +err_create_pkt: + return rc; +} + +static int venus_hfi_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; + } else { + dprintk(VIDC_INFO, "Device not registered\n"); + return -EINVAL; + } + } + } else { + dprintk(VIDC_INFO, "no device Registered\n"); + } + return -EINVAL; +} + +static void venus_hfi_process_sys_watchdog_timeout( + struct venus_hfi_device *device) +{ + struct msm_vidc_cb_cmd_done cmd_done; + memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done)); + cmd_done.device_id = device->device_id; + device->callback(SYS_WATCHDOG_TIMEOUT, &cmd_done); +} + +static int venus_hfi_core_pc_prep(void *device) +{ + struct hfi_cmd_sys_pc_prep_packet pkt; + int rc = 0; + struct venus_hfi_device *dev = device; + + if (!dev) { + dprintk(VIDC_ERR, "invalid device\n"); + return -ENODEV; + } + + rc = call_hfi_pkt_op(dev, sys_pc_prep, &pkt); + if (rc) { + dprintk(VIDC_ERR, "Failed to create sys pc prep pkt\n"); + goto err_create_pkt; + } + + if (venus_hfi_iface_cmdq_write(dev, &pkt)) + rc = -ENOTEMPTY; + +err_create_pkt: + return rc; +} + +static int venus_hfi_prepare_pc(struct venus_hfi_device *device) +{ + int rc = 0; + init_completion(&pc_prep_done); + rc = venus_hfi_core_pc_prep(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to prepare venus for power off"); + goto err_pc_prep; + } + rc = wait_for_completion_timeout(&pc_prep_done, + msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); + if (!rc) { + dprintk(VIDC_ERR, + "Wait interrupted or timeout for PC_PREP_DONE: %d\n", + rc); + venus_hfi_flush_debug_queue(device, NULL); + rc = -EIO; + goto err_pc_prep; + } + rc = 0; +err_pc_prep: + return rc; +} + +static void venus_hfi_pm_hndlr(struct work_struct *work) +{ + int rc = 0; + u32 ctrl_status = 0; + 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; + } + if (!device->power_enabled) { + dprintk(VIDC_DBG, "%s: Power already disabled\n", + __func__); + return; + } + + mutex_lock(&device->write_lock); + mutex_lock(&device->read_lock); + rc = venus_hfi_core_in_valid_state(device); + mutex_unlock(&device->read_lock); + mutex_unlock(&device->write_lock); + + if (!rc) { + dprintk(VIDC_WARN, + "Core is in bad state, Skipping power collapse\n"); + return; + } + + dprintk(VIDC_DBG, "Prepare for power collapse\n"); + + if (device->resources.imem.type) { + mutex_lock(&device->resource_lock); + rc = venus_hfi_unset_free_imem(device); + mutex_unlock(&device->resource_lock); + if (rc) { + dprintk(VIDC_ERR, "Failed to unset IMEM for PC: %d\n", + rc); + goto err_unset_imem; + } + } + + rc = venus_hfi_prepare_pc(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to prepare for PC %d\n", rc); + goto err_prepare_pc; + } + + mutex_lock(&device->write_lock); + + if (device->last_packet_type != HFI_CMD_SYS_PC_PREP) { + dprintk(VIDC_DBG, + "Last command (%#x) is not PC_PREP cmd\n", + device->last_packet_type); + goto skip_power_off; + } + + if (venus_hfi_get_q_size(device, VIDC_IFACEQ_MSGQ_IDX) || + venus_hfi_get_q_size(device, VIDC_IFACEQ_CMDQ_IDX)) { + dprintk(VIDC_DBG, "Cmd/msg queues are not empty\n"); + goto skip_power_off; + } + + ctrl_status = venus_hfi_read_register(device, VIDC_CPU_CS_SCIACMDARG0); + if (!(ctrl_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY)) { + dprintk(VIDC_DBG, + "Venus is not ready for power collapse (%#x)\n", + ctrl_status); + goto skip_power_off; + } + + rc = venus_hfi_power_off(device); + if (rc) { + dprintk(VIDC_ERR, "Failed venus power off\n"); + goto err_power_off; + } + + /* Cancel pending delayed works if any */ + cancel_delayed_work(&venus_hfi_pm_work); + + mutex_unlock(&device->write_lock); + return; + +err_power_off: +skip_power_off: + + /* + * When power collapse is escaped, driver no need to inform Venus. + * Venus is self-sufficient to come out of the power collapse at + * any stage. Driver can skip power collapse and continue with + * normal execution. + */ + + /* Cancel pending delayed works if any */ + cancel_delayed_work(&venus_hfi_pm_work); + dprintk(VIDC_WARN, "Power off skipped (last pkt %#x, status: %#x)\n", + device->last_packet_type, ctrl_status); + + mutex_unlock(&device->write_lock); +err_prepare_pc: + venus_hfi_alloc_imem(device, device->res->imem_size); +err_unset_imem: + return; +} + +static void venus_hfi_process_msg_event_notify( + struct venus_hfi_device *device, void *packet) +{ + struct hfi_sfr_struct *vsfr = NULL; + struct hfi_msg_event_notify_packet *event_pkt; + struct vidc_hal_msg_pkt_hdr *msg_hdr; + + msg_hdr = (struct vidc_hal_msg_pkt_hdr *)packet; + event_pkt = + (struct hfi_msg_event_notify_packet *)msg_hdr; + if (event_pkt && event_pkt->event_id == + HFI_EVENT_SYS_ERROR) { + + venus_hfi_set_state(device, VENUS_STATE_DEINIT); + + /* 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 (venus_hfi_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 venus_hfi_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_TEMPORARY); + if (!packet) { + dprintk(VIDC_ERR, "In %s() Fail to allocate mem\n", + __func__); + return; + } + local_packet = true; + } + + while (!venus_hfi_iface_dbgq_read(device, packet)) { + struct hfi_msg_sys_coverage_packet *pkt = + (struct hfi_msg_sys_coverage_packet *) packet; + if (pkt->packet_type == HFI_MSG_SYS_COV) { +#ifdef CONFIG_MSM_VIDC_COV + int stm_size = 0; + dprintk(VIDC_DBG, + "DbgQ pkt size: %d\n", pkt->msg_size); + 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__); +#endif + } else { + struct hfi_msg_sys_debug_packet *pkt = + (struct hfi_msg_sys_debug_packet *) packet; + dprintk(VIDC_FW, "%s", pkt->rg_msg_data); + } + } + if (local_packet) + kfree(packet); +} + +static void venus_hfi_response_handler(struct venus_hfi_device *device) +{ + u8 *packet = NULL; + u32 rc = 0; + struct hfi_sfr_struct *vsfr = NULL; + + packet = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_TEMPORARY); + if (!packet) { + dprintk(VIDC_ERR, "In %s() Fail to allocate mem\n", __func__); + return; + } + dprintk(VIDC_INFO, "#####venus_hfi_response_handler#####\n"); + if (device) { + if (device->intr_status & + VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK) { + dprintk(VIDC_ERR, "Received: Watchdog timeout %s\n", + __func__); + vsfr = (struct hfi_sfr_struct *) + device->sfr.align_virtual_addr; + if (vsfr) + dprintk(VIDC_ERR, + "SFR Message from FW: %s\n", + vsfr->rg_data); + venus_hfi_process_sys_watchdog_timeout(device); + } + + while (!venus_hfi_iface_msgq_read(device, packet)) { + rc = hfi_process_msg_packet(device->callback, + device->device_id, + (struct vidc_hal_msg_pkt_hdr *) packet, + &device->sess_head, &device->session_lock); + if (rc == HFI_MSG_EVENT_NOTIFY) { + venus_hfi_process_msg_event_notify( + device, (void *)packet); + } else if (rc == HFI_MSG_SYS_RELEASE_RESOURCE) { + dprintk(VIDC_DBG, + "Received HFI_MSG_SYS_RELEASE_RESOURCE\n"); + complete(&release_resources_done); + } else if (rc == HFI_MSG_SYS_INIT_DONE) { + dprintk(VIDC_DBG, + "Received HFI_MSG_SYS_INIT_DONE\n"); + if (venus_hfi_alloc_set_imem(device, true)) + dprintk(VIDC_WARN, + "Failed to allocate IMEM. Performance will be impacted\n"); + } + } + venus_hfi_flush_debug_queue(device, packet); + switch (rc) { + case HFI_MSG_SYS_PC_PREP_DONE: + dprintk(VIDC_DBG, + "Received HFI_MSG_SYS_PC_PREP_DONE\n"); + complete(&pc_prep_done); + break; + } + } else { + dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT\n"); + } + kfree(packet); +} + +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); + + dprintk(VIDC_INFO, "GOT INTERRUPT\n"); + if (!device->callback) { + dprintk(VIDC_ERR, "No interrupt callback function: %p\n", + device); + return; + } + if (venus_hfi_power_enable(device)) { + dprintk(VIDC_ERR, "%s: Power enable failed\n", __func__); + return; + } + if (device->res->sw_power_collapsible) { + dprintk(VIDC_DBG, "Cancel and queue delayed work again.\n"); + 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"); + } + } + venus_hfi_core_clear_interrupt(device); + venus_hfi_response_handler(device); + if (!(device->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK)) + enable_irq(device->hal_data->irq); +} +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, "vidc_hal_isr %d\n", irq); + disable_irq_nosync(irq); + queue_work(device->vidc_workq, &venus_hfi_work); + return IRQ_HANDLED; +} + +static int venus_hfi_init_regs_and_interrupts( + struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + struct hal_data *hal = NULL; + int rc = 0; + + rc = venus_hfi_check_core_registered(hal_ctxt, + res->firmware_base, + (u8 *)(unsigned long)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 = (struct hal_data *) + 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 = ioremap_nocache(res->register_base, + (unsigned long)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 venus_hfi_deinit_clocks(struct venus_hfi_device *device) +{ + struct clock_info *cl; + + venus_hfi_for_each_clock_reverse(device, cl) { + if (cl->clk) { + clk_put(cl->clk); + cl->clk = NULL; + } + } +} + +static inline int venus_hfi_init_clocks(struct venus_hfi_device *device) +{ + int rc = 0; + struct clock_info *cl = NULL; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return -EINVAL; + } + + venus_hfi_for_each_clock(device, cl) { + int i = 0; + + dprintk(VIDC_DBG, "%s: scalable? %d\n", + cl->name, !!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; + } + } + } + + return 0; + +err_clk_get: + venus_hfi_deinit_clocks(device); + return rc; +} + + +static inline void venus_hfi_disable_unprepare_clks( + struct venus_hfi_device *device) +{ + struct clock_info *cl; + + if (!device) { + dprintk(VIDC_ERR, "Invalid params: %p\n", device); + return; + } + + if (device->clk_state == DISABLED_UNPREPARED) { + dprintk(VIDC_DBG, "Clocks already unprepared and disabled\n"); + return; + } + + /* + * Make the clock state variable as unprepared before actually + * unpreparing clocks. This will make sure that when we check + * the state, we have the right clock state. We are not taking + * any action based unprepare failures. So it is safe to do + * before the call. This is also in sync with prepare_enable + * state update. + */ + + device->clk_state = DISABLED_UNPREPARED; + + 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 venus_hfi_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: %p\n", device); + return -EINVAL; + } + + if (device->clk_state == ENABLED_PREPARED) { + dprintk(VIDC_DBG, "Clocks already prepared and enabled\n"); + return 0; + } + + 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->count) + 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 clock %s, %d\n", cl->name, rc); + cl_fail = cl; + goto fail_clk_enable; + } + + dprintk(VIDC_DBG, "Clock: %s prepared and enabled\n", cl->name); + } + + device->clk_state = ENABLED_PREPARED; + venus_hfi_write_register(device, VIDC_WRAPPER_CLOCK_CONFIG, 0); + venus_hfi_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); + } + device->clk_state = DISABLED_UNPREPARED; + return rc; +} + +static void venus_hfi_deinit_bus(struct venus_hfi_device *device) +{ + struct bus_info *bus = NULL; + if (!device) + return; + + venus_hfi_for_each_bus(device, bus) { + if (bus->priv) { + msm_bus_scale_unregister_client( + bus->priv); + bus->priv = 0; + dprintk(VIDC_DBG, "Unregistered bus client %s\n", + bus->pdata->name); + } + } + + kfree(device->bus_load.vote_data); + device->bus_load.vote_data = NULL; + device->bus_load.vote_data_count = 0; +} + +static int venus_hfi_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) { + const char *name = bus->pdata->name; + + if (!device->res->imem_type && + strnstr(name, "ocmem", strlen(name))) { + dprintk(VIDC_ERR, + "%s found when target doesn't support ocmem\n", + name); + rc = -EINVAL; + goto err_init_bus; + } else if (bus->passive && bus->pdata->num_usecases != 2) { + /* + * Passive buses can only be "turned on" and "turned + * off". We never scale them based on hardware load, + * and are usually used for the purposes of holding + * certain clocks high (in case we can't control these + * clocks directly). + */ + rc = -EINVAL; + dprintk(VIDC_ERR, + "Passive buses expected to have only 2 vectors\n"); + } + + bus->priv = msm_bus_scale_register_client(bus->pdata); + if (!bus->priv) { + dprintk(VIDC_ERR, + "Failed to register bus client %s\n", name); + rc = -EBADHANDLE; + goto err_init_bus; + } + + dprintk(VIDC_DBG, "Registered bus client %s\n", name); + } + + device->bus_load.vote_data = NULL; + device->bus_load.vote_data_count = 0; + + return rc; +err_init_bus: + venus_hfi_deinit_bus(device); + return rc; +} + +static void venus_hfi_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 venus_hfi_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, err: %d\n", + rinfo->name, rc); + rinfo->regulator = NULL; + goto err_reg_get; + } + } + + return 0; + +err_reg_get: + venus_hfi_deinit_regulators(device); + return rc; +} + +static int venus_hfi_init_resources(struct venus_hfi_device *device, + struct msm_vidc_platform_resources *res) +{ + int rc = 0; + +#if 0 + rc = venus_hfi_init_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to get all regulators\n"); + return -ENODEV; + } +#endif + + rc = venus_hfi_init_clocks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init clocks\n"); + rc = -ENODEV; + goto err_init_clocks; + } + + rc = venus_hfi_init_bus(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to init bus: %d\n", rc); + goto err_init_bus; + } + + return rc; + +err_init_bus: + venus_hfi_deinit_clocks(device); +err_init_clocks: + venus_hfi_deinit_regulators(device); + return rc; +} + +static void venus_hfi_deinit_resources(struct venus_hfi_device *device) +{ + venus_hfi_deinit_bus(device); + venus_hfi_deinit_clocks(device); + venus_hfi_deinit_regulators(device); +} + +static int protect_cp_mem(struct venus_hfi_device *device) +{ + u32 cp_start = 0; + u32 cp_size = 0; + u32 cp_nonpixel_start = 0; + u32 cp_nonpixel_size = 0; + struct context_bank_info *cb; + int ret; + + if (!device) + return -EINVAL; + + list_for_each_entry(cb, &device->res->context_banks, list) { + if (!strcmp(cb->name, "venus_ns")) { + cp_size = cb->addr_range.start; + dprintk(VIDC_DBG, "%s memprot.cp_size: %#x\n", + __func__, cp_size); + } + + if (!strcmp(cb->name, "venus_sec_non_pixel")) { + cp_nonpixel_start = cb->addr_range.start; + cp_nonpixel_size = cb->addr_range.size; + dprintk(VIDC_DBG, + "%s memprot.cp_start: %#x size: %#x\n", + __func__, cp_nonpixel_start, + cp_nonpixel_size); + } + } + + ret = qcom_scm_mem_protect_video_var(cp_start, cp_size, + cp_nonpixel_start, + cp_nonpixel_size); + if (ret) { + dprintk(VIDC_ERR, "Failed to protect memory (%d)\n", ret); + return ret; + } + + return 0; +} + +static int venus_hfi_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 = venus_hfi_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(1); + return rc; +} + +static int venus_hfi_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 = venus_hfi_hand_off_regulators(device); + if (rc) + dprintk(VIDC_WARN, + "%s : Failed to enable HW power collapse %d\n", + __func__, rc); + return rc; +} + +static int venus_hfi_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) + venus_hfi_disable_regulator(rinfo); + + return rc; +} + +static int venus_hfi_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) + venus_hfi_disable_regulator(rinfo); + + return 0; +} + +static int venus_hfi_load_fw(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + + if (!device) { + dprintk(VIDC_ERR, "%s Invalid paramter: %p\n", + __func__, device); + return -EINVAL; + } + + /* Initialize hardware resources */ + rc = venus_hfi_init_resources(device, device->res); + if (rc) { + dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); + goto fail_init_res; + } + + rc = venus_hfi_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"); + + /* Vote for all hardware resources */ + rc = venus_hfi_vote_buses(device, device->bus_load.vote_data, + device->bus_load.vote_data_count); + if (rc) { + dprintk(VIDC_ERR, + "Failed to vote buses when loading firmware: %d\n", + rc); + goto fail_vote_buses; + } + +#if 0 + rc = venus_hfi_enable_regulators(device); + if (rc) { + dprintk(VIDC_ERR, "%s : Failed to enable GDSC, Err = %d\n", + __func__, rc); + goto fail_enable_gdsc; + } +#endif + + /* iommu_attach makes call to TZ for restore_sec_cfg. With this call + * TZ accesses the VMIDMT block which needs all the Venus clocks. + */ + rc = venus_hfi_prepare_enable_clks(device); + if (rc) { + dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc); + goto fail_enable_clks; + } + + 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 = +#if 0 + subsystem_get_with_fwname("venus", + device->res->fw_name); +#else + subsystem_get("venus"); +#endif + + if (IS_ERR_OR_NULL(device->resources.fw.cookie)) { + dprintk(VIDC_ERR, "Failed to download firmware\n"); + rc = -ENOMEM; + goto fail_load_fw; + } + } + device->power_enabled = true; + + /* Hand off control of regulators to h/w _after_ enabling clocks */ + venus_hfi_enable_hw_power_collapse(device); + + if (!device->res->use_non_secure_pil && !device->res->firmware_base) { +#if 0 + rc = protect_cp_mem(device); +#else + rc = 0; +#endif + 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: + device->power_enabled = false; + if (device->resources.fw.cookie) + subsystem_put(device->resources.fw.cookie); + device->resources.fw.cookie = NULL; +fail_load_fw: + venus_hfi_disable_unprepare_clks(device); +fail_enable_clks: + venus_hfi_disable_regulators(device); +fail_enable_gdsc: + venus_hfi_unvote_buses(device); +fail_vote_buses: +fail_init_pkt: + venus_hfi_deinit_resources(device); +fail_init_res: + trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end"); + return rc; +} + +static void venus_hfi_unload_fw(void *dev) +{ + struct venus_hfi_device *device = dev; + if (!device) { + dprintk(VIDC_ERR, "%s Invalid paramter: %p\n", + __func__, device); + return; + } + if (device->resources.fw.cookie) { + flush_workqueue(device->vidc_workq); + cancel_delayed_work(&venus_hfi_pm_work); + flush_workqueue(device->venus_pm_workq); + subsystem_put(device->resources.fw.cookie); + venus_hfi_interface_queues_release(dev); + + /* Halt the AXI to make sure there are no pending transactions. + * Clocks should be unprepared after making sure axi is halted. + */ + if (venus_hfi_halt_axi(device)) + dprintk(VIDC_WARN, "Failed to halt AXI\n"); + venus_hfi_disable_unprepare_clks(device); +#if 0 + venus_hfi_disable_regulators(device); +#endif + venus_hfi_unvote_buses(device); + device->power_enabled = false; + device->resources.fw.cookie = NULL; + } +} + +static int venus_hfi_resurrect_fw(void *dev) +{ + struct venus_hfi_device *device = dev; + int rc = 0; + + if (!device) { + dprintk(VIDC_ERR, "%s Invalid paramter: %p\n", + __func__, device); + return -EINVAL; + } + + rc = venus_hfi_core_release(device); + if (rc) { + dprintk(VIDC_ERR, "%s - failed to release venus core rc = %d\n", + __func__, rc); + goto exit; + } + + dprintk(VIDC_ERR, "praying for firmware resurrection\n"); + venus_hfi_unload_fw(device); + + rc = venus_hfi_vote_buses(device, device->bus_load.vote_data, + device->bus_load.vote_data_count); + if (rc) { + dprintk(VIDC_ERR, "Failed to scale buses\n"); + goto exit; + } + + rc = venus_hfi_load_fw(device); + if (rc) { + dprintk(VIDC_ERR, "%s - failed to load venus fw rc = %d\n", + __func__, rc); + goto exit; + } + + dprintk(VIDC_ERR, "Hurray!! firmware has restarted\n"); +exit: + return rc; +} + +static int venus_hfi_get_fw_info(void *dev, enum fw_info info) +{ + int rc = 0; + struct venus_hfi_device *device = dev; + + if (!device) { + dprintk(VIDC_ERR, "%s Invalid paramter: %p\n", + __func__, device); + return -EINVAL; + } + + switch (info) { + case FW_BASE_ADDRESS: + rc = (u32)device->hal_data->firmware_base; + if ((phys_addr_t)rc != device->hal_data->firmware_base) { + dprintk(VIDC_INFO, + "%s: firmware_base (%pa) truncated to %#x", + __func__, &device->hal_data->firmware_base, rc); + } + break; + + case FW_REGISTER_BASE: + rc = (u32)device->res->register_base; + if ((phys_addr_t)rc != device->res->register_base) { + dprintk(VIDC_INFO, + "%s: register_base (%pa) truncated to %#x", + __func__, &device->res->register_base, rc); + } + break; + + case FW_REGISTER_SIZE: + rc = device->hal_data->register_size; + break; + + case FW_IRQ: + rc = device->hal_data->irq; + break; + + default: + dprintk(VIDC_ERR, "Invalid fw info requested\n"); + } + return rc; +} + +int venus_hfi_get_stride_scanline(int color_fmt, + int width, int height, int *stride, int *scanlines) { + *stride = VENUS_Y_STRIDE(color_fmt, width); + *scanlines = VENUS_Y_SCANLINES(color_fmt, height); + return 0; +} + +int venus_hfi_get_core_capabilities(void) +{ + int rc = 0; + rc = HAL_VIDEO_ENCODER_ROTATION_CAPABILITY | + HAL_VIDEO_ENCODER_SCALING_CAPABILITY | + HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY | + HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY; + return rc; +} + +static int venus_hfi_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 void *venus_hfi_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 = (struct venus_hfi_device *) + kzalloc(sizeof(struct venus_hfi_device), GFP_KERNEL); + if (!hdevice) { + dprintk(VIDC_ERR, "failed to allocate new device\n"); + goto err_alloc; + } + + rc = venus_hfi_init_regs_and_interrupts(hdevice, res); + if (rc) + goto err_init_regs; + + 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 error_createq; + } + hdevice->venus_pm_workq = create_singlethread_workqueue( + "pm_workerq_venus"); + if (!hdevice->venus_pm_workq) { + dprintk(VIDC_ERR, ": create pm workq failed\n"); + goto error_createq_pm; + } + + mutex_init(&hdevice->read_lock); + mutex_init(&hdevice->write_lock); + mutex_init(&hdevice->session_lock); + mutex_init(&hdevice->resource_lock); + + if (!hal_ctxt.dev_count) + INIT_LIST_HEAD(&hal_ctxt.dev_head); + + 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 (void *) hdevice; +error_createq_pm: + destroy_workqueue(hdevice->vidc_workq); +error_createq: +err_init_regs: + kfree(hdevice); +err_alloc: + return NULL; +} + +static void *venus_hfi_get_device(u32 device_id, + struct msm_vidc_platform_resources *res, + hfi_cmd_response_callback callback) +{ + if (!res || !callback) { + dprintk(VIDC_ERR, "Invalid params: %p %p\n", res, callback); + return NULL; + } + + return venus_hfi_add_device(device_id, res, callback); +} + +void venus_hfi_delete_device(void *device) +{ + struct venus_hfi_device *close, *tmp, *dev; + + if (device) { + venus_hfi_iommu_detach(device); + venus_hfi_deinit_resources(device); + dev = (struct venus_hfi_device *) device; + 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); + break; + } + } + + } +} + +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_pc_prep = venus_hfi_core_pc_prep; + 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_stop = venus_hfi_session_stop; + hdev->session_etb = venus_hfi_session_etb; + hdev->session_ftb = venus_hfi_session_ftb; + 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_active_buses; + hdev->unvote_bus = venus_hfi_unvote_active_buses; + hdev->load_fw = venus_hfi_load_fw; + hdev->unload_fw = venus_hfi_unload_fw; + hdev->resurrect_fw = venus_hfi_resurrect_fw; + hdev->get_fw_info = venus_hfi_get_fw_info; + hdev->get_stride_scanline = venus_hfi_get_stride_scanline; + hdev->get_core_capabilities = venus_hfi_get_core_capabilities; + hdev->power_enable = venus_hfi_power_enable; + 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: %p %p %p\n", + hdev, res, callback); + rc = -EINVAL; + goto err_venus_hfi_init; + } + hdev->hfi_device_data = venus_hfi_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/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h new file mode 100644 index 000000000000..2ae40db73b99 --- /dev/null +++ b/drivers/media/platform/msm/vidc/venus_hfi.h @@ -0,0 +1,280 @@ +/* Copyright (c) 2012-2015, 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 <linux/clk.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <soc/qcom/ocmem.h> +#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 + +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, +}; + +enum bus_index { + BUS_IDX_ENC_IMEM, + BUS_IDX_DEC_IMEM, + BUS_IDX_ENC_DDR, + BUS_IDX_DEC_DDR, + BUS_IDX_MAX +}; + +enum clock_state { + DISABLED_UNPREPARED, + ENABLED_PREPARED, +}; + +struct vidc_mem_addr { + ion_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 unneccessary 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) + + +/* 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 on_chip_mem { + struct ocmem_buf *buf; + struct notifier_block vidc_ocmem_nb; + void *handle; +}; + +struct imem { + enum imem_type type; + union { + struct on_chip_mem ocmem; + 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_load; + u32 codecs_enabled; + u32 last_packet_type; + struct { + struct vidc_bus_vote_data *vote_data; + u32 vote_data_count; + } bus_load; + enum clock_state clk_state; + bool power_enabled; + struct mutex read_lock; + struct mutex write_lock; + struct mutex resource_lock; + struct mutex session_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 smem_client *hal_client; + 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; +}; + +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); +#endif diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.c b/drivers/media/platform/msm/vidc/vidc_hfi.c new file mode 100644 index 000000000000..ef0de370eb09 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2012-2013, 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 <linux/slab.h> +#include "msm_vidc_debug.h" +#include "vidc_hfi_api.h" +#include "venus_hfi.h" +#include "q6_hfi.h" + +void *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 = (struct hfi_device *) + 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; + + case VIDC_HFI_Q6: + rc = q6_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 %p", __func__, hdev); + return; + } + + switch (hfi_type) { + case VIDC_HFI_VENUS: + venus_hfi_delete_device(hdev->hfi_device_data); + break; + + case VIDC_HFI_Q6: + q6_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/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h new file mode 100644 index 000000000000..4b9a9df6431a --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi.h @@ -0,0 +1,890 @@ +/* Copyright (c) 2012-2015, 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 <media/msm_media_info.h> +#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_OUTPUT2 (HFI_OX_BASE + 0x3) +#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_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_DIGITAL_ZOOM 0x07000010 +#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_ACTUAL \ + (HFI_PROPERTY_PARAM_OX_START + 0x00C) +#define HFI_PROPERTY_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL \ + (HFI_PROPERTY_PARAM_OX_START + 0x00D) + + +#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_CONFIG_VDEC_OX_START \ + (HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x0000) +#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_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_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_actual { + u32 buffer_type; + u32 buffer_size; +}; + +struct hfi_buffer_display_hold_count_actual { + u32 buffer_type; + u32 hold_count; +}; + +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) + +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_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[0]; +}; + +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_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 hal_session { + struct list_head list; + void *session_id; + bool is_decoder; + void *device; +}; + +struct hal_device_data { + struct list_head dev_head; + int dev_count; +}; + +struct msm_vidc_fw { + void *cookie; +}; + +u32 hfi_process_msg_packet(msm_vidc_callback callback, + u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr, + struct list_head *sessions, struct mutex *session_lock); + +struct hal_session *hfi_process_get_session( + struct list_head *sessions, u32 session_id); +#endif + diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h new file mode 100644 index 000000000000..5bf0d98d74dd --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -0,0 +1,1392 @@ +/* Copyright (c) 2012-2015, 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 <linux/log2.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <media/msm_vidc.h> +#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 + +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_FRAME_QP, + HAL_EXTRADATA_FRAME_BITS_INFO, + HAL_EXTRADATA_INPUT_CROP, + HAL_EXTRADATA_DIGITAL_ZOOM, + HAL_EXTRADATA_LTR_INFO, + HAL_EXTRADATA_METADATA_MBI, +}; + +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_ACTUAL, + HAL_PARAM_BUFFER_DISPLAY_HOLD_COUNT_ACTUAL, + 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_LOW_LATENCY, + 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_H264_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, +}; + +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_HEVC_HYBRID = 0x00004000, + 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_UNUSED_COLOR = 0x10000000, +}; + +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 { + u32 enable; +}; + +struct hal_buffer_count_actual { + enum hal_buffer buffer_type; + u32 buffer_count_actual; +}; + +struct hal_buffer_size_actual { + 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_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_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, + 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_SECURE_OUTPUT2_THRESHOLD, + 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_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]; +}; + +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; + ion_phys_addr_t align_device_addr; + ion_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; + ion_phys_addr_t device_addr; + ion_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 { + ion_phys_addr_t seq_hdr; + u32 seq_hdr_len; +}; + +enum hal_flush { + HAL_FLUSH_INPUT, + HAL_FLUSH_OUTPUT, + HAL_FLUSH_OUTPUT2, + 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; +}; + +struct hal_venc_perf_mode { + u32 mode; +}; + +struct hal_hybrid_hierp { + u32 layers; +}; + +struct hfi_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_h264_entropy_control h264_entropy_control; + 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; +}; + +/* HAL Response */ + +enum command_response { +/* SYSTEM COMMANDS_DONE*/ + VIDC_EVENT_CHANGE, + SYS_INIT_DONE, + SET_RESOURCE_DONE, + RELEASE_RESOURCE_DONE, + PING_ACK_DONE, + PC_PREP_DONE, + SYS_IDLE, + SYS_DEBUG, + SYS_WATCHDOG_TIMEOUT, + SYS_ERROR, +/* SESSION COMMANDS_DONE */ + SESSION_LOAD_RESOURCE_DONE, + SESSION_INIT_DONE, + SESSION_END_DONE, + SESSION_ABORT_DONE, + SESSION_START_DONE, + SESSION_STOP_DONE, + SESSION_ETB_DONE, + SESSION_FTB_DONE, + SESSION_FLUSH_DONE, + SESSION_SUSPEND_DONE, + SESSION_RESUME_DONE, + SESSION_SET_PROP_DONE, + SESSION_GET_PROP_DONE, + SESSION_PARSE_SEQ_HDR_DONE, + SESSION_GET_SEQ_HDR_DONE, + SESSION_RELEASE_BUFFER_DONE, + SESSION_RELEASE_RESOURCE_DONE, + SESSION_PROPERTY_INFO, + SESSION_ERROR, + RESPONSE_UNUSED = 0x10000000, +}; + +/* Command Callback structure */ + +struct msm_vidc_cb_cmd_done { + u32 device_id; + void *session_id; + enum vidc_status status; + u32 size; + void *data; +}; + +struct msm_vidc_cb_event { + u32 device_id; + u32 session_id; + enum vidc_status status; + u32 height; + u32 width; + u32 hal_event_type; + ion_phys_addr_t packet_buffer; + ion_phys_addr_t extra_data_buffer; +}; + +/* Data callback structure */ + +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; + ion_phys_addr_t packet_buffer; + ion_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; + ion_phys_addr_t packet_buffer1; + ion_phys_addr_t extra_data_buffer; + u32 flags2; + u32 alloc_len2; + u32 filled_len2; + u32 offset2; + ion_phys_addr_t packet_buffer2; + u32 flags3; + u32 alloc_len3; + u32 filled_len3; + u32 offset3; + ion_phys_addr_t packet_buffer3; + enum hal_buffer buffer_type; +}; + +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 vidc_hal_sys_init_done { + u32 enc_codec_supported; + u32 dec_codec_supported; +}; + +struct vidc_hal_session_init_done { + 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 hier_p; + struct hal_capability_supported ltr_count; + struct hal_capability_supported secure_output2_threshold; + 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_profile_level_supported profile_level; + /*allocate and released memory for above.*/ + struct hal_intra_refresh intra_refresh; + struct hal_seq_header_info seq_hdr_info; + enum buffer_mode_type alloc_mode_out; +}; + +enum msm_vidc_hfi_type { + VIDC_HFI_VENUS, + VIDC_HFI_Q6, +}; + +enum fw_info { + FW_BASE_ADDRESS, + FW_REGISTER_BASE, + FW_REGISTER_SIZE, + FW_IRQ, + FW_INFO_MAX, +}; + +enum msm_vidc_thermal_level { + VIDC_THERMAL_NORMAL = 0, + VIDC_THERMAL_LOW, + VIDC_THERMAL_HIGH, + VIDC_THERMAL_CRITICAL +}; + +enum vidc_bus_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_bus_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 vidc_bus_vote_data { + enum vidc_bus_vote_data_session session; + int load; +}; + +#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_release)(void *device); + int (*core_pc_prep)(void *device); + int (*core_ping)(void *device); + int (*core_trigger_ssr)(void *device, enum hal_ssr_trigger_type); + void *(*session_init)(void *device, void *session_id, + enum hal_domain session_type, enum hal_video_codec codec_type); + 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_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_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, int codecs_enabled); + int (*vote_bus)(void *dev, struct vidc_bus_vote_data *data, + int num_data); + int (*unvote_bus)(void *dev); + int (*load_fw)(void *dev); + void (*unload_fw)(void *dev); + int (*resurrect_fw)(void *dev); + int (*get_fw_info)(void *dev, enum fw_info info); + int (*get_stride_scanline)(int color_fmt, int width, + int height, int *stride, int *scanlines); + int (*session_clean)(void *sess); + int (*get_core_capabilities)(void); + int (*power_enable)(void *dev); + int (*suspend)(void *dev); + unsigned long (*get_core_clock_rate)(void *dev); + enum hal_default_properties (*get_default_properties)(void *dev); +}; + +typedef void (*hfi_cmd_response_callback) (enum command_response cmd, + void *data); +typedef void (*msm_vidc_callback) (u32 response, void *callback); + +void *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); + + +#endif /*__VIDC_HFI_API_H__ */ diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h new file mode 100644 index 000000000000..cf06b8970527 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -0,0 +1,1075 @@ +/* Copyright (c) 2012-2014, 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_HEVC_HYBRID 0x00004000 + +#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_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_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_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_H264_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_FULL_RANGE \ + (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_PRESERVE_TEXT_QUALITY \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023) +#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_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_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_VPE_COMMON_START \ + (HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000) +#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_bitrate { + u32 bit_rate; + u32 layer_id; +}; + +#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_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_P_HYBRID_NUM_ENH_LAYERS (HFI_COMMON_BASE + 0x15) + +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 { + int 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; + int slice_alpha_offset; + int 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_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_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 + +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_h264_vui_timing_info { + u32 enable; + u32 fixed_frame_rate; + u32 time_scale; +}; + +/* 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 + +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 { + int 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_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]; +}; + +#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; + int close_gop; + int h264_constrain_intra_pred; + int h264_transform_8x8_flag; + int mpeg4_qpel_enable; + int multi_refp_en; + int 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_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_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_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_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/vidc_hfi_io.h b/drivers/media/platform/msm/vidc/vidc_hfi_io.h new file mode 100644 index 000000000000..337291b0042f --- /dev/null +++ b/drivers/media/platform/msm/vidc/vidc_hfi_io.h @@ -0,0 +1,191 @@ +/* Copyright (c) 2012-2014, 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 <linux/io.h> + +#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_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_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_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/vmem/Kconfig b/drivers/media/platform/msm/vidc/vmem/Kconfig new file mode 100644 index 000000000000..e602e52da383 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vmem/Kconfig @@ -0,0 +1,3 @@ +menuconfig MSM_VIDC_VMEM + bool "Qualcomm Technologies Inc MSM VMEM driver" + depends on ARCH_QCOM && MSM_VIDC_V4L2 diff --git a/drivers/media/platform/msm/vidc/vmem/Makefile b/drivers/media/platform/msm/vidc/vmem/Makefile new file mode 100644 index 000000000000..713b92e64544 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vmem/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MSM_VIDC_VMEM) := vmem.o \ + vmem_debugfs.o diff --git a/drivers/media/platform/msm/vidc/vmem/vmem.c b/drivers/media/platform/msm/vidc/vmem/vmem.c new file mode 100644 index 000000000000..0035457d5cba --- /dev/null +++ b/drivers/media/platform/msm/vidc/vmem/vmem.c @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2014-2015, 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 <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/msm-bus.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#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 +#define BYTES_PER_BANK SZ_256K + +enum bank_state { + BANK_STATE_NORM_PASSTHRU = 0b000, + BANK_STATE_NORM_FORCE_CORE_ON = 0b010, + BANK_STATE_NORM_FORCE_PERIPH_ON = 0b001, + BANK_STATE_NORM_FORCE_ALL_ON = 0b011, + BANK_STATE_SLEEP_RET = 0b110, + BANK_STATE_SLEEP_RET_PERIPH_ON = 0b111, + BANK_STATE_SLEEP_NO_RET = 0b100, +}; + +struct vmem { + int irq; + int num_banks; + 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 %p ", 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 -> %p\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)", 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 + }; + + BUG_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); + 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}, + }; + + BUG_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; + } + + 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; + } + + BUG_ON(vmem->num_banks != DIV_ROUND_UP(size, BYTES_PER_BANK)); + + /* Make sure all the banks are sleeping (default) */ + for (c = 0; c < vmem->num_banks; ++c) { + enum bank_state curr_bank_state = __bank_get_state(vmem, c); + + if (curr_bank_state != BANK_STATE_SLEEP_NO_RET) { + pr_err("Found bank %d in a wrong state, expected %d, was %d\n", + c, BANK_STATE_SLEEP_NO_RET, + curr_bank_state); + rc = -EIO; + goto disable_clocks; + } + } + + /* 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; + +disable_clocks: + __power_off(vmem); +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; + + BUG_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_cont("\tinterrupt status: %x\n", stat); + pr_cont("\tgeneral status: %x\n", gen_stat); + pr_cont("\tmemory status: %x\n", pscgc_stat); + pr_cont("\tfault address: %x (absolute), %x (relative)\n", + err_addr_abs, err_addr_rel); + pr_cont("\tfault bank: %x\n", err_addr_rel / BYTES_PER_BANK); + pr_cont("\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(pdev->dev.of_node, "clock-names", + &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,banks", + &v->num_banks); + if (rc || !v->num_banks) { + pr_err("Failed reading (or found invalid) qcom,banks in %s (%d)\n", + of_node_full_name(pdev->dev.of_node), rc); + rc = -ENOENT; + goto free_pdata; + } + + pr_debug("Found configuration with %d banks\n", v->num_banks); + + 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", pdev->name); + return -EEXIST; + } + + v = devm_kzalloc(&pdev->dev, sizeof(*v), GFP_KERNEL); + if (!v) { + pr_err("Failed allocate context memory in probe\n"); + 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 exceeds what's available on chip (%d)\n", + v->num_banks, num_banks); + rc = -EINVAL; + goto disable_clocks; + } + + if (v->num_banks * BYTES_PER_BANK > + resource_size(v->mem.resource)) { + pr_err("Too many banks in configuration\n"); + rc = -E2BIG; + 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); + + BUG_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", + .owner = THIS_MODULE, + .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/vmem/vmem.h b/drivers/media/platform/msm/vidc/vmem/vmem.h new file mode 100644 index 000000000000..751904e00755 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vmem/vmem.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014, 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/vmem/vmem_debugfs.c b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.c new file mode 100644 index 000000000000..7b9b230bedba --- /dev/null +++ b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, 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 <linux/debugfs.h> +#include <linux/fs.h> +#include <linux/platform_device.h> +#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_SIMPLE_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 '<debugfs>/vmem'\n"); + debugfs_root = NULL; + goto exit; + } + + debugfs_create_file("alloc", S_IRUSR | S_IWUSR, 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/vmem/vmem_debugfs.h b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.h new file mode 100644 index 000000000000..6d86ffbd8401 --- /dev/null +++ b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2014, 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 <linux/debugfs.h> + +struct dentry *vmem_debugfs_init(struct platform_device *pdev); +void vmem_debugfs_deinit(struct dentry *debugfs_root); + +#endif /* __VMEM_DEBUGFS_H__ */ diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index cc16e76a2493..7451bc63497e 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -913,7 +913,7 @@ static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) /* * Make sure the requested values and current defaults are sane. */ - num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); + num_buffers = min_t(unsigned int, req->count, VB2_MAX_FRAME); num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed); memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); @@ -1018,7 +1018,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create unsigned int num_planes = 0, num_buffers, allocated_buffers; int ret; - if (q->num_buffers == VIDEO_MAX_FRAME) { + if (q->num_buffers == VB2_MAX_FRAME) { dprintk(1, "maximum number of buffers already allocated\n"); return -ENOBUFS; } @@ -1030,7 +1030,7 @@ static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type); } - num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers); + num_buffers = min(create->count, VB2_MAX_FRAME - q->num_buffers); /* * Ask the driver, whether the requested number of buffers, planes per @@ -2755,7 +2755,7 @@ struct vb2_fileio_data { struct v4l2_requestbuffers req; struct v4l2_plane p; struct v4l2_buffer b; - struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME]; + struct vb2_fileio_buf bufs[VB2_MAX_FRAME]; unsigned int cur_index; unsigned int initial_index; unsigned int q_count; diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 4c29e72c4177..c36c90309d42 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -45,4 +45,8 @@ extern int qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); extern int qcom_scm_get_feat_version(u32 feat); extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); +extern int qcom_scm_set_video_state(u32 state, u32 spare); +extern int qcom_scm_mem_protect_video_var(u32 start, u32 size, + u32 nonpixel_start, + u32 nonpixel_size); #endif diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h new file mode 100644 index 000000000000..83aa7ee1c139 --- /dev/null +++ b/include/media/msm_media_info.h @@ -0,0 +1,611 @@ +#ifndef __MEDIA_INFO_H__ +#define __MEDIA_INFO_H__ + +#ifndef MSM_MEDIA_ALIGN +#define MSM_MEDIA_ALIGN(__sz, __align) (((__sz) + (__align-1)) & (~(__align-1))) +#endif + +#ifndef MSM_MEDIA_ROUNDUP +#define MSM_MEDIA_ROUNDUP(__sz, __r) (((__sz) + ((__r) - 1)) / (__r)) +#endif + +enum color_fmts { + /* Venus NV12: + * YUV 4:2:0 image with a plane of 8 bit Y samples followed + * by an interleaved U/V plane containing 8 bit 2x2 subsampled + * colour difference samples. + * + * <-------- Y/UV_Stride --------> + * <------- Width -------> + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * U V U V U V U V U V U V . . . . ^ + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . | + * U V U V U V U V U V U V . . . . UV_Scanlines + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . --> Buffer size alignment + * + * Y_Stride : Width aligned to 128 + * UV_Stride : Width aligned to 128 + * Y_Scanlines: Height aligned to 32 + * UV_Scanlines: Height/2 aligned to 16 + * Extradata: Arbitrary (software-imposed) padding + * Total size = align((Y_Stride * Y_Scanlines + * + UV_Stride * UV_Scanlines + Extradata), 4096) + */ + COLOR_FMT_NV12, + + /* Venus NV21: + * YUV 4:2:0 image with a plane of 8 bit Y samples followed + * by an interleaved V/U plane containing 8 bit 2x2 subsampled + * colour difference samples. + * + * <-------- Y/UV_Stride --------> + * <------- Width -------> + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * V U V U V U V U V U V U . . . . ^ + * V U V U V U V U V U V U . . . . | + * V U V U V U V U V U V U . . . . | + * V U V U V U V U V U V U . . . . UV_Scanlines + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . --> Padding & Buffer size alignment + * + * Y_Stride : Width aligned to 128 + * UV_Stride : Width aligned to 128 + * Y_Scanlines: Height aligned to 32 + * UV_Scanlines: Height/2 aligned to 16 + * Extradata: Arbitrary (software-imposed) padding + * Total size = align((Y_Stride * Y_Scanlines + * + UV_Stride * UV_Scanlines + Extradata), 4096) + */ + COLOR_FMT_NV21, + /* Venus NV12_MVTB: + * Two YUV 4:2:0 images/views one after the other + * in a top-bottom layout, same as NV12 + * with a plane of 8 bit Y samples followed + * by an interleaved U/V plane containing 8 bit 2x2 subsampled + * colour difference samples. + * + * + * <-------- Y/UV_Stride --------> + * <------- Width -------> + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | | + * . . . . . . . . . . . . . . . . | View_1 + * . . . . . . . . . . . . . . . . | | + * . . . . . . . . . . . . . . . . | | + * . . . . . . . . . . . . . . . . V | + * U V U V U V U V U V U V . . . . ^ | + * U V U V U V U V U V U V . . . . | | + * U V U V U V U V U V U V . . . . | | + * U V U V U V U V U V U V . . . . UV_Scanlines | + * . . . . . . . . . . . . . . . . | | + * . . . . . . . . . . . . . . . . V V + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^ ^ + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | Y_Scanlines | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | | | + * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V | | + * . . . . . . . . . . . . . . . . | View_2 + * . . . . . . . . . . . . . . . . | | + * . . . . . . . . . . . . . . . . | | + * . . . . . . . . . . . . . . . . V | + * U V U V U V U V U V U V . . . . ^ | + * U V U V U V U V U V U V . . . . | | + * U V U V U V U V U V U V . . . . | | + * U V U V U V U V U V U V . . . . UV_Scanlines | + * . . . . . . . . . . . . . . . . | | + * . . . . . . . . . . . . . . . . V V + * . . . . . . . . . . . . . . . . --> Buffer size alignment + * + * Y_Stride : Width aligned to 128 + * UV_Stride : Width aligned to 128 + * Y_Scanlines: Height aligned to 32 + * UV_Scanlines: Height/2 aligned to 16 + * View_1 begin at: 0 (zero) + * View_2 begin at: Y_Stride * Y_Scanlines + UV_Stride * UV_Scanlines + * Extradata: Arbitrary (software-imposed) padding + * Total size = align((2*(Y_Stride * Y_Scanlines) + * + 2*(UV_Stride * UV_Scanlines) + Extradata), 4096) + */ + COLOR_FMT_NV12_MVTB, + /* Venus NV12 UBWC: + * Compressed Macro-tile format for NV12. + * Contains 4 planes in the following order - + * (A) Y_Meta_Plane + * (B) Y_UBWC_Plane + * (C) UV_Meta_Plane + * (D) UV_UBWC_Plane + * + * Y_Meta_Plane consists of meta information to decode compressed + * tile data in Y_UBWC_Plane. + * Y_UBWC_Plane consists of Y data in compressed macro-tile format. + * UBWC decoder block will use the Y_Meta_Plane data together with + * Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples. + * + * UV_Meta_Plane consists of meta information to decode compressed + * tile data in UV_UBWC_Plane. + * UV_UBWC_Plane consists of UV data in compressed macro-tile format. + * UBWC decoder block will use UV_Meta_Plane data together with + * UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2 + * subsampled color difference samples. + * + * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable + * and randomly accessible. There is no dependency between tiles. + * + * <----- Y_Meta_Stride ----> + * <-------- Width ------> + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height | + * M M M M M M M M M M M M . . | Meta_Y_Scanlines + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . V | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . V + * <--Compressed tile Y Stride---> + * <------- Width -------> + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . . . V + * <----- UV_Meta_Stride ----> + * M M M M M M M M M M M M . . ^ + * M M M M M M M M M M M M . . | + * M M M M M M M M M M M M . . | + * M M M M M M M M M M M M . . M_UV_Scanlines + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * <--Compressed tile UV Stride---> + * U* V* U* V* U* V* U* V* . . . . ^ + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . UV_Scanlines + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * + * Y_Stride = align(Width, 128) + * UV_Stride = align(Width, 128) + * Y_Scanlines = align(Height, 32) + * UV_Scanlines = align(Height/2, 16) + * Y_UBWC_Plane_size = align(Y_Stride * Y_Scanlines, 4096) + * UV_UBWC_Plane_size = align(UV_Stride * UV_Scanlines, 4096) + * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) + * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16) + * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096) + * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) + * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) + * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) + * Extradata = 8k + * + * Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size + + * Y_Meta_Plane_size + UV_Meta_Plane_size + Extradata, 4096) + */ + COLOR_FMT_NV12_UBWC, + /* Venus NV12 10-bit UBWC: + * Compressed Macro-tile format for NV12. + * Contains 4 planes in the following order - + * (A) Y_Meta_Plane + * (B) Y_UBWC_Plane + * (C) UV_Meta_Plane + * (D) UV_UBWC_Plane + * + * Y_Meta_Plane consists of meta information to decode compressed + * tile data in Y_UBWC_Plane. + * Y_UBWC_Plane consists of Y data in compressed macro-tile format. + * UBWC decoder block will use the Y_Meta_Plane data together with + * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples. + * + * UV_Meta_Plane consists of meta information to decode compressed + * tile data in UV_UBWC_Plane. + * UV_UBWC_Plane consists of UV data in compressed macro-tile format. + * UBWC decoder block will use UV_Meta_Plane data together with + * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2 + * subsampled color difference samples. + * + * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable + * and randomly accessible. There is no dependency between tiles. + * + * <----- Y_Meta_Stride -----> + * <-------- Width ------> + * M M M M M M M M M M M M . . ^ ^ + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . Height | + * M M M M M M M M M M M M . . | Meta_Y_Scanlines + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . | | + * M M M M M M M M M M M M . . V | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . V + * <--Compressed tile Y Stride---> + * <------- Width -------> + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^ + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | | + * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * . . . . . . . . . . . . . . . . V + * <----- UV_Meta_Stride ----> + * M M M M M M M M M M M M . . ^ + * M M M M M M M M M M M M . . | + * M M M M M M M M M M M M . . | + * M M M M M M M M M M M M . . M_UV_Scanlines + * . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * <--Compressed tile UV Stride---> + * U* V* U* V* U* V* U* V* . . . . ^ + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . | + * U* V* U* V* U* V* U* V* . . . . UV_Scanlines + * . . . . . . . . . . . . . . . . | + * . . . . . . . . . . . . . . . . V + * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k + * + * + * Y_Stride = align(Width * 4/3, 128) + * UV_Stride = align(Width * 4/3, 128) + * Y_Scanlines = align(Height, 32) + * UV_Scanlines = align(Height/2, 16) + * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096) + * UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096) + * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64) + * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16) + * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096) + * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64) + * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16) + * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096) + * Extradata = 8k + */ + COLOR_FMT_NV12_BPP10_UBWC, +}; + +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 8K satisfies all current usecases. + */ + return 8 * 1024; +} + +static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width) +{ + unsigned int alignment, stride = 0; + if (!width) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: + case COLOR_FMT_NV12_MVTB: + case COLOR_FMT_NV12_UBWC: + alignment = 128; + stride = MSM_MEDIA_ALIGN(width, alignment); + break; + case COLOR_FMT_NV12_BPP10_UBWC: + alignment = 128; + stride = MSM_MEDIA_ALIGN(width * 4/3, alignment); + break; + default: + break; + } +invalid_input: + return stride; +} + +static inline unsigned int VENUS_UV_STRIDE(int color_fmt, int width) +{ + unsigned int alignment, stride = 0; + if (!width) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: + case COLOR_FMT_NV12_MVTB: + case COLOR_FMT_NV12_UBWC: + alignment = 128; + stride = MSM_MEDIA_ALIGN(width, alignment); + break; + case COLOR_FMT_NV12_BPP10_UBWC: + alignment = 128; + stride = MSM_MEDIA_ALIGN(width * 4/3, alignment); + break; + default: + break; + } +invalid_input: + return stride; +} + +static inline unsigned int VENUS_Y_SCANLINES(int color_fmt, int height) +{ + unsigned int alignment, sclines = 0; + if (!height) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: + case COLOR_FMT_NV12_MVTB: + case COLOR_FMT_NV12_UBWC: + case COLOR_FMT_NV12_BPP10_UBWC: + alignment = 32; + sclines = MSM_MEDIA_ALIGN(height, alignment); + break; + default: + break; + } +invalid_input: + return sclines; +} + +static inline unsigned int VENUS_UV_SCANLINES(int color_fmt, int height) +{ + unsigned int alignment, sclines = 0; + if (!height) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: + case COLOR_FMT_NV12_MVTB: + case COLOR_FMT_NV12_UBWC: + case COLOR_FMT_NV12_BPP10_UBWC: + alignment = 16; + sclines = MSM_MEDIA_ALIGN(((height + 1) >> 1), alignment); + break; + default: + break; + } +invalid_input: + return sclines; +} + +static inline unsigned int VENUS_Y_META_STRIDE(int color_fmt, int width) +{ + int y_tile_width = 0, y_meta_stride = 0; + + if (!width) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV12_UBWC: + y_tile_width = 32; + break; + case COLOR_FMT_NV12_BPP10_UBWC: + y_tile_width = 48; + break; + default: + goto invalid_input; + } + + y_meta_stride = MSM_MEDIA_ROUNDUP(width, y_tile_width); + y_meta_stride = MSM_MEDIA_ALIGN(y_meta_stride, 64); + +invalid_input: + return y_meta_stride; +} + +static inline unsigned int VENUS_Y_META_SCANLINES(int color_fmt, int height) +{ + int y_tile_height = 0, y_meta_scanlines = 0; + + if (!height) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV12_UBWC: + y_tile_height = 8; + break; + case COLOR_FMT_NV12_BPP10_UBWC: + y_tile_height = 4; + break; + default: + goto invalid_input; + } + + y_meta_scanlines = MSM_MEDIA_ROUNDUP(height, y_tile_height); + y_meta_scanlines = MSM_MEDIA_ALIGN(y_meta_scanlines, 16); + +invalid_input: + return y_meta_scanlines; +} + +static inline unsigned int VENUS_UV_META_STRIDE(int color_fmt, int width) +{ + int uv_tile_width = 0, uv_meta_stride = 0; + + if (!width) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV12_UBWC: + uv_tile_width = 16; + break; + case COLOR_FMT_NV12_BPP10_UBWC: + uv_tile_width = 24; + break; + default: + goto invalid_input; + } + + uv_meta_stride = MSM_MEDIA_ROUNDUP(width, uv_tile_width); + uv_meta_stride = MSM_MEDIA_ALIGN(uv_meta_stride, 64); + +invalid_input: + return uv_meta_stride; +} + +static inline unsigned int VENUS_UV_META_SCANLINES(int color_fmt, int height) +{ + int uv_tile_height = 0, uv_meta_scanlines = 0; + + if (!height) + goto invalid_input; + + switch (color_fmt) { + case COLOR_FMT_NV12_UBWC: + uv_tile_height = 8; + break; + case COLOR_FMT_NV12_BPP10_UBWC: + uv_tile_height = 4; + break; + default: + goto invalid_input; + } + + uv_meta_scanlines = MSM_MEDIA_ROUNDUP(height, uv_tile_height); + uv_meta_scanlines = MSM_MEDIA_ALIGN(uv_meta_scanlines, 16); + +invalid_input: + return uv_meta_scanlines; +} + +static inline unsigned int VENUS_BUFFER_SIZE( + int color_fmt, int width, int height) +{ + const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height); + unsigned int uv_alignment = 0, size = 0; + unsigned int y_plane, uv_plane, y_stride, + uv_stride, y_sclines, uv_sclines; + unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0; + unsigned int y_meta_stride = 0, y_meta_scanlines = 0; + unsigned int uv_meta_stride = 0, uv_meta_scanlines = 0; + unsigned int y_meta_plane = 0, uv_meta_plane = 0; + + if (!width || !height) + goto invalid_input; + + y_stride = VENUS_Y_STRIDE(color_fmt, width); + uv_stride = VENUS_UV_STRIDE(color_fmt, width); + y_sclines = VENUS_Y_SCANLINES(color_fmt, height); + uv_sclines = VENUS_UV_SCANLINES(color_fmt, height); + switch (color_fmt) { + case COLOR_FMT_NV21: + case COLOR_FMT_NV12: + uv_alignment = 4096; + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + uv_alignment; + size = y_plane + uv_plane + extra_size; + size = MSM_MEDIA_ALIGN(size, 4096); + break; + case COLOR_FMT_NV12_MVTB: + uv_alignment = 4096; + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + uv_alignment; + size = y_plane + uv_plane; + size = 2 * size + extra_size; + size = MSM_MEDIA_ALIGN(size, 4096); + break; + case COLOR_FMT_NV12_UBWC: + case COLOR_FMT_NV12_BPP10_UBWC: + y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096); + uv_ubwc_plane = MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096); + y_meta_stride = VENUS_Y_META_STRIDE(color_fmt, width); + y_meta_scanlines = VENUS_Y_META_SCANLINES(color_fmt, height); + y_meta_plane = MSM_MEDIA_ALIGN( + y_meta_stride * y_meta_scanlines, 4096); + uv_meta_stride = VENUS_UV_META_STRIDE(color_fmt, width); + uv_meta_scanlines = VENUS_UV_META_SCANLINES(color_fmt, height); + uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride * + uv_meta_scanlines, 4096); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + + uv_meta_plane + extra_size; + size = MSM_MEDIA_ALIGN(size, 4096); + break; + default: + break; + } +invalid_input: + return size; +} + +static inline unsigned int VENUS_VIEW2_OFFSET( + int color_fmt, int width, int height) +{ + unsigned int offset = 0; + unsigned int y_plane, uv_plane, y_stride, + uv_stride, y_sclines, uv_sclines; + if (!width || !height) + goto invalid_input; + + y_stride = VENUS_Y_STRIDE(color_fmt, width); + uv_stride = VENUS_UV_STRIDE(color_fmt, width); + y_sclines = VENUS_Y_SCANLINES(color_fmt, height); + uv_sclines = VENUS_UV_SCANLINES(color_fmt, height); + switch (color_fmt) { + case COLOR_FMT_NV12_MVTB: + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines; + offset = y_plane + uv_plane; + break; + default: + break; + } +invalid_input: + return offset; +} + +#endif diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h new file mode 100644 index 000000000000..40a39e693bb4 --- /dev/null +++ b/include/media/msm_vidc.h @@ -0,0 +1,134 @@ +/* Copyright (c) 2012-2014, 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 <linux/poll.h> +#include <linux/videodev2.h> +#include <linux/types.h> +#ifdef CONFIG_ION +#include <linux/msm_ion.h> +#endif +#include <uapi/media/msm_vidc.h> +#include <uapi/media/msm-v4l2-controls.h> + +#define HAL_BUFFER_MAX 0xb + +/*For backward compatibility*/ +#ifndef ion_phys_addr_t +#define ion_phys_addr_t dma_addr_t +#endif + +enum smem_type { + SMEM_ION, + SMEM_DMA, +}; + +enum smem_prop { +#ifdef CONFIG_ION + SMEM_CACHED = ION_FLAG_CACHED, + SMEM_SECURE = ION_FLAG_SECURE, +#else + SMEM_CACHED = BIT(0), + SMEM_SECURE = BIT(1), +#endif +}; + +/* NOTE: if you change this enum you MUST update the + * "buffer-type-tz-usage-table" for any affected target + * in arch/arm/boot/dts/<arch>.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, +}; + +struct dma_mapping_info { + struct device *dev; + struct dma_iommu_mapping *mapping; + struct sg_table *table; + struct dma_buf_attachment *attach; + struct dma_buf *buf; +}; + +struct msm_smem { + int mem_type; + size_t size; + void *kvaddr; + ion_phys_addr_t device_addr; + unsigned long flags; + void *smem_priv; + enum hal_buffer buffer_type; + struct dma_mapping_info mapping_info; +}; + +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_MAX_DEVICES, +}; +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_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_ctrl(void *instance, struct v4l2_control *a); +int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b); +int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b); +int msm_vidc_release_buffers(void *instance, int buffer_type); +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_streamoff(void *instance, enum v4l2_buf_type i); +int msm_vidc_decoder_cmd(void *instance, struct v4l2_decoder_cmd *dec); +int msm_vidc_encoder_cmd(void *instance, struct v4l2_encoder_cmd *enc); +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_s_parm(void *instance, struct v4l2_streamparm *a); +int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize); +int msm_vidc_mmap(void* instance, struct vm_area_struct *vma); +int msm_vidc_querybuf(void *instance, struct v4l2_buffer *b); + +#endif diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index bd2cec2d6c3d..c91aefa9cab7 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -18,6 +18,8 @@ #include <linux/videodev2.h> #include <linux/dma-buf.h> +#define VB2_MAX_FRAME 64 + struct vb2_alloc_ctx; struct vb2_fileio_data; struct vb2_threadio_data; @@ -411,7 +413,7 @@ struct vb2_queue { /* private: internal use only */ struct mutex mmap_lock; enum v4l2_memory memory; - struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; + struct vb2_buffer *bufs[VB2_MAX_FRAME]; unsigned int num_buffers; struct list_head queued_list; diff --git a/include/soc/qcom/ocmem.h b/include/soc/qcom/ocmem.h new file mode 100644 index 000000000000..2252e77d2490 --- /dev/null +++ b/include/soc/qcom/ocmem.h @@ -0,0 +1,266 @@ +/* Copyright (c) 2012,2014, 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 _ARCH_ARM_MACH_MSM_OCMEM_H +#define _ARCH_ARM_MACH_MSM_OCMEM_H + +#include <asm/page.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/err.h> + +#define OCMEM_MIN_ALLOC SZ_64K +#define OCMEM_MIN_ALIGN SZ_64K + +/* Maximum number of slots in DM */ +#define OCMEM_MAX_CHUNKS 32 +#define MIN_CHUNK_SIZE SZ_4K + +struct ocmem_notifier; + +struct ocmem_buf { + unsigned long addr; + unsigned long len; +}; + +struct ocmem_buf_attr { + unsigned long paddr; + unsigned long len; +}; + +struct ocmem_chunk { + bool ro; + unsigned long ddr_paddr; + unsigned long size; +}; + +struct ocmem_map_list { + unsigned num_chunks; + struct ocmem_chunk chunks[OCMEM_MAX_CHUNKS]; +}; + +enum ocmem_power_state { + OCMEM_OFF = 0x0, + OCMEM_RETENTION, + OCMEM_ON, + OCMEM_MAX = OCMEM_ON, +}; + +struct ocmem_resource { + unsigned resource_id; + unsigned num_keys; + unsigned int *keys; +}; + +struct ocmem_vectors { + unsigned num_resources; + struct ocmem_resource *r; +}; + +/* List of clients that allocate/interact with OCMEM */ +/* Must be in sync with client_names */ +enum ocmem_client { + /* GMEM clients */ + OCMEM_GRAPHICS = 0x0, + /* TCMEM clients */ + OCMEM_VIDEO, + OCMEM_CAMERA, + /* Dummy Clients */ + OCMEM_HP_AUDIO, + OCMEM_VOICE, + /* IMEM Clients */ + OCMEM_LP_AUDIO, + OCMEM_SENSORS, + OCMEM_OTHER_OS, + OCMEM_CLIENT_MAX, +}; + +/** + * List of OCMEM notification events which will be broadcasted + * to clients that optionally register for these notifications + * on a per allocation basis. + **/ +enum ocmem_notif_type { + OCMEM_MAP_DONE = 1, + OCMEM_MAP_FAIL, + OCMEM_UNMAP_DONE, + OCMEM_UNMAP_FAIL, + OCMEM_ALLOC_GROW, + OCMEM_ALLOC_SHRINK, + OCMEM_NOTIF_TYPE_COUNT, +}; + +/* APIS */ +#ifdef CONFIG_MSM_OCMEM +/* Notification APIs */ +struct ocmem_notifier *ocmem_notifier_register(int client_id, + struct notifier_block *nb); + +int ocmem_notifier_unregister(struct ocmem_notifier *notif_hndl, + struct notifier_block *nb); + +/* Obtain the maximum quota for the client */ +unsigned long get_max_quota(int client_id); + +/* Allocation APIs */ +struct ocmem_buf *ocmem_allocate(int client_id, unsigned long size); + +struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size); + +struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size); + +struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min, + unsigned long goal, unsigned long step); + +/* Free APIs */ +int ocmem_free(int client_id, struct ocmem_buf *buf); + +/* Dynamic Resize APIs */ +int ocmem_shrink(int client_id, struct ocmem_buf *buf, + unsigned long new_size); + +/* Transfer APIs */ +int ocmem_map(int client_id, struct ocmem_buf *buffer, + struct ocmem_map_list *list); + + +int ocmem_unmap(int client_id, struct ocmem_buf *buffer, + struct ocmem_map_list *list); + +int ocmem_drop(int client_id, struct ocmem_buf *buffer, + struct ocmem_map_list *list); + +int ocmem_dump(int client_id, struct ocmem_buf *buffer, + unsigned long dst_phys_addr); + +/* Priority Enforcement APIs */ +int ocmem_evict(int client_id); + +int ocmem_restore(int client_id); + +/* Power Control APIs */ +int ocmem_set_power_state(int client_id, struct ocmem_buf *buf, + enum ocmem_power_state new_state); + +enum ocmem_power_state ocmem_get_power_state(int client_id, + struct ocmem_buf *buf); + +struct ocmem_vectors *ocmem_get_vectors(int client_id, + struct ocmem_buf *buf); + +#else +/* Notification APIs */ +static inline struct ocmem_notifier *ocmem_notifier_register + (int client_id, struct notifier_block *nb) +{ + return ERR_PTR(-ENODEV); +} + +static inline int ocmem_notifier_unregister(struct ocmem_notifier *notif_hndl, + struct notifier_block *nb) +{ + return -ENODEV; +} + +/* Obtain the maximum quota for the client */ +static inline unsigned long get_max_quota(int client_id) +{ + return 0; +} + +/* Allocation APIs */ +static inline struct ocmem_buf *ocmem_allocate(int client_id, + unsigned long size) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ocmem_buf *ocmem_allocate_nowait(int client_id, + unsigned long size) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ocmem_buf *ocmem_allocate_nb(int client_id, + unsigned long size) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ocmem_buf *ocmem_allocate_range(int client_id, + unsigned long min, unsigned long goal, unsigned long step) +{ + return ERR_PTR(-ENODEV); +} + +/* Free APIs */ +static inline int ocmem_free(int client_id, struct ocmem_buf *buf) +{ + return -ENODEV; +} + +/* Dynamic Resize APIs */ +static inline int ocmem_shrink(int client_id, struct ocmem_buf *buf, + unsigned long new_size) +{ + return -ENODEV; +} + +/* Transfer APIs */ +static inline int ocmem_map(int client_id, struct ocmem_buf *buffer, + struct ocmem_map_list *list) +{ + return -ENODEV; +} + +static inline int ocmem_unmap(int client_id, struct ocmem_buf *buffer, + struct ocmem_map_list *list) +{ + return -ENODEV; +} + +static inline int ocmem_dump(int client_id, struct ocmem_buf *buffer, + unsigned long dst_phys_addr) +{ + return -ENODEV; +} + +/* Priority Enforcement APIs */ +static inline int ocmem_evict(int client_id) +{ + return -ENODEV; +} + +static inline int ocmem_restore(int client_id) +{ + return -ENODEV; +} + +/* Power Control APIs */ +static inline int ocmem_set_power_state(int client_id, + struct ocmem_buf *buf, enum ocmem_power_state new_state) +{ + return -ENODEV; +} + +static inline enum ocmem_power_state ocmem_get_power_state(int client_id, + struct ocmem_buf *buf) +{ + return -ENODEV; +} +static inline struct ocmem_vectors *ocmem_get_vectors(int client_id, + struct ocmem_buf *buf) +{ + return ERR_PTR(-ENODEV); +} +#endif +#endif diff --git a/include/trace/events/msm_vidc.h b/include/trace/events/msm_vidc.h new file mode 100644 index 000000000000..74283aebb2db --- /dev/null +++ b/include/trace/events/msm_vidc.h @@ -0,0 +1,315 @@ +/* Copyright (c) 2014, 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 <linux/types.h> +#include <linux/tracepoint.h> + +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( + __field(char *, 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( + __entry->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", + __entry->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_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( + __field(char *, 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( + __entry->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", + __entry->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) +); + +#endif + +#include <trace/define_trace.h> diff --git a/include/uapi/Kbuild b/include/uapi/Kbuild index 245aa6e05e6a..aeb3366db4fc 100644 --- a/include/uapi/Kbuild +++ b/include/uapi/Kbuild @@ -13,3 +13,4 @@ header-y += drm/ header-y += xen/ header-y += scsi/ header-y += misc/ +header-y += media/ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 9f6e108ff4a0..5c6348918483 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -365,6 +365,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) @@ -376,6 +377,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) @@ -421,6 +423,7 @@ enum v4l2_mpeg_video_h264_level { V4L2_MPEG_VIDEO_H264_LEVEL_4_2 = 13, V4L2_MPEG_VIDEO_H264_LEVEL_5_0 = 14, V4L2_MPEG_VIDEO_H264_LEVEL_5_1 = 15, + V4L2_MPEG_VIDEO_H264_LEVEL_5_2 = 16, }; #define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_MPEG_BASE+360) #define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_MPEG_BASE+361) @@ -449,6 +452,7 @@ enum v4l2_mpeg_video_h264_profile { V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA = 14, V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH = 15, V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH = 16, + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH = 17, }; #define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_MPEG_BASE+364) #define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_MPEG_BASE+365) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index fbdc3602ee27..47b7289896c0 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -442,6 +442,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */ #define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */ +/* UBWC 8-bit Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV12_UBWC v4l2_fourcc('Q', '1', '2', '8') +/* UBWC 10-bit Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV12_TP10_UBWC v4l2_fourcc('Q', '1', '2', 'A') + /* two non contiguous planes - one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ #define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */ @@ -500,6 +505,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */ #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_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') /* for HEVC stream */ +#define V4L2_PIX_FMT_HEVC_HYBRID v4l2_fourcc('H', 'V', 'C', 'H') /* Vendor-specific formats */ #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild new file mode 100644 index 000000000000..684a730658b5 --- /dev/null +++ b/include/uapi/media/Kbuild @@ -0,0 +1,2 @@ +header-y += msm_vidc.h +header-y += msm-v4l2-controls.h diff --git a/include/uapi/media/msm-v4l2-controls.h b/include/uapi/media/msm-v4l2-controls.h new file mode 100644 index 000000000000..0b5639faa4c2 --- /dev/null +++ b/include/uapi/media/msm-v4l2-controls.h @@ -0,0 +1,455 @@ +#ifndef __MSM_V4L2_CONTROLS_H__ +#define __MSM_V4L2_CONTROLS_H__ + +#include <linux/v4l2-controls.h> + +/* MPEG-class control IDs specific to the msm_vidc driver */ +#define V4L2_CID_MPEG_MSM_VIDC_BASE (V4L2_CTRL_CLASS_MPEG | 0x2000) + +#define V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_PICTURE_TYPE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+0) +#define V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+1) +#define V4L2_CID_MPEG_VIDC_VIDEO_POST_LOOP_DEBLOCKER_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+2) +#define V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+3) +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_VIDEO_MB_ERROR_MAP_REPORTING \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+4) +#define V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER \ + (V4L2_CID_MPEG_MSM_VIDC_BASE+5) + +#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT (V4L2_CID_MPEG_MSM_VIDC_BASE+6) +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_OUTPUT_ORDER (V4L2_CID_MPEG_MSM_VIDC_BASE+7) +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_FRAME_RATE (V4L2_CID_MPEG_MSM_VIDC_BASE+8) +#define V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD (V4L2_CID_MPEG_MSM_VIDC_BASE+9) +#define V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+10) +#define V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+11) +#define V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME (V4L2_CID_MPEG_MSM_VIDC_BASE+12) + +#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL (V4L2_CID_MPEG_MSM_VIDC_BASE+13) +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, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_ROTATION (V4L2_CID_MPEG_MSM_VIDC_BASE+14) +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 MSM_VIDC_BASE V4L2_CID_MPEG_MSM_VIDC_BASE +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL (MSM_VIDC_BASE+15) +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 (MSM_VIDC_BASE+16) +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_ADAPTIVE = 2, + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC_ADAPTIVE = 3, + V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM = 4, +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_AIR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE+17) +#define V4L2_CID_MPEG_VIDC_VIDEO_AIR_REF (V4L2_CID_MPEG_MSM_VIDC_BASE+18) +#define V4L2_CID_MPEG_VIDC_VIDEO_CIR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE+19) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H263_PROFILE (V4L2_CID_MPEG_MSM_VIDC_BASE+20) +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_H263_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE+21) +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_H264_AU_DELIMITER \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 22) +enum v4l2_mpeg_vidc_video_h264_au_delimiter { + V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED = 0, + V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED = 1 +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 23) +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+24) +#define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 25) +enum v4l2_mpeg_vidc_extradata { + V4L2_MPEG_VIDC_EXTRADATA_NONE = 0, + V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION = 1, + V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO = 2, + V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP = 3, + V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP = 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, + 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, +}; + +#define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26) +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_VIDEO_MULTI_SLICE_GOB \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 27) + +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 28) + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_TIMING_INFO \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 29) +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 +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_ALLOC_MODE_INPUT \ + (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_FRAME_ASSEMBLY \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 32) +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_VP8_PROFILE_LEVEL \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 33) +enum v4l2_mpeg_vidc_video_vp8_profile_level { + V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED, + V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0, + V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1, + V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2, + V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 34) +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_PRESERVE_TEXT_QUALITY \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 35) +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_DEINTERLACE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 36) +enum v4l2_mpeg_vidc_video_deinterlace { + V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED = 0, + V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_ENABLED = 1 +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_MPEG4_TIME_RESOLUTION \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 37) + +#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 38) +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, +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_SCS_THRESHOLD \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 39) + +#define V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE+40) +enum v4l2_mpeg_vidc_video_mpeg2_level { + V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0 = 0, + V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1 = 1, + V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2 = 2, + V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3 = 3, +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE (V4L2_CID_MPEG_MSM_VIDC_BASE+41) +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_422 = 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_HIGH = 5, +}; +#define V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_SEQ_HEADER \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 42) + +#define V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 43) +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_VP8_MIN_QP (V4L2_CID_MPEG_MSM_VIDC_BASE + 44) +#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP (V4L2_CID_MPEG_MSM_VIDC_BASE + 45) +#define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 46) + +#define V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 47) + +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 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 48) + +#define V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 49) + +#define V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 50) + +#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 51) + +#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_TIMESTAMP_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 52) +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 + 53) +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_I_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 54) + +#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 55) + +#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 56) + +#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 57) + +#define V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 58) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 59) + +#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 60) + +#define V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 61) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 62) + +#define V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 63) + +#define V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 64) + +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 + 65) +enum v4l2_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 + 66) +enum v4l2_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_CID_MPEG_VIDC_VIDEO_H264_NAL_SVC \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 67) + +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, +}; + +#define V4L2_CID_MPEG_VIDC_VIDEO_PERF_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 68) + +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_HIER_B_NUM_LAYERS \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 69) + +#define V4L2_CID_MPEG_VIDC_VIDEO_SECURE_SCALING_THRESHOLD \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 70) + +#define V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2 \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 71) + +#define V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 72) + +/* Vendor extensions */ +#define V4L2_QCOM_BUF_FLAG_CODECCONFIG 0x10000 +#define V4L2_QCOM_BUF_FLAG_EOSEQ 0x20000 +#define V4L2_QCOM_BUF_TIMESTAMP_INVALID 0x40000 +#define V4L2_QCOM_BUF_FLAG_IDRFRAME 0x80000 /*Image is a IDR-frame*/ +#define V4L2_QCOM_BUF_FLAG_DECODEONLY 0x100000 +#define V4L2_QCOM_BUF_DATA_CORRUPT 0x200000 +#define V4L2_QCOM_BUF_DROP_FRAME 0x400000 +#define V4L2_QCOM_BUF_INPUT_UNSUPPORTED 0x800000 +#define V4L2_QCOM_BUF_FLAG_EOS 0x1000000 +#define V4L2_QCOM_BUF_TS_DISCONTINUITY 0x2000000 +#define V4L2_QCOM_BUF_TS_ERROR 0x4000000 +#define V4L2_QCOM_BUF_FLAG_READONLY 0x8000000 +#define V4L2_MSM_VIDC_BUF_START_CODE_NOT_FOUND 0x10000000 +#define V4L2_MSM_BUF_FLAG_YUV_601_709_CLAMP 0x20000000 +#define V4L2_MSM_BUF_FLAG_MBAFF 0x40000000 + +/* Capabilities */ +#define V4L2_CAP_QCOM_FRAMESKIP 0x2000 /* frame skipping is supported */ + +struct v4l2_qcom_frameskip { + __u64 maxframeinterval; + __u8 fpsvariance; +}; + +/* Encoder commands */ +#define V4L2_ENC_QCOM_CMD_FLUSH (4) + +/* Decoder commands */ +#define V4L2_DEC_QCOM_CMD_FLUSH (4) + +/* Flags for V4L2_DEC_QCOM_CMD_FLUSH */ +#define V4L2_DEC_QCOM_CMD_FLUSH_OUTPUT (1 << 0) +#define V4L2_DEC_QCOM_CMD_FLUSH_CAPTURE (1 << 1) + +#define V4L2_QCOM_CMD_FLUSH_OUTPUT (1 << 0) +#define V4L2_QCOM_CMD_FLUSH_CAPTURE (1 << 1) + +/* Events */ +#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 \ + (V4L2_EVENT_MSM_VIDC_START + 2) +#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT \ + (V4L2_EVENT_MSM_VIDC_START + 3) +#define V4L2_EVENT_MSM_VIDC_CLOSE_DONE (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) +#define V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER \ + (V4L2_EVENT_MSM_VIDC_START + 7) +#define V4L2_EVENT_MSM_VIDC_HW_OVERLOAD (V4L2_EVENT_MSM_VIDC_START + 8) +#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9) +#define V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED (V4L2_EVENT_MSM_VIDC_START + 10) + +#endif/* __MSM_V4L2_CONTROLS_H__ */ diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h new file mode 100644 index 000000000000..6fa53ad7997b --- /dev/null +++ b/include/uapi/media/msm_vidc.h @@ -0,0 +1,154 @@ +#ifndef __MSM_VIDC_H__ +#define __MSM_VIDC_H__ + +#include <linux/types.h> + +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; +}; +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_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_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; +}; +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; +}; + +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_INPUT_CROP = 0x0700000E, + 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, +}; +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, +}; +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, +}; +#endif |