summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-04-28 09:24:36 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-04-28 09:24:36 -0700
commit3aa139aa9fdc138a84243dc49dc18d9b40e1c6e4 (patch)
tree5f533d8f1c48f0e8bc02b4f8079ed9ffdc14ebc1 /drivers/media/platform
parentacd3d28594536e9096c1ea76c5867d8a68babef6 (diff)
parent0b276e470a4d43e1365d3eb53c608a3d208cabd4 (diff)
Merge tag 'media/v5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - addition of a maintainer's profile for the media subsystem - addition of i.MX8 IP support - qcom/camss gained support for hardware version Titan 170 - new RC keymaps - Lots of other improvements, cleanups and bug fixes * tag 'media/v5.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (488 commits) media: coda: fix macroblocks count control usage media: rkisp1: params: fix wrong bits settings media: cedrus: Fix H265 status definitions media: meson-ge2d: fix rotation parameters media: v4l2-ctrls: fix reference to freed memory media: venus : hfi: add venus image info into smem media: venus: Fix internal buffer size calculations for v6. media: venus: helpers: keep max bandwidth when mbps exceeds the supported range media: venus: fix hw overload error log condition media: venus: core: correct firmware name for sm8250 media: venus: core,pm: fix potential infinite loop media: venus: core: Fix kerneldoc warnings media: gscpa/stv06xx: fix memory leak media: cx25821: remove unused including <linux/version.h> media: staging: media/meson: remove redundant dev_err call media: adv7842: support 1 block EDIDs, fix clearing EDID media: adv7842: configure all pads media: allegro: change kernel-doc comment blocks to normal comments media: camss: ispif: Remove redundant dev_err call in msm_ispif_subdev_init() media: i2c: rdamc21: Fix warning on u8 cast ...
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig21
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/allegro-dvt/nal-h264.h10
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.h6
-rw-r--r--drivers/media/platform/aspeed-video.c9
-rw-r--r--drivers/media/platform/coda/coda-common.c6
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.h21
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.h12
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.h21
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h17
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.h1
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c6
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h7
-rw-r--r--drivers/media/platform/fsl-viu.c6
-rw-r--r--drivers/media/platform/imx-jpeg/Kconfig11
-rw-r--r--drivers/media/platform/imx-jpeg/Makefile3
-rw-r--r--drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c168
-rw-r--r--drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h140
-rw-r--r--drivers/media/platform/imx-jpeg/mxc-jpeg.c2126
-rw-r--r--drivers/media/platform/imx-jpeg/mxc-jpeg.h180
-rw-r--r--drivers/media/platform/imx-pxp.c9
-rw-r--r--drivers/media/platform/meson/ge2d/ge2d.c4
-rw-r--r--drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h14
-rw-r--r--drivers/media/platform/mtk-mdp/mtk_mdp_comp.h1
-rw-r--r--drivers/media/platform/mtk-mdp/mtk_mdp_core.h10
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h21
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c18
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c108
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c40
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c4
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec_drv_if.h8
-rw-r--r--drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c4
-rw-r--r--drivers/media/platform/mtk-vcodec/venc_ipi_msg.h14
-rw-r--r--drivers/media/platform/mtk-vpu/mtk_vpu.c4
-rw-r--r--drivers/media/platform/mtk-vpu/mtk_vpu.h18
-rw-r--r--drivers/media/platform/omap3isp/isp.c16
-rw-r--r--drivers/media/platform/pxa_camera.c4
-rw-r--r--drivers/media/platform/qcom/camss/Makefile6
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-170.c599
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-1.c328
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-7.c404
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen1.h27
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.h39
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c661
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h150
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c22
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c197
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c104
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h2
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c127
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.h3
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-170.c786
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c144
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c277
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-8.c1195
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.c742
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.h117
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c847
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h128
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c52
-rw-r--r--drivers/media/platform/qcom/camss/camss.c435
-rw-r--r--drivers/media/platform/qcom/camss/camss.h19
-rw-r--r--drivers/media/platform/qcom/venus/core.c116
-rw-r--r--drivers/media/platform/qcom/venus/core.h52
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c42
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c71
-rw-r--r--drivers/media/platform/qcom/venus/helpers.h3
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c59
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h39
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c20
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c12
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c32
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v6.c138
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c167
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus_io.h118
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c143
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.h7
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c36
-rw-r--r--drivers/media/platform/qcom/venus/vdec_ctrls.c23
-rw-r--r--drivers/media/platform/qcom/venus/venc.c54
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c70
-rw-r--r--drivers/media/platform/rcar-vin/rcar-vin.h19
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c445
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c9
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.h16
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.h18
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h51
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c16
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-debug.c2
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-filter.h4
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c2
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h3
-rw-r--r--drivers/media/platform/sti/hva/hva-h264.c10
-rw-r--r--drivers/media/platform/sti/hva/hva.h2
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c4
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/sun8i-di.c2
-rw-r--r--drivers/media/platform/ti-vpe/cal-camerarx.c1
-rw-r--r--drivers/media/platform/ti-vpe/cal-video.c332
-rw-r--r--drivers/media/platform/ti-vpe/cal.c16
-rw-r--r--drivers/media/platform/ti-vpe/cal.h21
-rw-r--r--drivers/media/platform/ti-vpe/csc.c4
-rw-r--r--drivers/media/platform/ti-vpe/sc.c4
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.c3
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.c2
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.h4
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c1
107 files changed, 9927 insertions, 2748 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index fd1831e97b22..157c924686e4 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -186,6 +186,20 @@ config VIDEO_TI_CAL
In TI Technical Reference Manual this module is referred as
Camera Interface Subsystem (CAMSS).
+if VIDEO_TI_CAL
+
+config VIDEO_TI_CAL_MC
+ bool "Media Controller centric mode by default"
+ default n
+ help
+ Enables Media Controller centric mode by default.
+
+ If set, CAL driver will start in Media Controller mode by
+ default. Note that this behavior can be overridden via
+ module parameter 'mc_api'.
+
+endif # VIDEO_TI_CAL
+
endif # V4L_PLATFORM_DRIVERS
menuconfig V4L_MEM2MEM_DRIVERS
@@ -239,11 +253,14 @@ config VIDEO_IMX_PXP
The i.MX Pixel Pipeline is a memory-to-memory engine for scaling,
color space conversion, and rotation.
+source "drivers/media/platform/imx-jpeg/Kconfig"
+
config VIDEO_MEDIATEK_JPEG
tristate "Mediatek JPEG Codec driver"
depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -271,6 +288,7 @@ config VIDEO_MEDIATEK_MDP
depends on MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select VIDEO_MEDIATEK_VPU
@@ -291,6 +309,7 @@ config VIDEO_MEDIATEK_VCODEC
# our dependencies, to avoid missing symbols during link.
depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU
depends on MTK_SCP || !MTK_SCP
+ depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU
@@ -543,7 +562,7 @@ config VIDEO_TI_VPE_DEBUG
config VIDEO_QCOM_VENUS
tristate "Qualcomm Venus V4L2 encoder/decoder driver"
- depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_DEV && VIDEO_V4L2 && QCOM_SMEM
depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
select QCOM_MDT_LOADER if ARCH_QCOM
select QCOM_SCM if ARCH_QCOM
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 9d4d6370908d..eedc14aafb32 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
obj-$(CONFIG_VIDEO_CODA) += coda/
obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
+obj-$(CONFIG_VIDEO_IMX8_JPEG) += imx-jpeg/
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
diff --git a/drivers/media/platform/allegro-dvt/nal-h264.h b/drivers/media/platform/allegro-dvt/nal-h264.h
index 2ba7cbced7a5..a19634fe8c0b 100644
--- a/drivers/media/platform/allegro-dvt/nal-h264.h
+++ b/drivers/media/platform/allegro-dvt/nal-h264.h
@@ -11,8 +11,8 @@
#include <linux/kernel.h>
#include <linux/types.h>
-/**
- * struct nal_h264_hdr_parameters - HDR parameters
+/*
+ * struct nal_h264_hrd_parameters - HRD parameters
*
* C struct representation of the sequence parameter set NAL unit as defined by
* Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax.
@@ -32,7 +32,7 @@ struct nal_h264_hrd_parameters {
unsigned int time_offset_length;
};
-/**
+/*
* struct nal_h264_vui_parameters - VUI parameters
*
* C struct representation of the VUI parameters as defined by Rec. ITU-T
@@ -87,7 +87,7 @@ struct nal_h264_vui_parameters {
};
};
-/**
+/*
* struct nal_h264_sps - Sequence parameter set
*
* C struct representation of the sequence parameter set NAL unit as defined by
@@ -142,7 +142,7 @@ struct nal_h264_sps {
struct nal_h264_vui_parameters vui;
};
-/**
+/*
* struct nal_h264_pps - Picture parameter set
*
* C struct representation of the picture parameter set NAL unit as defined by
diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h
index fc994d4242d8..c09bbe5446aa 100644
--- a/drivers/media/platform/allegro-dvt/nal-hevc.h
+++ b/drivers/media/platform/allegro-dvt/nal-hevc.h
@@ -54,7 +54,7 @@ struct nal_hevc_profile_tier_level {
unsigned int general_level_idc;
};
-/**
+/*
* struct nal_hevc_vps - Video parameter set
*
* C struct representation of the video parameter set NAL unit as defined by
@@ -129,7 +129,7 @@ struct nal_hevc_hrd_parameters {
};
};
-/**
+/*
* struct nal_hevc_vui_parameters - VUI parameters
*
* C struct representation of the VUI parameters as defined by Rec. ITU-T
@@ -192,7 +192,7 @@ struct nal_hevc_vui_parameters {
};
};
-/**
+/*
* struct nal_hevc_sps - Sequence parameter set
*
* C struct representation of the video parameter set NAL unit as defined by
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
index f2c4dadd6a0e..7bb6babdcade 100644
--- a/drivers/media/platform/aspeed-video.c
+++ b/drivers/media/platform/aspeed-video.c
@@ -514,8 +514,8 @@ static void aspeed_video_off(struct aspeed_video *video)
aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff);
/* Turn off the relevant clocks */
- clk_disable(video->vclk);
clk_disable(video->eclk);
+ clk_disable(video->vclk);
clear_bit(VIDEO_CLOCKS_ON, &video->flags);
}
@@ -526,8 +526,8 @@ static void aspeed_video_on(struct aspeed_video *video)
return;
/* Turn on the relevant clocks */
- clk_enable(video->eclk);
clk_enable(video->vclk);
+ clk_enable(video->eclk);
set_bit(VIDEO_CLOCKS_ON, &video->flags);
}
@@ -1719,8 +1719,11 @@ static int aspeed_video_probe(struct platform_device *pdev)
return rc;
rc = aspeed_video_setup_video(video);
- if (rc)
+ if (rc) {
+ clk_unprepare(video->vclk);
+ clk_unprepare(video->eclk);
return rc;
+ }
return 0;
}
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 995e95272e51..bd666c858fa1 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -2062,7 +2062,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG)
ctx->params.gop_size = 1;
ctx->gopcounter = ctx->params.gop_size - 1;
- v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0);
+ /* Only decoders have this control */
+ if (ctx->mb_err_cnt_ctrl)
+ v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0);
ret = ctx->ops->start_streaming(ctx);
if (ctx->inst_type == CODA_INST_DECODER) {
@@ -3317,7 +3319,7 @@ static struct platform_driver coda_driver = {
.remove = coda_remove,
.driver = {
.name = CODA_NAME,
- .of_match_table = of_match_ptr(coda_dt_ids),
+ .of_match_table = coda_dt_ids,
.pm = &coda_pm_ops,
},
};
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index 8e5a9acb78aa..e894e85e84a4 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -64,14 +64,11 @@ enum gsc_irq {
* enum gsc_datapath - the path of data used for G-Scaler
* @GSC_CAMERA: from camera
* @GSC_DMA: from/to DMA
- * @GSC_LOCAL: to local path
* @GSC_WRITEBACK: from FIMD
*/
enum gsc_datapath {
GSC_CAMERA = 0x1,
GSC_DMA,
- GSC_MIXER,
- GSC_FIMD,
GSC_WRITEBACK,
};
@@ -104,10 +101,11 @@ enum gsc_yuv_fmt {
* struct gsc_fmt - the driver's internal color format data
* @mbus_code: Media Bus pixel code, -1 if not applicable
* @pixelformat: the fourcc code for this format, 0 if not applicable
+ * @color: color encoding
* @yorder: Y/C order
* @corder: Chrominance order control
* @num_planes: number of physically non-contiguous data planes
- * @nr_comp: number of physically contiguous data planes
+ * @num_comp: number of physically contiguous data planes
* @depth: per plane driver's private 'number of bits per pixel'
* @flags: flags indicating which operation mode format applies to
*/
@@ -280,7 +278,7 @@ struct gsc_pix_align {
u16 target_h;
};
-/**
+/*
* struct gsc_variant - G-Scaler variant information
*/
struct gsc_variant {
@@ -301,6 +299,9 @@ struct gsc_variant {
*
* @variant: the variant information for this driver.
* @num_entities: the number of g-scalers
+ * @clk_names: clock names
+ * @num_clocks: the number of clocks in @clk_names
+ * @num_entities: the number of g-scalers
*/
struct gsc_driverdata {
struct gsc_variant *variant[GSC_MAX_DEVS];
@@ -316,12 +317,14 @@ struct gsc_driverdata {
* @pdev: pointer to the G-Scaler platform device
* @variant: the IP variant information
* @id: G-Scaler device index (0..GSC_MAX_DEVS)
+ * @num_clocks: number of clocks required for G-Scaler operation
* @clock: clocks required for G-Scaler operation
* @regs: the mapped hardware registers
* @irq_queue: interrupt handler waitqueue
* @m2m: memory-to-memory V4L2 device information
* @state: flags used to synchronize m2m and capture mode operation
* @vdev: video device for G-Scaler instance
+ * @v4l2_dev: v4l2_device for G-Scaler instance
*/
struct gsc_dev {
spinlock_t slock;
@@ -340,7 +343,7 @@ struct gsc_dev {
};
/**
- * gsc_ctx - the device context data
+ * struct gsc_ctx - the device context data
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @in_path: input mode (DMA or camera)
@@ -348,12 +351,16 @@ struct gsc_dev {
* @scaler: image scaler properties
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
+ * @rotation: rotation
+ * @hflip: horizontal flip
+ * @vflip: vertical flip
* @gsc_dev: the G-Scaler device this context applies to
* @m2m_ctx: memory-to-memory device context
* @fh: v4l2 file handle
* @ctrl_handler: v4l2 controls handler
- * @gsc_ctrls G-Scaler control set
+ * @gsc_ctrls: G-Scaler control set
* @ctrls_rdy: true if the control handler is initialized
+ * @out_colorspace: the colorspace of the OUTPUT queue
*/
struct gsc_ctx {
struct gsc_frame s_frame;
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index 58b72a052cef..7a058f3e6298 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -242,6 +242,7 @@ struct fimc_vid_buffer {
* @addr: image frame buffer DMA addresses
* @dma_offset: DMA offset in bytes
* @fmt: fimc color format pointer
+ * @alpha: alpha value
*/
struct fimc_frame {
u32 f_width;
@@ -296,6 +297,7 @@ struct fimc_m2m_device {
* @buf_index: index for managing the output DMA buffers
* @frame_count: the frame counter for statistics
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
+ * @streaming: is streaming in progress?
* @input: capture input type, grp_id of the attached subdev
* @user_subdev_api: true if subdevs are not configured by the host driver
*/
@@ -400,6 +402,7 @@ struct fimc_ctx;
* @pdata: pointer to the device platform data
* @sysreg: pointer to the SYSREG regmap
* @variant: the IP variant information
+ * @drv_data: driver data
* @id: FIMC device index (0..FIMC_MAX_DEVS)
* @clock: clocks required for FIMC operation
* @regs: the mapped hardware registers
@@ -408,7 +411,6 @@ struct fimc_ctx;
* @m2m: memory-to-memory V4L2 device information
* @vid_cap: camera capture device information
* @state: flags used to synchronize m2m and capture mode operation
- * @pipeline: fimc video capture pipeline data structure
*/
struct fimc_dev {
spinlock_t slock;
@@ -453,12 +455,12 @@ struct fimc_ctrls {
};
/**
- * fimc_ctx - the device context data
+ * struct fimc_ctx - the device context data
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @out_order_1p: output 1-plane YCBCR order
* @out_order_2p: output 2-plane YCBCR order
- * @in_order_1p input 1-plane YCBCR order
+ * @in_order_1p: input 1-plane YCBCR order
* @in_order_2p: input 2-plane YCBCR order
* @in_path: input mode (DMA or camera)
* @out_path: output mode (DMA or FIFO)
@@ -666,6 +668,7 @@ int fimc_capture_resume(struct fimc_dev *fimc);
/**
* fimc_active_queue_add - add buffer to the capture active buffers queue
+ * @vid_cap: camera capture device information
* @buf: buffer to add to the active buffers list
*/
static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
@@ -677,6 +680,7 @@ static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
/**
* fimc_active_queue_pop - pop buffer from the capture active buffers queue
+ * @vid_cap: camera capture device information
*
* The caller must assure the active_buf_q list is not empty.
*/
@@ -693,6 +697,7 @@ static inline struct fimc_vid_buffer *fimc_active_queue_pop(
/**
* fimc_pending_queue_add - add buffer to the capture pending buffers queue
+ * @vid_cap: camera capture device information
* @buf: buffer to add to the pending buffers list
*/
static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
@@ -703,6 +708,7 @@ static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
/**
* fimc_pending_queue_pop - pop buffer from the capture pending buffers queue
+ * @vid_cap: camera capture device information
*
* The caller must assure the pending_buf_q list is not empty.
*/
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
index ce30b007bc55..06586e455b1d 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -232,15 +232,33 @@ struct chain_config {
* struct fimc_is - fimc-is data structure
* @pdev: pointer to FIMC-IS platform device
* @pctrl: pointer to pinctrl structure for this device
- * @v4l2_dev: pointer to top the level v4l2_device
+ * @v4l2_dev: pointer to the top level v4l2_device
+ * @fw: data structure describing the FIMC-IS firmware binary
+ * @memory: memory region assigned for the FIMC-IS (firmware)
+ * @isp: the ISP block data structure
+ * @sensor: fimc-is sensor subdevice array
+ * @setfile: descriptor of the imaging pipeline calibration data
+ * @ctrl_handler: the v4l2 controls handler
* @lock: mutex serializing video device and the subdev operations
* @slock: spinlock protecting this data structure and the hw registers
* @clocks: FIMC-LITE gate clock
* @regs: MCUCTL mmapped registers region
* @pmu_regs: PMU ISP mmapped registers region
+ * @irq: FIMC-IS interrupt
* @irq_queue: interrupt handling waitqueue
* @lpm: low power mode flag
* @state: internal driver's state flags
+ * @sensor_index: image sensor index for the firmware
+ * @i2h_cmd: FIMC-IS to the host (CPU) mailbox command data structure
+ * @h2i_cmd: the host (CPU) to FIMC-IS mailbox command data structure
+ * @fd_header: the face detection result data structure
+ * @config: shared HW pipeline configuration data
+ * @config_index: index to the @config entry currently in use
+ * @is_p_region: pointer to the shared parameter memory region
+ * @is_dma_p_region: DMA address of the shared parameter memory region
+ * @is_shared_region: pointer to the IS shared region data structure
+ * @af: auto focus data
+ * @debugfs_entry: debugfs entry for the firmware log
*/
struct fimc_is {
struct platform_device *pdev;
@@ -249,7 +267,6 @@ struct fimc_is {
struct fimc_is_firmware fw;
struct fimc_is_memory memory;
- struct firmware *f_w;
struct fimc_isp isp;
struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM];
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h
index 161fa01a8781..12017cd924d9 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.h
+++ b/drivers/media/platform/exynos4-is/fimc-isp.h
@@ -110,16 +110,20 @@ struct isp_video_buf {
/**
* struct fimc_is_video - fimc-is video device structure
- * @vdev: video_device structure
+ * @ve: video_device structure and media pipeline
* @type: video device type (CAPTURE/OUTPUT)
* @pad: video device media (sink) pad
* @pending_buf_q: pending buffers queue head
* @active_buf_q: a queue head of buffers scheduled in hardware
* @vb_queue: vb2 buffer queue
- * @active_buf_count: number of video buffers scheduled in hardware
+ * @reqbufs_count: the number of buffers requested in REQBUFS ioctl
+ * @buf_count: number of video buffers scheduled in hardware
+ * @buf_mask: bitmask of the queued video buffer indices
* @frame_count: counter of frames dequeued to user space
- * @reqbufs_count: number of buffers requested with REQBUFS ioctl
- * @format: current pixel format
+ * @streaming: is streaming in progress?
+ * @buffers: buffer info
+ * @format: current fimc pixel format
+ * @pixfmt: current pixel format
*/
struct fimc_is_video {
struct exynos_video_entity ve;
@@ -147,9 +151,12 @@ struct fimc_is_video {
* @pdev: pointer to FIMC-IS platform device
* @subdev: ISP v4l2_subdev
* @subdev_pads: the ISP subdev media pads
+ * @src_fmt: source mediabus format
+ * @sink_fmt: sink mediabus format
* @test_pattern: test pattern controls
* @ctrls: v4l2 controls structure
- * @video_lock: mutex serializing video device and the subdev operations
+ * @video_lock: mutex serializing video device operations
+ * @subdev_lock: mutex serializing subdev operations
* @cac_margin_x: horizontal CAC margin in pixels
* @cac_margin_y: vertical CAC margin in pixels
* @state: driver state flags
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h
index e2d4d628b5aa..ddf29e0b5b1c 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.h
+++ b/drivers/media/platform/exynos4-is/fimc-lite.h
@@ -137,6 +137,8 @@ struct flite_buffer {
* @active_buf_count: number of video buffers scheduled in hardware
* @frame_count: the captured frames counter
* @reqbufs_count: the number of buffers requested with REQBUFS ioctl
+ * @events: event info
+ * @streaming: is streaming in progress?
*/
struct fimc_lite {
struct platform_device *pdev;
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.h b/drivers/media/platform/exynos4-is/fimc-reg.h
index d7a62465c14e..b9b33aa1f12f 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.h
+++ b/drivers/media/platform/exynos4-is/fimc-reg.h
@@ -324,6 +324,7 @@ void fimc_deactivate_capture(struct fimc_dev *fimc);
/**
* fimc_hw_set_dma_seq - configure output DMA buffer sequence
+ * @dev: fimc device
* @mask: bitmask for the DMA output buffer registers, set to 0 to skip buffer
* This function masks output DMA ring buffers, it allows to select which of
* the 32 available output buffer address registers will be used by the DMA
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 8e1e892085ec..13d192ba4aa6 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -488,8 +488,10 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
for_each_child_of_node(port, ep) {
ret = fimc_md_parse_one_endpoint(fmd, ep);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(ep);
return ret;
+ }
}
return 0;
@@ -806,7 +808,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
}
/**
- * __fimc_md_create_fimc_links - create links to all FIMC entities
+ * __fimc_md_create_fimc_sink_links - create links to all FIMC entities
* @fmd: fimc media device
* @source: the source entity to create links to all fimc entities from
* @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index a3876d668ea6..62ad5d7e035a 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -100,6 +100,8 @@ struct cam_clk {
* @sensor: array of registered sensor subdevs
* @num_sensors: actual number of registered sensors
* @camclk: external sensor clock information
+ * @wbclk: external writeback clock information
+ * @fimc_lite: array of registered fimc-lite devices
* @fimc: array of registered fimc devices
* @fimc_is: fimc-is data structure
* @use_isp: set to true when FIMC-IS subsystem is used
@@ -107,9 +109,12 @@ struct cam_clk {
* @media_dev: top level media device
* @v4l2_dev: top level v4l2_device holding up the subdevs
* @pdev: platform device this media device is hooked up into
- * @cam_clk_provider: CAMCLK clock provider structure
+ * @clk_provider: CAMCLK clock provider structure
+ * @subdev_notifier: notifier for the subdevs
* @user_subdev_api: true if subdevs are not configured by the host driver
* @slock: spinlock protecting @sensor array
+ * @pipelines: list of pipelines
+ * @link_setup_graph: graph iterator
*/
struct fimc_md {
struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 1f1042d5c865..a4bfa70b49b2 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -1529,7 +1529,7 @@ err_irq:
static int viu_of_remove(struct platform_device *op)
{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(op);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next,
struct v4l2_subdev, list);
@@ -1550,7 +1550,7 @@ static int viu_of_remove(struct platform_device *op)
#ifdef CONFIG_PM
static int viu_suspend(struct platform_device *op, pm_message_t state)
{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(op);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
clk_disable(dev->clk);
@@ -1559,7 +1559,7 @@ static int viu_suspend(struct platform_device *op, pm_message_t state)
static int viu_resume(struct platform_device *op)
{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(op);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
clk_enable(dev->clk);
diff --git a/drivers/media/platform/imx-jpeg/Kconfig b/drivers/media/platform/imx-jpeg/Kconfig
new file mode 100644
index 000000000000..2fdd648cda80
--- /dev/null
+++ b/drivers/media/platform/imx-jpeg/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+config VIDEO_IMX8_JPEG
+ tristate "IMX8 JPEG Encoder/Decoder"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on VIDEO_DEV && VIDEO_V4L2
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select V4L2_JPEG_HELPER
+ help
+ This is a video4linux2 driver for the i.MX8 QXP/QM integrated
+ JPEG encoder/decoder.
diff --git a/drivers/media/platform/imx-jpeg/Makefile b/drivers/media/platform/imx-jpeg/Makefile
new file mode 100644
index 000000000000..bf19c82e61b4
--- /dev/null
+++ b/drivers/media/platform/imx-jpeg/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+mxc-jpeg-encdec-objs := mxc-jpeg-hw.o mxc-jpeg.o
+obj-$(CONFIG_VIDEO_IMX8_JPEG) += mxc-jpeg-encdec.o
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c b/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c
new file mode 100644
index 000000000000..29c604b1b179
--- /dev/null
+++ b/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX8QXP/i.MX8QM JPEG encoder/decoder v4l2 driver
+ *
+ * Copyright 2018-2019 NXP
+ */
+
+#include <linux/delay.h>
+#include <media/videobuf2-core.h>
+#include "mxc-jpeg-hw.h"
+
+#define print_wrapper_reg(dev, base_address, reg_offset)\
+ internal_print_wrapper_reg(dev, (base_address), #reg_offset,\
+ (reg_offset))
+#define internal_print_wrapper_reg(dev, base_address, reg_name, reg_offset) {\
+ int val;\
+ val = readl((base_address) + (reg_offset));\
+ dev_dbg(dev, "Wrapper reg %s = 0x%x\n", reg_name, val);\
+}
+
+void print_descriptor_info(struct device *dev, struct mxc_jpeg_desc *desc)
+{
+ dev_dbg(dev, " MXC JPEG NEXT_DESCPT_PTR 0x%x\n",
+ desc->next_descpt_ptr);
+ dev_dbg(dev, " MXC JPEG BUF_BASE0 0x%x\n", desc->buf_base0);
+ dev_dbg(dev, " MXC JPEG BUF_BASE1 0x%x\n", desc->buf_base1);
+ dev_dbg(dev, " MXC JPEG LINE_PITCH %d\n", desc->line_pitch);
+ dev_dbg(dev, " MXC JPEG STM_BUFBASE 0x%x\n", desc->stm_bufbase);
+ dev_dbg(dev, " MXC JPEG STM_BUFSIZE %d\n", desc->stm_bufsize);
+ dev_dbg(dev, " MXC JPEG IMGSIZE %x (%d x %d)\n", desc->imgsize,
+ desc->imgsize >> 16, desc->imgsize & 0xFFFF);
+ dev_dbg(dev, " MXC JPEG STM_CTRL 0x%x\n", desc->stm_ctrl);
+}
+
+void print_cast_status(struct device *dev, void __iomem *reg,
+ unsigned int mode)
+{
+ dev_dbg(dev, "CAST IP status regs:\n");
+ print_wrapper_reg(dev, reg, CAST_STATUS0);
+ print_wrapper_reg(dev, reg, CAST_STATUS1);
+ print_wrapper_reg(dev, reg, CAST_STATUS2);
+ print_wrapper_reg(dev, reg, CAST_STATUS3);
+ print_wrapper_reg(dev, reg, CAST_STATUS4);
+ print_wrapper_reg(dev, reg, CAST_STATUS5);
+ print_wrapper_reg(dev, reg, CAST_STATUS6);
+ print_wrapper_reg(dev, reg, CAST_STATUS7);
+ print_wrapper_reg(dev, reg, CAST_STATUS8);
+ print_wrapper_reg(dev, reg, CAST_STATUS9);
+ print_wrapper_reg(dev, reg, CAST_STATUS10);
+ print_wrapper_reg(dev, reg, CAST_STATUS11);
+ print_wrapper_reg(dev, reg, CAST_STATUS12);
+ print_wrapper_reg(dev, reg, CAST_STATUS13);
+ if (mode == MXC_JPEG_DECODE)
+ return;
+ print_wrapper_reg(dev, reg, CAST_STATUS14);
+ print_wrapper_reg(dev, reg, CAST_STATUS15);
+ print_wrapper_reg(dev, reg, CAST_STATUS16);
+ print_wrapper_reg(dev, reg, CAST_STATUS17);
+ print_wrapper_reg(dev, reg, CAST_STATUS18);
+ print_wrapper_reg(dev, reg, CAST_STATUS19);
+}
+
+void print_wrapper_info(struct device *dev, void __iomem *reg)
+{
+ dev_dbg(dev, "Wrapper regs:\n");
+ print_wrapper_reg(dev, reg, GLB_CTRL);
+ print_wrapper_reg(dev, reg, COM_STATUS);
+ print_wrapper_reg(dev, reg, BUF_BASE0);
+ print_wrapper_reg(dev, reg, BUF_BASE1);
+ print_wrapper_reg(dev, reg, LINE_PITCH);
+ print_wrapper_reg(dev, reg, STM_BUFBASE);
+ print_wrapper_reg(dev, reg, STM_BUFSIZE);
+ print_wrapper_reg(dev, reg, IMGSIZE);
+ print_wrapper_reg(dev, reg, STM_CTRL);
+}
+
+void mxc_jpeg_enable_irq(void __iomem *reg, int slot)
+{
+ writel(0xFFFFFFFF, reg + MXC_SLOT_OFFSET(slot, SLOT_IRQ_EN));
+}
+
+void mxc_jpeg_sw_reset(void __iomem *reg)
+{
+ /*
+ * engine soft reset, internal state machine reset
+ * this will not reset registers, however, it seems
+ * the registers may remain inconsistent with the internal state
+ * so, on purpose, at least let GLB_CTRL bits clear after this reset
+ */
+ writel(GLB_CTRL_SFT_RST, reg + GLB_CTRL);
+}
+
+void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg)
+{
+ dev_dbg(dev, "CAST Encoder CONFIG...\n");
+ /*
+ * "Config_Mode" enabled, "Config_Mode auto clear enabled",
+ */
+ writel(0xa0, reg + CAST_MODE);
+
+ /* all markers and segments */
+ writel(0x3ff, reg + CAST_CFG_MODE);
+
+ /* quality factor */
+ writel(0x4b, reg + CAST_QUALITY);
+}
+
+void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg)
+{
+ dev_dbg(dev, "CAST Encoder GO...\n");
+ /*
+ * "GO" enabled, "GO bit auto clear" enabled
+ */
+ writel(0x140, reg + CAST_MODE);
+}
+
+void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg)
+{
+ dev_dbg(dev, "CAST Decoder GO...\n");
+ writel(MXC_DEC_EXIT_IDLE_MODE, reg + CAST_CTRL);
+}
+
+int mxc_jpeg_enable(void __iomem *reg)
+{
+ u32 regval;
+
+ writel(GLB_CTRL_JPG_EN, reg + GLB_CTRL);
+ regval = readl(reg);
+ return regval;
+}
+
+void mxc_jpeg_enable_slot(void __iomem *reg, int slot)
+{
+ u32 regval;
+
+ regval = readl(reg + GLB_CTRL);
+ writel(GLB_CTRL_SLOT_EN(slot) | regval, reg + GLB_CTRL);
+}
+
+void mxc_jpeg_set_l_endian(void __iomem *reg, int le)
+{
+ u32 regval;
+
+ regval = readl(reg + GLB_CTRL);
+ regval &= ~GLB_CTRL_L_ENDIAN(1); /* clear */
+ writel(GLB_CTRL_L_ENDIAN(le) | regval, reg + GLB_CTRL); /* set */
+}
+
+void mxc_jpeg_set_bufsize(struct mxc_jpeg_desc *desc, u32 bufsize)
+{
+ desc->stm_bufsize = bufsize;
+}
+
+void mxc_jpeg_set_res(struct mxc_jpeg_desc *desc, u16 w, u16 h)
+{
+ desc->imgsize = w << 16 | h;
+}
+
+void mxc_jpeg_set_line_pitch(struct mxc_jpeg_desc *desc, u32 line_pitch)
+{
+ desc->line_pitch = line_pitch;
+}
+
+void mxc_jpeg_set_desc(u32 desc, void __iomem *reg, int slot)
+{
+ writel(desc | MXC_NXT_DESCPT_EN,
+ reg + MXC_SLOT_OFFSET(slot, SLOT_NXT_DESCPT_PTR));
+}
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h
new file mode 100644
index 000000000000..ae70d3a0dc24
--- /dev/null
+++ b/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * i.MX8QXP/i.MX8QM JPEG encoder/decoder v4l2 driver
+ *
+ * Copyright 2018-2019 NXP
+ */
+
+#ifndef _MXC_JPEG_HW_H
+#define _MXC_JPEG_HW_H
+
+/* JPEG Decoder/Encoder Wrapper Register Map */
+#define GLB_CTRL 0x0
+#define COM_STATUS 0x4
+#define BUF_BASE0 0x14
+#define BUF_BASE1 0x18
+#define LINE_PITCH 0x1C
+#define STM_BUFBASE 0x20
+#define STM_BUFSIZE 0x24
+#define IMGSIZE 0x28
+#define STM_CTRL 0x2C
+
+/* CAST JPEG-Decoder/Encoder Status Register Map (read-only)*/
+#define CAST_STATUS0 0x100
+#define CAST_STATUS1 0x104
+#define CAST_STATUS2 0x108
+#define CAST_STATUS3 0x10c
+#define CAST_STATUS4 0x110
+#define CAST_STATUS5 0x114
+#define CAST_STATUS6 0x118
+#define CAST_STATUS7 0x11c
+#define CAST_STATUS8 0x120
+#define CAST_STATUS9 0x124
+#define CAST_STATUS10 0x128
+#define CAST_STATUS11 0x12c
+#define CAST_STATUS12 0x130
+#define CAST_STATUS13 0x134
+/* the following are for encoder only */
+#define CAST_STATUS14 0x138
+#define CAST_STATUS15 0x13c
+#define CAST_STATUS16 0x140
+#define CAST_STATUS17 0x144
+#define CAST_STATUS18 0x148
+#define CAST_STATUS19 0x14c
+
+/* CAST JPEG-Decoder Control Register Map (write-only) */
+#define CAST_CTRL CAST_STATUS13
+
+/* CAST JPEG-Encoder Control Register Map (write-only) */
+#define CAST_MODE CAST_STATUS0
+#define CAST_CFG_MODE CAST_STATUS1
+#define CAST_QUALITY CAST_STATUS2
+#define CAST_RSVD CAST_STATUS3
+#define CAST_REC_REGS_SEL CAST_STATUS4
+#define CAST_LUMTH CAST_STATUS5
+#define CAST_CHRTH CAST_STATUS6
+#define CAST_NOMFRSIZE_LO CAST_STATUS7
+#define CAST_NOMFRSIZE_HI CAST_STATUS8
+#define CAST_OFBSIZE_LO CAST_STATUS9
+#define CAST_OFBSIZE_HI CAST_STATUS10
+
+#define MXC_MAX_SLOTS 1 /* TODO use all 4 slots*/
+/* JPEG-Decoder Wrapper Slot Registers 0..3 */
+#define SLOT_BASE 0x10000
+#define SLOT_STATUS 0x0
+#define SLOT_IRQ_EN 0x4
+#define SLOT_BUF_PTR 0x8
+#define SLOT_CUR_DESCPT_PTR 0xC
+#define SLOT_NXT_DESCPT_PTR 0x10
+#define MXC_SLOT_OFFSET(slot, offset) ((SLOT_BASE * ((slot) + 1)) + (offset))
+
+/* GLB_CTRL fields */
+#define GLB_CTRL_JPG_EN 0x1
+#define GLB_CTRL_SFT_RST (0x1 << 1)
+#define GLB_CTRL_DEC_GO (0x1 << 2)
+#define GLB_CTRL_L_ENDIAN(le) ((le) << 3)
+#define GLB_CTRL_SLOT_EN(slot) (0x1 << ((slot) + 4))
+
+/* COM_STAUS fields */
+#define COM_STATUS_DEC_ONGOING(r) (((r) & (1 << 31)) >> 31)
+#define COM_STATUS_CUR_SLOT(r) (((r) & (0x3 << 29)) >> 29)
+
+/* STM_CTRL fields */
+#define STM_CTRL_PIXEL_PRECISION (0x1 << 2)
+#define STM_CTRL_IMAGE_FORMAT(img_fmt) ((img_fmt) << 3)
+#define STM_CTRL_IMAGE_FORMAT_MASK (0xF << 3)
+#define STM_CTRL_BITBUF_PTR_CLR(clr) ((clr) << 7)
+#define STM_CTRL_AUTO_START(go) ((go) << 8)
+#define STM_CTRL_CONFIG_MOD(mod) ((mod) << 9)
+
+/* SLOT_STATUS fields for slots 0..3 */
+#define SLOT_STATUS_FRMDONE (0x1 << 3)
+#define SLOT_STATUS_ENC_CONFIG_ERR (0x1 << 8)
+
+/* SLOT_IRQ_EN fields TBD */
+
+#define MXC_NXT_DESCPT_EN 0x1
+#define MXC_DEC_EXIT_IDLE_MODE 0x4
+
+/* JPEG-Decoder Wrapper - STM_CTRL Register Fields */
+#define MXC_PIXEL_PRECISION(precision) ((precision) / 8 << 2)
+enum mxc_jpeg_image_format {
+ MXC_JPEG_INVALID = -1,
+ MXC_JPEG_YUV420 = 0x0, /* 2 Plannar, Y=1st plane UV=2nd plane */
+ MXC_JPEG_YUV422 = 0x1, /* 1 Plannar, YUYV sequence */
+ MXC_JPEG_RGB = 0x2, /* RGBRGB packed format */
+ MXC_JPEG_YUV444 = 0x3, /* 1 Plannar, YUVYUV sequence */
+ MXC_JPEG_GRAY = 0x4, /* Y8 or Y12 or Single Component */
+ MXC_JPEG_RESERVED = 0x5,
+ MXC_JPEG_ARGB = 0x6,
+};
+
+#include "mxc-jpeg.h"
+void print_descriptor_info(struct device *dev, struct mxc_jpeg_desc *desc);
+void print_cast_status(struct device *dev, void __iomem *reg,
+ unsigned int mode);
+void print_wrapper_info(struct device *dev, void __iomem *reg);
+void mxc_jpeg_sw_reset(void __iomem *reg);
+int mxc_jpeg_enable(void __iomem *reg);
+void wait_frmdone(struct device *dev, void __iomem *reg);
+void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg);
+void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg);
+void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg);
+int mxc_jpeg_get_slot(void __iomem *reg);
+u32 mxc_jpeg_get_offset(void __iomem *reg, int slot);
+void mxc_jpeg_enable_slot(void __iomem *reg, int slot);
+void mxc_jpeg_set_l_endian(void __iomem *reg, int le);
+void mxc_jpeg_enable_irq(void __iomem *reg, int slot);
+int mxc_jpeg_set_input(void __iomem *reg, u32 in_buf, u32 bufsize);
+int mxc_jpeg_set_output(void __iomem *reg, u16 out_pitch, u32 out_buf,
+ u16 w, u16 h);
+void mxc_jpeg_set_config_mode(void __iomem *reg, int config_mode);
+int mxc_jpeg_set_params(struct mxc_jpeg_desc *desc, u32 bufsize, u16
+ out_pitch, u32 format);
+void mxc_jpeg_set_bufsize(struct mxc_jpeg_desc *desc, u32 bufsize);
+void mxc_jpeg_set_res(struct mxc_jpeg_desc *desc, u16 w, u16 h);
+void mxc_jpeg_set_line_pitch(struct mxc_jpeg_desc *desc, u32 line_pitch);
+void mxc_jpeg_set_desc(u32 desc, void __iomem *reg, int slot);
+void mxc_jpeg_set_regs_from_desc(struct mxc_jpeg_desc *desc,
+ void __iomem *reg);
+#endif
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/imx-jpeg/mxc-jpeg.c
new file mode 100644
index 000000000000..03b9264af068
--- /dev/null
+++ b/drivers/media/platform/imx-jpeg/mxc-jpeg.c
@@ -0,0 +1,2126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 driver for the JPEG encoder/decoder from i.MX8QXP/i.MX8QM application
+ * processors.
+ *
+ * The multi-planar buffers API is used.
+ *
+ * Baseline and extended sequential jpeg decoding is supported.
+ * Progressive jpeg decoding is not supported by the IP.
+ * Supports encode and decode of various formats:
+ * YUV444, YUV422, YUV420, RGB, ARGB, Gray
+ * YUV420 is the only multi-planar format supported.
+ * Minimum resolution is 64 x 64, maximum 8192 x 8192.
+ * To achieve 8192 x 8192, modify in defconfig: CONFIG_CMA_SIZE_MBYTES=320
+ * The alignment requirements for the resolution depend on the format,
+ * multiple of 16 resolutions should work for all formats.
+ * Special workarounds are made in the driver to support NV12 1080p.
+ * When decoding, the driver detects image resolution and pixel format
+ * from the jpeg stream, by parsing the jpeg markers.
+ *
+ * The IP has 4 slots available for context switching, but only slot 0
+ * was fully tested to work. Context switching is not used by the driver.
+ * Each driver instance (context) allocates a slot for itself, but this
+ * is postponed until device_run, to allow unlimited opens.
+ *
+ * The driver submits jobs to the IP by setting up a descriptor for the
+ * used slot, and then validating it. The encoder has an additional descriptor
+ * for the configuration phase. The driver expects FRM_DONE interrupt from
+ * IP to mark the job as finished.
+ *
+ * The decoder IP has some limitations regarding the component ID's,
+ * but the driver works around this by replacing them in the jpeg stream.
+ *
+ * A module parameter is available for debug purpose (jpeg_tracing), to enable
+ * it, enable dynamic debug for this module and:
+ * echo 1 > /sys/module/mxc_jpeg_encdec/parameters/jpeg_tracing
+ *
+ * This is inspired by the drivers/media/platform/s5p-jpeg driver
+ *
+ * Copyright 2018-2019 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irqreturn.h>
+#include <linux/interrupt.h>
+#include <linux/pm_domain.h>
+#include <linux/string.h>
+
+#include <media/v4l2-jpeg.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mxc-jpeg-hw.h"
+#include "mxc-jpeg.h"
+
+static struct mxc_jpeg_fmt mxc_formats[] = {
+ {
+ .name = "JPEG",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .subsampling = -1,
+ .nc = -1,
+ .colplanes = 1,
+ .flags = MXC_JPEG_FMT_TYPE_ENC,
+ },
+ {
+ .name = "RGB", /*RGBRGB packed format*/
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ .nc = 3,
+ .depth = 24,
+ .colplanes = 1,
+ .h_align = 3,
+ .v_align = 3,
+ .flags = MXC_JPEG_FMT_TYPE_RAW,
+ },
+ {
+ .name = "ARGB", /* ARGBARGB packed format */
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ .nc = 4,
+ .depth = 32,
+ .colplanes = 1,
+ .h_align = 3,
+ .v_align = 3,
+ .flags = MXC_JPEG_FMT_TYPE_RAW,
+ },
+ {
+ .name = "YUV420", /* 1st plane = Y, 2nd plane = UV */
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ .nc = 3,
+ .depth = 12, /* 6 bytes (4Y + UV) for 4 pixels */
+ .colplanes = 2, /* 1 plane Y, 1 plane UV interleaved */
+ .h_align = 4,
+ .v_align = 4,
+ .flags = MXC_JPEG_FMT_TYPE_RAW,
+ },
+ {
+ .name = "YUV422", /* YUYV */
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ .nc = 3,
+ .depth = 16,
+ .colplanes = 1,
+ .h_align = 4,
+ .v_align = 3,
+ .flags = MXC_JPEG_FMT_TYPE_RAW,
+ },
+ {
+ .name = "YUV444", /* YUVYUV */
+ .fourcc = V4L2_PIX_FMT_YUV24,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ .nc = 3,
+ .depth = 24,
+ .colplanes = 1,
+ .h_align = 3,
+ .v_align = 3,
+ .flags = MXC_JPEG_FMT_TYPE_RAW,
+ },
+ {
+ .name = "Gray", /* Gray (Y8/Y12) or Single Comp */
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY,
+ .nc = 1,
+ .depth = 8,
+ .colplanes = 1,
+ .h_align = 3,
+ .v_align = 3,
+ .flags = MXC_JPEG_FMT_TYPE_RAW,
+ },
+};
+
+#define MXC_JPEG_NUM_FORMATS ARRAY_SIZE(mxc_formats)
+
+static const int mxc_decode_mode = MXC_JPEG_DECODE;
+static const int mxc_encode_mode = MXC_JPEG_ENCODE;
+
+static const struct of_device_id mxc_jpeg_match[] = {
+ {
+ .compatible = "nxp,imx8qxp-jpgdec",
+ .data = &mxc_decode_mode,
+ },
+ {
+ .compatible = "nxp,imx8qxp-jpgenc",
+ .data = &mxc_encode_mode,
+ },
+ { },
+};
+
+/*
+ * default configuration stream, 64x64 yuv422
+ * split by JPEG marker, so it's easier to modify & use
+ */
+static const unsigned char jpeg_soi[] = {
+ 0xFF, 0xD8
+};
+
+static const unsigned char jpeg_app0[] = {
+ 0xFF, 0xE0,
+ 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00
+};
+
+static const unsigned char jpeg_app14[] = {
+ 0xFF, 0xEE,
+ 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65,
+ 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char jpeg_dqt[] = {
+ 0xFF, 0xDB,
+ 0x00, 0x84, 0x00, 0x10, 0x0B, 0x0C, 0x0E,
+ 0x0C, 0x0A, 0x10, 0x0E, 0x0D, 0x0E, 0x12,
+ 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18,
+ 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1D,
+ 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40,
+ 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6D,
+ 0x51, 0x57, 0x5F, 0x62, 0x67, 0x68, 0x67,
+ 0x3E, 0x4D, 0x71, 0x79, 0x70, 0x64, 0x78,
+ 0x5C, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12,
+ 0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 0x1A,
+ 0x2F, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const unsigned char jpeg_sof_maximal[] = {
+ 0xFF, 0xC0,
+ 0x00, 0x14, 0x08, 0x00, 0x40, 0x00, 0x40,
+ 0x04, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01,
+ 0x03, 0x11, 0x01, 0x04, 0x11, 0x01
+};
+
+static const unsigned char jpeg_dht[] = {
+ 0xFF, 0xC4,
+ 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01,
+ 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05,
+ 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91,
+ 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15,
+ 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72,
+ 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19,
+ 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
+ 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
+ 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
+ 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4,
+ 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2,
+ 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
+ 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3,
+ 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
+ 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
+ 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02,
+ 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06,
+ 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13,
+ 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52,
+ 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16,
+ 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18,
+ 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
+ 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
+ 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4,
+ 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2,
+ 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+ 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5,
+ 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+};
+
+static const unsigned char jpeg_dri[] = {
+ 0xFF, 0xDD,
+ 0x00, 0x04, 0x00, 0x20
+};
+
+static const unsigned char jpeg_sos_maximal[] = {
+ 0xFF, 0xDA,
+ 0x00, 0x0C, 0x04, 0x01, 0x00, 0x02, 0x11, 0x03,
+ 0x11, 0x04, 0x11, 0x00, 0x3F, 0x00
+};
+
+static const unsigned char jpeg_eoi[] = {
+ 0xFF, 0xD9
+};
+
+struct mxc_jpeg_src_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer b;
+ struct list_head list;
+
+ /* mxc-jpeg specific */
+ bool dht_needed;
+ bool jpeg_parse_error;
+};
+
+static inline struct mxc_jpeg_src_buf *vb2_to_mxc_buf(struct vb2_buffer *vb)
+{
+ return container_of(to_vb2_v4l2_buffer(vb),
+ struct mxc_jpeg_src_buf, b);
+}
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-3)");
+
+static void _bswap16(u16 *a)
+{
+ *a = ((*a & 0x00FF) << 8) | ((*a & 0xFF00) >> 8);
+}
+
+static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf,
+ unsigned long len)
+{
+ unsigned int plane_no;
+ u32 dma_addr;
+ void *vaddr;
+ unsigned long payload;
+
+ if (debug < 3)
+ return;
+
+ for (plane_no = 0; plane_no < buf->num_planes; plane_no++) {
+ payload = vb2_get_plane_payload(buf, plane_no);
+ if (len == 0)
+ len = payload;
+ dma_addr = vb2_dma_contig_plane_dma_addr(buf, plane_no);
+ vaddr = vb2_plane_vaddr(buf, plane_no);
+ v4l2_dbg(3, debug, &jpeg->v4l2_dev,
+ "plane %d (vaddr=%p dma_addr=%x payload=%ld):",
+ plane_no, vaddr, dma_addr, payload);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+ vaddr, len, false);
+ }
+}
+
+static inline struct mxc_jpeg_ctx *mxc_jpeg_fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct mxc_jpeg_ctx, fh);
+}
+
+static int enum_fmt(struct mxc_jpeg_fmt *mxc_formats, int n,
+ struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num = 0;
+
+ for (i = 0; i < n; ++i) {
+ if (mxc_formats[i].flags == type) {
+ /* index-th format of searched type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index
+ */
+ ++num;
+ }
+ }
+
+ /* Format not found */
+ if (i >= n)
+ return -EINVAL;
+
+ strscpy(f->description, mxc_formats[i].name, sizeof(f->description));
+ f->pixelformat = mxc_formats[i].fourcc;
+
+ return 0;
+}
+
+static struct mxc_jpeg_fmt *mxc_jpeg_find_format(struct mxc_jpeg_ctx *ctx,
+ u32 pixelformat)
+{
+ unsigned int k;
+
+ for (k = 0; k < MXC_JPEG_NUM_FORMATS; k++) {
+ struct mxc_jpeg_fmt *fmt = &mxc_formats[k];
+
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+ return NULL;
+}
+
+static enum mxc_jpeg_image_format mxc_jpeg_fourcc_to_imgfmt(u32 fourcc)
+{
+ switch (fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ return MXC_JPEG_GRAY;
+ case V4L2_PIX_FMT_YUYV:
+ return MXC_JPEG_YUV422;
+ case V4L2_PIX_FMT_NV12:
+ return MXC_JPEG_YUV420;
+ case V4L2_PIX_FMT_YUV24:
+ return MXC_JPEG_YUV444;
+ case V4L2_PIX_FMT_RGB24:
+ return MXC_JPEG_RGB;
+ case V4L2_PIX_FMT_ARGB32:
+ return MXC_JPEG_ARGB;
+ default:
+ return MXC_JPEG_INVALID;
+ }
+}
+
+static struct mxc_jpeg_q_data *mxc_jpeg_get_q_data(struct mxc_jpeg_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &ctx->out_q;
+ return &ctx->cap_q;
+}
+
+static void mxc_jpeg_addrs(struct mxc_jpeg_desc *desc,
+ struct vb2_buffer *raw_buf,
+ struct vb2_buffer *jpeg_buf, int offset)
+{
+ int img_fmt = desc->stm_ctrl & STM_CTRL_IMAGE_FORMAT_MASK;
+
+ desc->buf_base0 = vb2_dma_contig_plane_dma_addr(raw_buf, 0);
+ desc->buf_base1 = 0;
+ if (img_fmt == STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV420)) {
+ WARN_ON(raw_buf->num_planes < 2);
+ desc->buf_base1 = vb2_dma_contig_plane_dma_addr(raw_buf, 1);
+ }
+ desc->stm_bufbase = vb2_dma_contig_plane_dma_addr(jpeg_buf, 0) +
+ offset;
+}
+
+static void notify_eos(struct mxc_jpeg_ctx *ctx)
+{
+ const struct v4l2_event ev = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ dev_dbg(ctx->mxc_jpeg->dev, "Notify app event EOS reached");
+ v4l2_event_queue_fh(&ctx->fh, &ev);
+}
+
+static void notify_src_chg(struct mxc_jpeg_ctx *ctx)
+{
+ const struct v4l2_event ev = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes =
+ V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ dev_dbg(ctx->mxc_jpeg->dev, "Notify app event SRC_CH_RESOLUTION");
+ v4l2_event_queue_fh(&ctx->fh, &ev);
+}
+
+static int mxc_get_free_slot(struct mxc_jpeg_slot_data slot_data[], int n)
+{
+ int free_slot = 0;
+
+ while (slot_data[free_slot].used && free_slot < n)
+ free_slot++;
+
+ return free_slot; /* >=n when there are no more free slots */
+}
+
+static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg,
+ unsigned int slot)
+{
+ struct mxc_jpeg_desc *desc;
+ struct mxc_jpeg_desc *cfg_desc;
+ void *cfg_stm;
+
+ if (jpeg->slot_data[slot].desc)
+ goto skip_alloc; /* already allocated, reuse it */
+
+ /* allocate descriptor for decoding/encoding phase */
+ desc = dma_alloc_coherent(jpeg->dev,
+ sizeof(struct mxc_jpeg_desc),
+ &jpeg->slot_data[slot].desc_handle,
+ GFP_ATOMIC);
+ if (!desc)
+ goto err;
+ jpeg->slot_data[slot].desc = desc;
+
+ /* allocate descriptor for configuration phase (encoder only) */
+ cfg_desc = dma_alloc_coherent(jpeg->dev,
+ sizeof(struct mxc_jpeg_desc),
+ &jpeg->slot_data[slot].cfg_desc_handle,
+ GFP_ATOMIC);
+ if (!cfg_desc)
+ goto err;
+ jpeg->slot_data[slot].cfg_desc = cfg_desc;
+
+ /* allocate configuration stream */
+ cfg_stm = dma_alloc_coherent(jpeg->dev,
+ MXC_JPEG_MAX_CFG_STREAM,
+ &jpeg->slot_data[slot].cfg_stream_handle,
+ GFP_ATOMIC);
+ if (!cfg_stm)
+ goto err;
+ jpeg->slot_data[slot].cfg_stream_vaddr = cfg_stm;
+
+skip_alloc:
+ jpeg->slot_data[slot].used = true;
+
+ return true;
+err:
+ dev_err(jpeg->dev, "Could not allocate descriptors for slot %d", slot);
+
+ return false;
+}
+
+static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg,
+ unsigned int slot)
+{
+ if (slot >= MXC_MAX_SLOTS) {
+ dev_err(jpeg->dev, "Invalid slot %d, nothing to free.", slot);
+ return;
+ }
+
+ /* free descriptor for decoding/encoding phase */
+ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data[slot].desc,
+ jpeg->slot_data[slot].desc_handle);
+
+ /* free descriptor for encoder configuration phase / decoder DHT */
+ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data[slot].cfg_desc,
+ jpeg->slot_data[slot].cfg_desc_handle);
+
+ /* free configuration stream */
+ dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM,
+ jpeg->slot_data[slot].cfg_stream_vaddr,
+ jpeg->slot_data[slot].cfg_stream_handle);
+
+ jpeg->slot_data[slot].used = false;
+}
+
+static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
+{
+ struct mxc_jpeg_dev *jpeg = priv;
+ struct mxc_jpeg_ctx *ctx;
+ void __iomem *reg = jpeg->base_reg;
+ struct device *dev = jpeg->dev;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ struct mxc_jpeg_src_buf *jpeg_src_buf;
+ enum vb2_buffer_state buf_state;
+ u32 dec_ret, com_status;
+ unsigned long payload;
+ struct mxc_jpeg_q_data *q_data;
+ enum v4l2_buf_type cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ unsigned int slot;
+
+ spin_lock(&jpeg->hw_lock);
+
+ com_status = readl(reg + COM_STATUS);
+ slot = COM_STATUS_CUR_SLOT(com_status);
+ dev_dbg(dev, "Irq %d on slot %d.\n", irq, slot);
+
+ ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
+ if (!ctx) {
+ dev_err(dev,
+ "Instance released before the end of transaction.\n");
+ /* soft reset only resets internal state, not registers */
+ mxc_jpeg_sw_reset(reg);
+ /* clear all interrupts */
+ writel(0xFFFFFFFF, reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS));
+ goto job_unlock;
+ }
+
+ if (slot != ctx->slot) {
+ /* TODO investigate when adding multi-instance support */
+ dev_warn(dev, "IRQ slot %d != context slot %d.\n",
+ slot, ctx->slot);
+ goto job_unlock;
+ }
+
+ dec_ret = readl(reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS));
+ writel(dec_ret, reg + MXC_SLOT_OFFSET(slot, SLOT_STATUS)); /* w1c */
+
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ jpeg_src_buf = vb2_to_mxc_buf(&src_buf->vb2_buf);
+
+ if (dec_ret & SLOT_STATUS_ENC_CONFIG_ERR) {
+ u32 ret = readl(reg + CAST_STATUS12);
+
+ dev_err(dev, "Encoder/decoder error, status=0x%08x", ret);
+ mxc_jpeg_sw_reset(reg);
+ buf_state = VB2_BUF_STATE_ERROR;
+ goto buffers_done;
+ }
+
+ if (!(dec_ret & SLOT_STATUS_FRMDONE))
+ goto job_unlock;
+
+ if (jpeg->mode == MXC_JPEG_ENCODE &&
+ ctx->enc_state == MXC_JPEG_ENC_CONF) {
+ ctx->enc_state = MXC_JPEG_ENCODING;
+ dev_dbg(dev, "Encoder config finished. Start encoding...\n");
+ mxc_jpeg_enc_mode_go(dev, reg);
+ goto job_unlock;
+ }
+ if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed) {
+ jpeg_src_buf->dht_needed = false;
+ dev_dbg(dev, "Decoder DHT cfg finished. Start decoding...\n");
+ goto job_unlock;
+ }
+ if (jpeg->mode == MXC_JPEG_ENCODE) {
+ payload = readl(reg + MXC_SLOT_OFFSET(slot, SLOT_BUF_PTR));
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload);
+ dev_dbg(dev, "Encoding finished, payload size: %ld\n",
+ payload);
+ } else {
+ q_data = mxc_jpeg_get_q_data(ctx, cap_type);
+ payload = q_data->sizeimage[0];
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0);
+ if (q_data->fmt->colplanes == 2) {
+ payload = q_data->sizeimage[1];
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 1, payload);
+ }
+ dev_dbg(dev, "Decoding finished, payload size: %ld + %ld\n",
+ vb2_get_plane_payload(&dst_buf->vb2_buf, 0),
+ vb2_get_plane_payload(&dst_buf->vb2_buf, 1));
+ }
+
+ /* short preview of the results */
+ dev_dbg(dev, "src_buf preview: ");
+ print_mxc_buf(jpeg, &src_buf->vb2_buf, 32);
+ dev_dbg(dev, "dst_buf preview: ");
+ print_mxc_buf(jpeg, &dst_buf->vb2_buf, 32);
+ buf_state = VB2_BUF_STATE_DONE;
+
+buffers_done:
+ jpeg->slot_data[slot].used = false; /* unused, but don't free */
+ v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, buf_state);
+ v4l2_m2m_buf_done(dst_buf, buf_state);
+ spin_unlock(&jpeg->hw_lock);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+ return IRQ_HANDLED;
+job_unlock:
+ spin_unlock(&jpeg->hw_lock);
+ return IRQ_HANDLED;
+}
+
+static int mxc_jpeg_fixup_sof(struct mxc_jpeg_sof *sof,
+ u32 fourcc,
+ u16 w, u16 h)
+{
+ int sof_length;
+
+ sof->precision = 8; /* TODO allow 8/12 bit precision*/
+ sof->height = h;
+ _bswap16(&sof->height);
+ sof->width = w;
+ _bswap16(&sof->width);
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ sof->components_no = 3;
+ sof->comp[0].v = 0x2;
+ sof->comp[0].h = 0x2;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ sof->components_no = 3;
+ sof->comp[0].v = 0x1;
+ sof->comp[0].h = 0x2;
+ break;
+ case V4L2_PIX_FMT_YUV24:
+ case V4L2_PIX_FMT_RGB24:
+ default:
+ sof->components_no = 3;
+ break;
+ case V4L2_PIX_FMT_ARGB32:
+ sof->components_no = 4;
+ break;
+ case V4L2_PIX_FMT_GREY:
+ sof->components_no = 1;
+ break;
+ }
+ sof_length = 8 + 3 * sof->components_no;
+ sof->length = sof_length;
+ _bswap16(&sof->length);
+
+ return sof_length; /* not swaped */
+}
+
+static int mxc_jpeg_fixup_sos(struct mxc_jpeg_sos *sos,
+ u32 fourcc)
+{
+ int sos_length;
+ u8 *sof_u8 = (u8 *)sos;
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ sos->components_no = 3;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ sos->components_no = 3;
+ break;
+ case V4L2_PIX_FMT_YUV24:
+ case V4L2_PIX_FMT_RGB24:
+ default:
+ sos->components_no = 3;
+ break;
+ case V4L2_PIX_FMT_ARGB32:
+ sos->components_no = 4;
+ break;
+ case V4L2_PIX_FMT_GREY:
+ sos->components_no = 1;
+ break;
+ }
+ sos_length = 6 + 2 * sos->components_no;
+ sos->length = sos_length;
+ _bswap16(&sos->length);
+
+ /* SOS ignorable bytes, not so ignorable after all */
+ sof_u8[sos_length - 1] = 0x0;
+ sof_u8[sos_length - 2] = 0x3f;
+ sof_u8[sos_length - 3] = 0x0;
+
+ return sos_length; /* not swaped */
+}
+
+static unsigned int mxc_jpeg_setup_cfg_stream(void *cfg_stream_vaddr,
+ u32 fourcc,
+ u16 w, u16 h)
+{
+ unsigned int offset = 0;
+ u8 *cfg = (u8 *)cfg_stream_vaddr;
+ struct mxc_jpeg_sof *sof;
+ struct mxc_jpeg_sos *sos;
+
+ memcpy(cfg + offset, jpeg_soi, ARRAY_SIZE(jpeg_soi));
+ offset += ARRAY_SIZE(jpeg_soi);
+
+ if (fourcc == V4L2_PIX_FMT_RGB24 ||
+ fourcc == V4L2_PIX_FMT_ARGB32) {
+ memcpy(cfg + offset, jpeg_app14, sizeof(jpeg_app14));
+ offset += sizeof(jpeg_app14);
+ } else {
+ memcpy(cfg + offset, jpeg_app0, sizeof(jpeg_app0));
+ offset += sizeof(jpeg_app0);
+ }
+
+ memcpy(cfg + offset, jpeg_dqt, sizeof(jpeg_dqt));
+ offset += sizeof(jpeg_dqt);
+
+ memcpy(cfg + offset, jpeg_sof_maximal, sizeof(jpeg_sof_maximal));
+ offset += 2; /* skip marker ID */
+ sof = (struct mxc_jpeg_sof *)(cfg + offset);
+ offset += mxc_jpeg_fixup_sof(sof, fourcc, w, h);
+
+ memcpy(cfg + offset, jpeg_dht, sizeof(jpeg_dht));
+ offset += sizeof(jpeg_dht);
+
+ memcpy(cfg + offset, jpeg_dri, sizeof(jpeg_dri));
+ offset += sizeof(jpeg_dri);
+
+ memcpy(cfg + offset, jpeg_sos_maximal, sizeof(jpeg_sos_maximal));
+ offset += 2; /* skip marker ID */
+ sos = (struct mxc_jpeg_sos *)(cfg + offset);
+ offset += mxc_jpeg_fixup_sos(sos, fourcc);
+
+ memcpy(cfg + offset, jpeg_eoi, sizeof(jpeg_eoi));
+ offset += sizeof(jpeg_eoi);
+
+ return offset;
+}
+
+static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf,
+ struct mxc_jpeg_ctx *ctx,
+ struct vb2_buffer *src_buf,
+ struct vb2_buffer *dst_buf)
+{
+ enum v4l2_buf_type cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ struct mxc_jpeg_q_data *q_data_cap;
+ enum mxc_jpeg_image_format img_fmt;
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ void __iomem *reg = jpeg->base_reg;
+ unsigned int slot = ctx->slot;
+ struct mxc_jpeg_desc *desc = jpeg->slot_data[slot].desc;
+ struct mxc_jpeg_desc *cfg_desc = jpeg->slot_data[slot].cfg_desc;
+ dma_addr_t desc_handle = jpeg->slot_data[slot].desc_handle;
+ dma_addr_t cfg_desc_handle = jpeg->slot_data[slot].cfg_desc_handle;
+ dma_addr_t cfg_stream_handle = jpeg->slot_data[slot].cfg_stream_handle;
+ unsigned int *cfg_size = &jpeg->slot_data[slot].cfg_stream_size;
+ void *cfg_stream_vaddr = jpeg->slot_data[slot].cfg_stream_vaddr;
+ struct mxc_jpeg_src_buf *jpeg_src_buf;
+
+ jpeg_src_buf = vb2_to_mxc_buf(src_buf);
+
+ /* setup the decoding descriptor */
+ desc->next_descpt_ptr = 0; /* end of chain */
+ q_data_cap = mxc_jpeg_get_q_data(ctx, cap_type);
+ desc->imgsize = q_data_cap->w_adjusted << 16 | q_data_cap->h_adjusted;
+ img_fmt = mxc_jpeg_fourcc_to_imgfmt(q_data_cap->fmt->fourcc);
+ desc->stm_ctrl &= ~STM_CTRL_IMAGE_FORMAT(0xF); /* clear image format */
+ desc->stm_ctrl |= STM_CTRL_IMAGE_FORMAT(img_fmt);
+ desc->line_pitch = q_data_cap->bytesperline[0];
+ mxc_jpeg_addrs(desc, dst_buf, src_buf, 0);
+ mxc_jpeg_set_bufsize(desc, ALIGN(vb2_plane_size(src_buf, 0), 1024));
+ print_descriptor_info(jpeg->dev, desc);
+
+ if (!jpeg_src_buf->dht_needed) {
+ /* validate the decoding descriptor */
+ mxc_jpeg_set_desc(desc_handle, reg, slot);
+ return;
+ }
+
+ /*
+ * if a default huffman table is needed, use the config descriptor to
+ * inject a DHT, by chaining it before the decoding descriptor
+ */
+ *cfg_size = mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr,
+ V4L2_PIX_FMT_YUYV,
+ MXC_JPEG_MIN_WIDTH,
+ MXC_JPEG_MIN_HEIGHT);
+ cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN;
+ cfg_desc->buf_base0 = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ cfg_desc->buf_base1 = 0;
+ cfg_desc->imgsize = MXC_JPEG_MIN_WIDTH << 16;
+ cfg_desc->imgsize |= MXC_JPEG_MIN_HEIGHT;
+ cfg_desc->line_pitch = MXC_JPEG_MIN_WIDTH * 2;
+ cfg_desc->stm_ctrl = STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV422);
+ cfg_desc->stm_bufbase = cfg_stream_handle;
+ cfg_desc->stm_bufsize = ALIGN(*cfg_size, 1024);
+ print_descriptor_info(jpeg->dev, cfg_desc);
+
+ /* validate the configuration descriptor */
+ mxc_jpeg_set_desc(cfg_desc_handle, reg, slot);
+}
+
+static void mxc_jpeg_config_enc_desc(struct vb2_buffer *out_buf,
+ struct mxc_jpeg_ctx *ctx,
+ struct vb2_buffer *src_buf,
+ struct vb2_buffer *dst_buf)
+{
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ void __iomem *reg = jpeg->base_reg;
+ unsigned int slot = ctx->slot;
+ struct mxc_jpeg_desc *desc = jpeg->slot_data[slot].desc;
+ struct mxc_jpeg_desc *cfg_desc = jpeg->slot_data[slot].cfg_desc;
+ dma_addr_t desc_handle = jpeg->slot_data[slot].desc_handle;
+ dma_addr_t cfg_desc_handle = jpeg->slot_data[slot].cfg_desc_handle;
+ void *cfg_stream_vaddr = jpeg->slot_data[slot].cfg_stream_vaddr;
+ struct mxc_jpeg_q_data *q_data;
+ enum mxc_jpeg_image_format img_fmt;
+ int w, h;
+
+ q_data = mxc_jpeg_get_q_data(ctx, src_buf->vb2_queue->type);
+
+ jpeg->slot_data[slot].cfg_stream_size =
+ mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr,
+ q_data->fmt->fourcc,
+ q_data->w_adjusted,
+ q_data->h_adjusted);
+
+ /* chain the config descriptor with the encoding descriptor */
+ cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN;
+
+ cfg_desc->buf_base0 = jpeg->slot_data[slot].cfg_stream_handle;
+ cfg_desc->buf_base1 = 0;
+ cfg_desc->line_pitch = 0;
+ cfg_desc->stm_bufbase = 0; /* no output expected */
+ cfg_desc->stm_bufsize = 0x0;
+ cfg_desc->imgsize = 0;
+ cfg_desc->stm_ctrl = STM_CTRL_CONFIG_MOD(1);
+
+ desc->next_descpt_ptr = 0; /* end of chain */
+
+ /* use adjusted resolution for CAST IP job */
+ w = q_data->w_adjusted;
+ h = q_data->h_adjusted;
+ mxc_jpeg_set_res(desc, w, h);
+ mxc_jpeg_set_line_pitch(desc, w * (q_data->fmt->depth / 8));
+ mxc_jpeg_set_bufsize(desc, desc->line_pitch * h);
+ img_fmt = mxc_jpeg_fourcc_to_imgfmt(q_data->fmt->fourcc);
+ if (img_fmt == MXC_JPEG_INVALID)
+ dev_err(jpeg->dev, "No valid image format detected\n");
+ desc->stm_ctrl = STM_CTRL_CONFIG_MOD(0) |
+ STM_CTRL_IMAGE_FORMAT(img_fmt);
+ mxc_jpeg_addrs(desc, src_buf, dst_buf, 0);
+ dev_dbg(jpeg->dev, "cfg_desc:\n");
+ print_descriptor_info(jpeg->dev, cfg_desc);
+ dev_dbg(jpeg->dev, "enc desc:\n");
+ print_descriptor_info(jpeg->dev, desc);
+ print_wrapper_info(jpeg->dev, reg);
+ print_cast_status(jpeg->dev, reg, MXC_JPEG_ENCODE);
+
+ /* validate the configuration descriptor */
+ mxc_jpeg_set_desc(cfg_desc_handle, reg, slot);
+}
+
+static void mxc_jpeg_device_run(void *priv)
+{
+ struct mxc_jpeg_ctx *ctx = priv;
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ void __iomem *reg = jpeg->base_reg;
+ struct device *dev = jpeg->dev;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ unsigned long flags;
+ struct mxc_jpeg_q_data *q_data_cap, *q_data_out;
+ struct mxc_jpeg_src_buf *jpeg_src_buf;
+
+ spin_lock_irqsave(&ctx->mxc_jpeg->hw_lock, flags);
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ if (!src_buf || !dst_buf) {
+ dev_err(dev, "Null src or dst buf\n");
+ goto end;
+ }
+
+ q_data_cap = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!q_data_cap)
+ goto end;
+ q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (!q_data_out)
+ goto end;
+ src_buf->sequence = q_data_out->sequence++;
+ dst_buf->sequence = q_data_cap->sequence++;
+
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+
+ jpeg_src_buf = vb2_to_mxc_buf(&src_buf->vb2_buf);
+ if (jpeg_src_buf->jpeg_parse_error) {
+ jpeg->slot_data[ctx->slot].used = false;
+ v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+
+ return;
+ }
+
+ /*
+ * TODO: this reset should be removed, once we figure out
+ * how to overcome hardware issues both on encoder and decoder
+ */
+ mxc_jpeg_sw_reset(reg);
+ mxc_jpeg_enable(reg);
+ mxc_jpeg_set_l_endian(reg, 1);
+
+ ctx->slot = mxc_get_free_slot(jpeg->slot_data, MXC_MAX_SLOTS);
+ if (ctx->slot >= MXC_MAX_SLOTS) {
+ dev_err(dev, "No more free slots\n");
+ goto end;
+ }
+ if (!mxc_jpeg_alloc_slot_data(jpeg, ctx->slot)) {
+ dev_err(dev, "Cannot allocate slot data\n");
+ goto end;
+ }
+
+ mxc_jpeg_enable_slot(reg, ctx->slot);
+ mxc_jpeg_enable_irq(reg, ctx->slot);
+
+ if (jpeg->mode == MXC_JPEG_ENCODE) {
+ dev_dbg(dev, "Encoding on slot %d\n", ctx->slot);
+ ctx->enc_state = MXC_JPEG_ENC_CONF;
+ mxc_jpeg_config_enc_desc(&dst_buf->vb2_buf, ctx,
+ &src_buf->vb2_buf, &dst_buf->vb2_buf);
+ mxc_jpeg_enc_mode_conf(dev, reg); /* start config phase */
+ } else {
+ dev_dbg(dev, "Decoding on slot %d\n", ctx->slot);
+ print_mxc_buf(jpeg, &src_buf->vb2_buf, 0);
+ mxc_jpeg_config_dec_desc(&dst_buf->vb2_buf, ctx,
+ &src_buf->vb2_buf, &dst_buf->vb2_buf);
+ mxc_jpeg_dec_mode_go(dev, reg);
+ }
+end:
+ spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
+}
+
+static int mxc_jpeg_decoder_cmd(struct file *file, void *priv,
+ struct v4l2_decoder_cmd *cmd)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(fh);
+ struct device *dev = ctx->mxc_jpeg->dev;
+ int ret;
+
+ ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd);
+ if (ret < 0)
+ return ret;
+
+ if (cmd->cmd == V4L2_DEC_CMD_STOP) {
+ dev_dbg(dev, "Received V4L2_DEC_CMD_STOP");
+ if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) {
+ /* No more src bufs, notify app EOS */
+ notify_eos(ctx);
+ } else {
+ /* will send EOS later*/
+ ctx->stopping = 1;
+ }
+ }
+
+ return 0;
+}
+
+static int mxc_jpeg_encoder_cmd(struct file *file, void *priv,
+ struct v4l2_encoder_cmd *cmd)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(fh);
+ struct device *dev = ctx->mxc_jpeg->dev;
+ int ret;
+
+ ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
+ if (ret < 0)
+ return ret;
+
+ if (cmd->cmd == V4L2_ENC_CMD_STOP) {
+ dev_dbg(dev, "Received V4L2_ENC_CMD_STOP");
+ if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) {
+ /* No more src bufs, notify app EOS */
+ notify_eos(ctx);
+ } else {
+ /* will send EOS later*/
+ ctx->stopping = 1;
+ }
+ }
+
+ return 0;
+}
+
+static int mxc_jpeg_queue_setup(struct vb2_queue *q,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_ctxs[])
+{
+ struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+ struct mxc_jpeg_q_data *q_data = NULL;
+ int i;
+
+ q_data = mxc_jpeg_get_q_data(ctx, q->type);
+ if (!q_data)
+ return -EINVAL;
+
+ /* Handle CREATE_BUFS situation - *nplanes != 0 */
+ if (*nplanes) {
+ for (i = 0; i < *nplanes; i++) {
+ if (sizes[i] < q_data->sizeimage[i])
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ /* Handle REQBUFS situation */
+ *nplanes = q_data->fmt->colplanes;
+ for (i = 0; i < *nplanes; i++)
+ sizes[i] = q_data->sizeimage[i];
+
+ return 0;
+}
+
+static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+ struct mxc_jpeg_q_data *q_data = mxc_jpeg_get_q_data(ctx, q->type);
+
+ dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx);
+ q_data->sequence = 0;
+
+ return 0;
+}
+
+static void mxc_jpeg_stop_streaming(struct vb2_queue *q)
+{
+ struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+ struct vb2_v4l2_buffer *vbuf;
+
+ dev_dbg(ctx->mxc_jpeg->dev, "Stop streaming ctx=%p", ctx);
+
+ /* Release all active buffers */
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (!vbuf)
+ return;
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static int mxc_jpeg_valid_comp_id(struct device *dev,
+ struct mxc_jpeg_sof *sof,
+ struct mxc_jpeg_sos *sos)
+{
+ int valid = 1;
+ int i;
+
+ /*
+ * there's a limitation in the IP that the component IDs must be
+ * between 0..4, if they are not, let's patch them
+ */
+ for (i = 0; i < sof->components_no; i++)
+ if (sof->comp[i].id > MXC_JPEG_MAX_COMPONENTS) {
+ valid = 0;
+ dev_err(dev, "Component %d has invalid ID: %d",
+ i, sof->comp[i].id);
+ }
+ if (!valid)
+ /* patch all comp IDs if at least one is invalid */
+ for (i = 0; i < sof->components_no; i++) {
+ dev_warn(dev, "Component %d ID patched to: %d",
+ i, i + 1);
+ sof->comp[i].id = i + 1;
+ sos->comp[i].id = i + 1;
+ }
+
+ return valid;
+}
+
+static u32 mxc_jpeg_get_image_format(struct device *dev,
+ const struct v4l2_jpeg_header *header)
+{
+ int i;
+ u32 fourcc = 0;
+
+ for (i = 0; i < MXC_JPEG_NUM_FORMATS; i++)
+ if (mxc_formats[i].subsampling == header->frame.subsampling &&
+ mxc_formats[i].nc == header->frame.num_components) {
+ fourcc = mxc_formats[i].fourcc;
+ break;
+ }
+ if (fourcc == 0) {
+ dev_err(dev, "Could not identify image format nc=%d, subsampling=%d\n",
+ header->frame.num_components,
+ header->frame.subsampling);
+ return fourcc;
+ }
+ /*
+ * If the transform flag from APP14 marker is 0, images that are
+ * encoded with 3 components have RGB colorspace, see Recommendation
+ * ITU-T T.872 chapter 6.5.3 APP14 marker segment for colour encoding
+ */
+ if (fourcc == V4L2_PIX_FMT_YUV24 || fourcc == V4L2_PIX_FMT_RGB24) {
+ if (header->app14_tf == V4L2_JPEG_APP14_TF_CMYK_RGB)
+ fourcc = V4L2_PIX_FMT_RGB24;
+ else
+ fourcc = V4L2_PIX_FMT_YUV24;
+ }
+
+ return fourcc;
+}
+
+static void mxc_jpeg_bytesperline(struct mxc_jpeg_q_data *q,
+ u32 precision)
+{
+ /* Bytes distance between the leftmost pixels in two adjacent lines */
+ if (q->fmt->fourcc == V4L2_PIX_FMT_JPEG) {
+ /* bytesperline unused for compressed formats */
+ q->bytesperline[0] = 0;
+ q->bytesperline[1] = 0;
+ } else if (q->fmt->fourcc == V4L2_PIX_FMT_NV12) {
+ /* When the image format is planar the bytesperline value
+ * applies to the first plane and is divided by the same factor
+ * as the width field for the other planes
+ */
+ q->bytesperline[0] = q->w * (precision / 8) *
+ (q->fmt->depth / 8);
+ q->bytesperline[1] = q->bytesperline[0];
+ } else {
+ /* single plane formats */
+ q->bytesperline[0] = q->w * (precision / 8) *
+ (q->fmt->depth / 8);
+ q->bytesperline[1] = 0;
+ }
+}
+
+static void mxc_jpeg_sizeimage(struct mxc_jpeg_q_data *q)
+{
+ if (q->fmt->fourcc == V4L2_PIX_FMT_JPEG) {
+ /* if no sizeimage from user, assume worst jpeg compression */
+ if (!q->sizeimage[0])
+ q->sizeimage[0] = 6 * q->w * q->h;
+ q->sizeimage[1] = 0;
+
+ if (q->sizeimage[0] > MXC_JPEG_MAX_SIZEIMAGE)
+ q->sizeimage[0] = MXC_JPEG_MAX_SIZEIMAGE;
+
+ /* jpeg stream size must be multiple of 1K */
+ q->sizeimage[0] = ALIGN(q->sizeimage[0], 1024);
+ } else {
+ q->sizeimage[0] = q->bytesperline[0] * q->h;
+ q->sizeimage[1] = 0;
+ if (q->fmt->fourcc == V4L2_PIX_FMT_NV12)
+ q->sizeimage[1] = q->sizeimage[0] / 2;
+ }
+}
+
+static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx,
+ u8 *src_addr, u32 size, bool *dht_needed)
+{
+ struct device *dev = ctx->mxc_jpeg->dev;
+ struct mxc_jpeg_q_data *q_data_out, *q_data_cap;
+ enum v4l2_buf_type cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ bool src_chg = false;
+ u32 fourcc;
+ struct v4l2_jpeg_header header;
+ struct mxc_jpeg_sof *psof = NULL;
+ struct mxc_jpeg_sos *psos = NULL;
+ int ret;
+
+ memset(&header, 0, sizeof(header));
+ ret = v4l2_jpeg_parse_header((void *)src_addr, size, &header);
+ if (ret < 0) {
+ dev_err(dev, "Error parsing JPEG stream markers\n");
+ return ret;
+ }
+
+ /* if DHT marker present, no need to inject default one */
+ *dht_needed = (header.num_dht == 0);
+
+ q_data_out = mxc_jpeg_get_q_data(ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (q_data_out->w == 0 && q_data_out->h == 0) {
+ dev_warn(dev, "Invalid user resolution 0x0");
+ dev_warn(dev, "Keeping resolution from JPEG: %dx%d",
+ header.frame.width, header.frame.height);
+ q_data_out->w = header.frame.width;
+ q_data_out->h = header.frame.height;
+ } else if (header.frame.width != q_data_out->w ||
+ header.frame.height != q_data_out->h) {
+ dev_err(dev,
+ "Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)",
+ header.frame.width, header.frame.height,
+ q_data_out->w, q_data_out->h);
+ return -EINVAL;
+ }
+ if (header.frame.width % 8 != 0 || header.frame.height % 8 != 0) {
+ dev_err(dev, "JPEG width or height not multiple of 8: %dx%d\n",
+ header.frame.width, header.frame.height);
+ return -EINVAL;
+ }
+ if (header.frame.width > MXC_JPEG_MAX_WIDTH ||
+ header.frame.height > MXC_JPEG_MAX_HEIGHT) {
+ dev_err(dev, "JPEG width or height should be <= 8192: %dx%d\n",
+ header.frame.width, header.frame.height);
+ return -EINVAL;
+ }
+ if (header.frame.width < MXC_JPEG_MIN_WIDTH ||
+ header.frame.height < MXC_JPEG_MIN_HEIGHT) {
+ dev_err(dev, "JPEG width or height should be > 64: %dx%d\n",
+ header.frame.width, header.frame.height);
+ return -EINVAL;
+ }
+ if (header.frame.num_components > V4L2_JPEG_MAX_COMPONENTS) {
+ dev_err(dev, "JPEG number of components should be <=%d",
+ V4L2_JPEG_MAX_COMPONENTS);
+ return -EINVAL;
+ }
+ /* check and, if necessary, patch component IDs*/
+ psof = (struct mxc_jpeg_sof *)header.sof.start;
+ psos = (struct mxc_jpeg_sos *)header.sos.start;
+ if (!mxc_jpeg_valid_comp_id(dev, psof, psos))
+ dev_warn(dev, "JPEG component ids should be 0-3 or 1-4");
+
+ fourcc = mxc_jpeg_get_image_format(dev, &header);
+ if (fourcc == 0)
+ return -EINVAL;
+
+ /*
+ * set-up the capture queue with the pixelformat and resolution
+ * detected from the jpeg output stream
+ */
+ q_data_cap = mxc_jpeg_get_q_data(ctx, cap_type);
+ if (q_data_cap->w != header.frame.width ||
+ q_data_cap->h != header.frame.height)
+ src_chg = true;
+ q_data_cap->w = header.frame.width;
+ q_data_cap->h = header.frame.height;
+ q_data_cap->fmt = mxc_jpeg_find_format(ctx, fourcc);
+ q_data_cap->w_adjusted = q_data_cap->w;
+ q_data_cap->h_adjusted = q_data_cap->h;
+ /*
+ * align up the resolution for CAST IP,
+ * but leave the buffer resolution unchanged
+ */
+ v4l_bound_align_image(&q_data_cap->w_adjusted,
+ q_data_cap->w_adjusted, /* adjust up */
+ MXC_JPEG_MAX_WIDTH,
+ q_data_cap->fmt->h_align,
+ &q_data_cap->h_adjusted,
+ q_data_cap->h_adjusted, /* adjust up */
+ MXC_JPEG_MAX_HEIGHT,
+ q_data_cap->fmt->v_align,
+ 0);
+ dev_dbg(dev, "Detected jpeg res=(%dx%d)->(%dx%d), pixfmt=%c%c%c%c\n",
+ q_data_cap->w, q_data_cap->h,
+ q_data_cap->w_adjusted, q_data_cap->h_adjusted,
+ (fourcc & 0xff),
+ (fourcc >> 8) & 0xff,
+ (fourcc >> 16) & 0xff,
+ (fourcc >> 24) & 0xff);
+
+ /* setup bytesperline/sizeimage for capture queue */
+ mxc_jpeg_bytesperline(q_data_cap, header.frame.precision);
+ mxc_jpeg_sizeimage(q_data_cap);
+
+ /*
+ * if the CAPTURE format was updated with new values, regardless of
+ * whether they match the values set by the client or not, signal
+ * a source change event
+ */
+ if (src_chg)
+ notify_src_chg(ctx);
+
+ return 0;
+}
+
+static void mxc_jpeg_buf_queue(struct vb2_buffer *vb)
+{
+ int ret;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct mxc_jpeg_src_buf *jpeg_src_buf;
+
+ if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ goto end;
+
+ /* for V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE */
+ if (ctx->mxc_jpeg->mode != MXC_JPEG_DECODE)
+ goto end;
+
+ jpeg_src_buf = vb2_to_mxc_buf(vb);
+ jpeg_src_buf->jpeg_parse_error = false;
+ ret = mxc_jpeg_parse(ctx,
+ (u8 *)vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0),
+ &jpeg_src_buf->dht_needed);
+ if (ret)
+ jpeg_src_buf->jpeg_parse_error = true;
+
+end:
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int mxc_jpeg_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct mxc_jpeg_q_data *q_data = NULL;
+ struct device *dev = ctx->mxc_jpeg->dev;
+ unsigned long sizeimage;
+ int i;
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ q_data = mxc_jpeg_get_q_data(ctx, vb->vb2_queue->type);
+ if (!q_data)
+ return -EINVAL;
+ for (i = 0; i < q_data->fmt->colplanes; i++) {
+ sizeimage = q_data->sizeimage[i];
+ if (vb2_plane_size(vb, i) < sizeimage) {
+ dev_err(dev, "plane %d too small (%lu < %lu)",
+ i, vb2_plane_size(vb, i), sizeimage);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, sizeimage);
+ }
+ return 0;
+}
+
+static const struct vb2_ops mxc_jpeg_qops = {
+ .queue_setup = mxc_jpeg_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_out_validate = mxc_jpeg_buf_out_validate,
+ .buf_prepare = mxc_jpeg_buf_prepare,
+ .start_streaming = mxc_jpeg_start_streaming,
+ .stop_streaming = mxc_jpeg_stop_streaming,
+ .buf_queue = mxc_jpeg_buf_queue,
+};
+
+static int mxc_jpeg_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct mxc_jpeg_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct mxc_jpeg_src_buf);
+ src_vq->ops = &mxc_jpeg_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->mxc_jpeg->lock;
+ src_vq->dev = ctx->mxc_jpeg->dev;
+ src_vq->allow_zero_bytesused = 1; /* keep old userspace apps working */
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &mxc_jpeg_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->mxc_jpeg->lock;
+ dst_vq->dev = ctx->mxc_jpeg->dev;
+
+ ret = vb2_queue_init(dst_vq);
+ return ret;
+}
+
+static void mxc_jpeg_set_default_params(struct mxc_jpeg_ctx *ctx)
+{
+ struct mxc_jpeg_q_data *out_q = &ctx->out_q;
+ struct mxc_jpeg_q_data *cap_q = &ctx->cap_q;
+ struct mxc_jpeg_q_data *q[2] = {out_q, cap_q};
+ int i;
+
+ if (ctx->mxc_jpeg->mode == MXC_JPEG_ENCODE) {
+ out_q->fmt = mxc_jpeg_find_format(ctx, MXC_JPEG_DEFAULT_PFMT);
+ cap_q->fmt = mxc_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG);
+ } else {
+ out_q->fmt = mxc_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG);
+ cap_q->fmt = mxc_jpeg_find_format(ctx, MXC_JPEG_DEFAULT_PFMT);
+ }
+
+ for (i = 0; i < 2; i++) {
+ q[i]->w = MXC_JPEG_DEFAULT_WIDTH;
+ q[i]->h = MXC_JPEG_DEFAULT_HEIGHT;
+ q[i]->w_adjusted = MXC_JPEG_DEFAULT_WIDTH;
+ q[i]->h_adjusted = MXC_JPEG_DEFAULT_HEIGHT;
+ mxc_jpeg_bytesperline(q[i], 8);
+ mxc_jpeg_sizeimage(q[i]);
+ }
+}
+
+static int mxc_jpeg_open(struct file *file)
+{
+ struct mxc_jpeg_dev *mxc_jpeg = video_drvdata(file);
+ struct video_device *mxc_vfd = video_devdata(file);
+ struct device *dev = mxc_jpeg->dev;
+ struct mxc_jpeg_ctx *ctx;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&mxc_jpeg->lock)) {
+ ret = -ERESTARTSYS;
+ goto free;
+ }
+
+ v4l2_fh_init(&ctx->fh, mxc_vfd);
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->mxc_jpeg = mxc_jpeg;
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(mxc_jpeg->m2m_dev, ctx,
+ mxc_jpeg_queue_init);
+
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ goto error;
+ }
+
+ mxc_jpeg_set_default_params(ctx);
+ ctx->slot = MXC_MAX_SLOTS; /* slot not allocated yet */
+
+ if (mxc_jpeg->mode == MXC_JPEG_DECODE)
+ dev_dbg(dev, "Opened JPEG decoder instance %p\n", ctx);
+ else
+ dev_dbg(dev, "Opened JPEG encoder instance %p\n", ctx);
+ mutex_unlock(&mxc_jpeg->lock);
+
+ return 0;
+
+error:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ mutex_unlock(&mxc_jpeg->lock);
+free:
+ kfree(ctx);
+ return ret;
+}
+
+static int mxc_jpeg_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct mxc_jpeg_dev *mxc_jpeg = video_drvdata(file);
+
+ strscpy(cap->driver, MXC_JPEG_NAME " codec", sizeof(cap->driver));
+ strscpy(cap->card, MXC_JPEG_NAME " codec", sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(mxc_jpeg->dev));
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int mxc_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
+
+ if (ctx->mxc_jpeg->mode == MXC_JPEG_ENCODE)
+ return enum_fmt(mxc_formats, MXC_JPEG_NUM_FORMATS, f,
+ MXC_JPEG_FMT_TYPE_ENC);
+ else
+ return enum_fmt(mxc_formats, MXC_JPEG_NUM_FORMATS, f,
+ MXC_JPEG_FMT_TYPE_RAW);
+}
+
+static int mxc_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
+
+ if (ctx->mxc_jpeg->mode == MXC_JPEG_DECODE)
+ return enum_fmt(mxc_formats, MXC_JPEG_NUM_FORMATS, f,
+ MXC_JPEG_FMT_TYPE_ENC);
+ else
+ return enum_fmt(mxc_formats, MXC_JPEG_NUM_FORMATS, f,
+ MXC_JPEG_FMT_TYPE_RAW);
+}
+
+static int mxc_jpeg_try_fmt(struct v4l2_format *f, struct mxc_jpeg_fmt *fmt,
+ struct mxc_jpeg_ctx *ctx, int q_type)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *pfmt;
+ u32 w = (pix_mp->width < MXC_JPEG_MAX_WIDTH) ?
+ pix_mp->width : MXC_JPEG_MAX_WIDTH;
+ u32 h = (pix_mp->height < MXC_JPEG_MAX_HEIGHT) ?
+ pix_mp->height : MXC_JPEG_MAX_HEIGHT;
+ int i;
+ struct mxc_jpeg_q_data tmp_q;
+
+ memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved));
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->num_planes = fmt->colplanes;
+ pix_mp->pixelformat = fmt->fourcc;
+
+ /*
+ * use MXC_JPEG_H_ALIGN instead of fmt->v_align, for vertical
+ * alignment, to loosen up the alignment to multiple of 8,
+ * otherwise NV12-1080p fails as 1080 is not a multiple of 16
+ */
+ v4l_bound_align_image(&w,
+ MXC_JPEG_MIN_WIDTH,
+ w, /* adjust downwards*/
+ fmt->h_align,
+ &h,
+ MXC_JPEG_MIN_HEIGHT,
+ h, /* adjust downwards*/
+ MXC_JPEG_H_ALIGN,
+ 0);
+ pix_mp->width = w; /* negotiate the width */
+ pix_mp->height = h; /* negotiate the height */
+
+ /* get user input into the tmp_q */
+ tmp_q.w = w;
+ tmp_q.h = h;
+ tmp_q.fmt = fmt;
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ pfmt = &pix_mp->plane_fmt[i];
+ tmp_q.bytesperline[i] = pfmt->bytesperline;
+ tmp_q.sizeimage[i] = pfmt->sizeimage;
+ }
+
+ /* calculate bytesperline & sizeimage into the tmp_q */
+ mxc_jpeg_bytesperline(&tmp_q, 8);
+ mxc_jpeg_sizeimage(&tmp_q);
+
+ /* adjust user format according to our calculations */
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ pfmt = &pix_mp->plane_fmt[i];
+ memset(pfmt->reserved, 0, sizeof(pfmt->reserved));
+ pfmt->bytesperline = tmp_q.bytesperline[i];
+ pfmt->sizeimage = tmp_q.sizeimage[i];
+ }
+
+ /* fix colorspace information to sRGB for both output & capture */
+ pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ pix_mp->xfer_func = V4L2_XFER_FUNC_SRGB;
+ /*
+ * this hardware does not change the range of the samples
+ * but since inside JPEG the YUV quantization is full-range,
+ * this driver will always use full-range for the raw frames, too
+ */
+ pix_mp->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ return 0;
+}
+
+static int mxc_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ struct device *dev = jpeg->dev;
+ struct mxc_jpeg_fmt *fmt;
+ u32 fourcc = f->fmt.pix_mp.pixelformat;
+
+ int q_type = (jpeg->mode == MXC_JPEG_DECODE) ?
+ MXC_JPEG_FMT_TYPE_RAW : MXC_JPEG_FMT_TYPE_ENC;
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ dev_err(dev, "TRY_FMT with Invalid type: %d\n", f->type);
+ return -EINVAL;
+ }
+
+ fmt = mxc_jpeg_find_format(ctx, fourcc);
+ if (!fmt || fmt->flags != q_type) {
+ dev_warn(dev, "Format not supported: %c%c%c%c, use the default.\n",
+ (fourcc & 0xff),
+ (fourcc >> 8) & 0xff,
+ (fourcc >> 16) & 0xff,
+ (fourcc >> 24) & 0xff);
+ f->fmt.pix_mp.pixelformat = (jpeg->mode == MXC_JPEG_DECODE) ?
+ MXC_JPEG_DEFAULT_PFMT : V4L2_PIX_FMT_JPEG;
+ fmt = mxc_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat);
+ }
+ return mxc_jpeg_try_fmt(f, fmt, ctx, q_type);
+}
+
+static int mxc_jpeg_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ struct device *dev = jpeg->dev;
+ struct mxc_jpeg_fmt *fmt;
+ u32 fourcc = f->fmt.pix_mp.pixelformat;
+
+ int q_type = (jpeg->mode == MXC_JPEG_ENCODE) ?
+ MXC_JPEG_FMT_TYPE_RAW : MXC_JPEG_FMT_TYPE_ENC;
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ dev_err(dev, "TRY_FMT with Invalid type: %d\n", f->type);
+ return -EINVAL;
+ }
+
+ fmt = mxc_jpeg_find_format(ctx, fourcc);
+ if (!fmt || fmt->flags != q_type) {
+ dev_warn(dev, "Format not supported: %c%c%c%c, use the default.\n",
+ (fourcc & 0xff),
+ (fourcc >> 8) & 0xff,
+ (fourcc >> 16) & 0xff,
+ (fourcc >> 24) & 0xff);
+ f->fmt.pix_mp.pixelformat = (jpeg->mode == MXC_JPEG_ENCODE) ?
+ MXC_JPEG_DEFAULT_PFMT : V4L2_PIX_FMT_JPEG;
+ fmt = mxc_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat);
+ }
+ return mxc_jpeg_try_fmt(f, fmt, ctx, q_type);
+}
+
+static int mxc_jpeg_s_fmt(struct mxc_jpeg_ctx *ctx,
+ struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct mxc_jpeg_q_data *q_data = NULL;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ int i;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = mxc_jpeg_get_q_data(ctx, f->type);
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&jpeg->v4l2_dev, "queue busy\n");
+ return -EBUSY;
+ }
+
+ q_data->fmt = mxc_jpeg_find_format(ctx, pix_mp->pixelformat);
+ q_data->w = pix_mp->width;
+ q_data->h = pix_mp->height;
+
+ q_data->w_adjusted = q_data->w;
+ q_data->h_adjusted = q_data->h;
+ if (jpeg->mode == MXC_JPEG_DECODE) {
+ /*
+ * align up the resolution for CAST IP,
+ * but leave the buffer resolution unchanged
+ */
+ v4l_bound_align_image(&q_data->w_adjusted,
+ q_data->w_adjusted, /* adjust upwards */
+ MXC_JPEG_MAX_WIDTH,
+ q_data->fmt->h_align,
+ &q_data->h_adjusted,
+ q_data->h_adjusted, /* adjust upwards */
+ MXC_JPEG_MAX_HEIGHT,
+ q_data->fmt->v_align,
+ 0);
+ } else {
+ /*
+ * align down the resolution for CAST IP,
+ * but leave the buffer resolution unchanged
+ */
+ v4l_bound_align_image(&q_data->w_adjusted,
+ MXC_JPEG_MIN_WIDTH,
+ q_data->w_adjusted, /* adjust downwards*/
+ q_data->fmt->h_align,
+ &q_data->h_adjusted,
+ MXC_JPEG_MIN_HEIGHT,
+ q_data->h_adjusted, /* adjust downwards*/
+ q_data->fmt->v_align,
+ 0);
+ }
+
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ q_data->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
+ q_data->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static int mxc_jpeg_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = mxc_jpeg_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return mxc_jpeg_s_fmt(mxc_jpeg_fh_to_ctx(priv), f);
+}
+
+static int mxc_jpeg_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = mxc_jpeg_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return mxc_jpeg_s_fmt(mxc_jpeg_fh_to_ctx(priv), f);
+}
+
+static int mxc_jpeg_g_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ struct device *dev = jpeg->dev;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct mxc_jpeg_q_data *q_data = mxc_jpeg_get_q_data(ctx, f->type);
+ int i;
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ dev_err(dev, "G_FMT with Invalid type: %d\n", f->type);
+ return -EINVAL;
+ }
+
+ pix_mp->pixelformat = q_data->fmt->fourcc;
+ pix_mp->width = q_data->w;
+ pix_mp->height = q_data->h;
+ pix_mp->field = V4L2_FIELD_NONE;
+
+ /* fix colorspace information to sRGB for both output & capture */
+ pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ pix_mp->xfer_func = V4L2_XFER_FUNC_SRGB;
+ pix_mp->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ pix_mp->num_planes = q_data->fmt->colplanes;
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ pix_mp->plane_fmt[i].bytesperline = q_data->bytesperline[i];
+ pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i];
+ }
+
+ return 0;
+}
+
+static int mxc_jpeg_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mxc_jpeg_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(priv);
+ struct device *dev = ctx->mxc_jpeg->dev;
+ int num_src_ready = v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx);
+ int ret;
+
+ dev_dbg(dev, "DQBUF type=%d, index=%d", buf->type, buf->index);
+ if (ctx->stopping == 1 && num_src_ready == 0) {
+ /* No more src bufs, notify app EOS */
+ notify_eos(ctx);
+ ctx->stopping = 0;
+ }
+
+ ret = v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf);
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops mxc_jpeg_ioctl_ops = {
+ .vidioc_querycap = mxc_jpeg_querycap,
+ .vidioc_enum_fmt_vid_cap = mxc_jpeg_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = mxc_jpeg_enum_fmt_vid_out,
+
+ .vidioc_try_fmt_vid_cap_mplane = mxc_jpeg_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out_mplane = mxc_jpeg_try_fmt_vid_out,
+
+ .vidioc_s_fmt_vid_cap_mplane = mxc_jpeg_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out_mplane = mxc_jpeg_s_fmt_vid_out,
+
+ .vidioc_g_fmt_vid_cap_mplane = mxc_jpeg_g_fmt_vid,
+ .vidioc_g_fmt_vid_out_mplane = mxc_jpeg_g_fmt_vid,
+
+ .vidioc_subscribe_event = mxc_jpeg_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
+ .vidioc_decoder_cmd = mxc_jpeg_decoder_cmd,
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+ .vidioc_encoder_cmd = mxc_jpeg_encoder_cmd,
+
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = mxc_jpeg_dqbuf,
+
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+};
+
+static int mxc_jpeg_release(struct file *file)
+{
+ struct mxc_jpeg_dev *mxc_jpeg = video_drvdata(file);
+ struct mxc_jpeg_ctx *ctx = mxc_jpeg_fh_to_ctx(file->private_data);
+ struct device *dev = mxc_jpeg->dev;
+
+ mutex_lock(&mxc_jpeg->lock);
+ if (mxc_jpeg->mode == MXC_JPEG_DECODE)
+ dev_dbg(dev, "Release JPEG decoder instance on slot %d.",
+ ctx->slot);
+ else
+ dev_dbg(dev, "Release JPEG encoder instance on slot %d.",
+ ctx->slot);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mutex_unlock(&mxc_jpeg->lock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations mxc_jpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_jpeg_open,
+ .release = mxc_jpeg_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static struct v4l2_m2m_ops mxc_jpeg_m2m_ops = {
+ .device_run = mxc_jpeg_device_run,
+};
+
+static void mxc_jpeg_detach_pm_domains(struct mxc_jpeg_dev *jpeg)
+{
+ int i;
+
+ for (i = 0; i < jpeg->num_domains; i++) {
+ if (jpeg->pd_link[i] && !IS_ERR(jpeg->pd_link[i]))
+ device_link_del(jpeg->pd_link[i]);
+ if (jpeg->pd_dev[i] && !IS_ERR(jpeg->pd_dev[i]))
+ dev_pm_domain_detach(jpeg->pd_dev[i], true);
+ jpeg->pd_dev[i] = NULL;
+ jpeg->pd_link[i] = NULL;
+ }
+}
+
+static int mxc_jpeg_attach_pm_domains(struct mxc_jpeg_dev *jpeg)
+{
+ struct device *dev = jpeg->dev;
+ struct device_node *np = jpeg->pdev->dev.of_node;
+ int i;
+ int ret;
+
+ jpeg->num_domains = of_count_phandle_with_args(np, "power-domains",
+ "#power-domain-cells");
+ if (jpeg->num_domains < 0) {
+ dev_err(dev, "No power domains defined for jpeg node\n");
+ return jpeg->num_domains;
+ }
+
+ jpeg->pd_dev = devm_kmalloc_array(dev, jpeg->num_domains,
+ sizeof(*jpeg->pd_dev), GFP_KERNEL);
+ if (!jpeg->pd_dev)
+ return -ENOMEM;
+
+ jpeg->pd_link = devm_kmalloc_array(dev, jpeg->num_domains,
+ sizeof(*jpeg->pd_link), GFP_KERNEL);
+ if (!jpeg->pd_link)
+ return -ENOMEM;
+
+ for (i = 0; i < jpeg->num_domains; i++) {
+ jpeg->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
+ if (IS_ERR(jpeg->pd_dev[i])) {
+ ret = PTR_ERR(jpeg->pd_dev[i]);
+ goto fail;
+ }
+
+ jpeg->pd_link[i] = device_link_add(dev, jpeg->pd_dev[i],
+ DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!jpeg->pd_link[i]) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ return 0;
+fail:
+ mxc_jpeg_detach_pm_domains(jpeg);
+ return ret;
+}
+
+static int mxc_jpeg_probe(struct platform_device *pdev)
+{
+ struct mxc_jpeg_dev *jpeg;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int dec_irq;
+ int ret;
+ int mode;
+ const struct of_device_id *of_id;
+ unsigned int slot;
+
+ of_id = of_match_node(mxc_jpeg_match, dev->of_node);
+ mode = *(const int *)of_id->data;
+
+ jpeg = devm_kzalloc(dev, sizeof(struct mxc_jpeg_dev), GFP_KERNEL);
+ if (!jpeg)
+ return -ENOMEM;
+
+ mutex_init(&jpeg->lock);
+ spin_lock_init(&jpeg->hw_lock);
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "No suitable DMA available.\n");
+ goto err_irq;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ jpeg->base_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(jpeg->base_reg))
+ return PTR_ERR(jpeg->base_reg);
+
+ for (slot = 0; slot < MXC_MAX_SLOTS; slot++) {
+ dec_irq = platform_get_irq(pdev, slot);
+ if (dec_irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq %d\n", dec_irq);
+ ret = dec_irq;
+ goto err_irq;
+ }
+ ret = devm_request_irq(&pdev->dev, dec_irq, mxc_jpeg_dec_irq,
+ 0, pdev->name, jpeg);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq %d (%d)\n",
+ dec_irq, ret);
+ goto err_irq;
+ }
+ }
+
+ jpeg->pdev = pdev;
+ jpeg->dev = dev;
+ jpeg->mode = mode;
+
+ ret = mxc_jpeg_attach_pm_domains(jpeg);
+ if (ret < 0) {
+ dev_err(dev, "failed to attach power domains %d\n", ret);
+ return ret;
+ }
+
+ /* v4l2 */
+ ret = v4l2_device_register(dev, &jpeg->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "failed to register v4l2 device\n");
+ goto err_register;
+ }
+ jpeg->m2m_dev = v4l2_m2m_init(&mxc_jpeg_m2m_ops);
+ if (IS_ERR(jpeg->m2m_dev)) {
+ dev_err(dev, "failed to register v4l2 device\n");
+ ret = PTR_ERR(jpeg->m2m_dev);
+ goto err_m2m;
+ }
+
+ jpeg->dec_vdev = video_device_alloc();
+ if (!jpeg->dec_vdev) {
+ dev_err(dev, "failed to register v4l2 device\n");
+ ret = -ENOMEM;
+ goto err_vdev_alloc;
+ }
+ if (mode == MXC_JPEG_ENCODE)
+ snprintf(jpeg->dec_vdev->name,
+ sizeof(jpeg->dec_vdev->name),
+ "%s-enc", MXC_JPEG_NAME);
+ else
+ snprintf(jpeg->dec_vdev->name,
+ sizeof(jpeg->dec_vdev->name),
+ "%s-dec", MXC_JPEG_NAME);
+
+ jpeg->dec_vdev->fops = &mxc_jpeg_fops;
+ jpeg->dec_vdev->ioctl_ops = &mxc_jpeg_ioctl_ops;
+ jpeg->dec_vdev->minor = -1;
+ jpeg->dec_vdev->release = video_device_release;
+ jpeg->dec_vdev->lock = &jpeg->lock; /* lock for ioctl serialization */
+ jpeg->dec_vdev->v4l2_dev = &jpeg->v4l2_dev;
+ jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M;
+ jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_M2M_MPLANE;
+ if (mode == MXC_JPEG_ENCODE) {
+ v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_DECODER_CMD);
+ v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_TRY_DECODER_CMD);
+ } else {
+ v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_ENCODER_CMD);
+ v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_TRY_ENCODER_CMD);
+ }
+ ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(dev, "failed to register video device\n");
+ goto err_vdev_register;
+ }
+ video_set_drvdata(jpeg->dec_vdev, jpeg);
+ if (mode == MXC_JPEG_ENCODE)
+ v4l2_info(&jpeg->v4l2_dev,
+ "encoder device registered as /dev/video%d (%d,%d)\n",
+ jpeg->dec_vdev->num, VIDEO_MAJOR,
+ jpeg->dec_vdev->minor);
+ else
+ v4l2_info(&jpeg->v4l2_dev,
+ "decoder device registered as /dev/video%d (%d,%d)\n",
+ jpeg->dec_vdev->num, VIDEO_MAJOR,
+ jpeg->dec_vdev->minor);
+
+ platform_set_drvdata(pdev, jpeg);
+
+ return 0;
+
+err_vdev_register:
+ video_device_release(jpeg->dec_vdev);
+
+err_vdev_alloc:
+ v4l2_m2m_release(jpeg->m2m_dev);
+
+err_m2m:
+ v4l2_device_unregister(&jpeg->v4l2_dev);
+
+err_register:
+err_irq:
+ return ret;
+}
+
+static int mxc_jpeg_remove(struct platform_device *pdev)
+{
+ unsigned int slot;
+ struct mxc_jpeg_dev *jpeg = platform_get_drvdata(pdev);
+
+ for (slot = 0; slot < MXC_MAX_SLOTS; slot++)
+ mxc_jpeg_free_slot_data(jpeg, slot);
+
+ video_unregister_device(jpeg->dec_vdev);
+ v4l2_m2m_release(jpeg->m2m_dev);
+ v4l2_device_unregister(&jpeg->v4l2_dev);
+ mxc_jpeg_detach_pm_domains(jpeg);
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(of, mxc_jpeg_match);
+
+static struct platform_driver mxc_jpeg_driver = {
+ .probe = mxc_jpeg_probe,
+ .remove = mxc_jpeg_remove,
+ .driver = {
+ .name = "mxc-jpeg",
+ .of_match_table = mxc_jpeg_match,
+ },
+};
+module_platform_driver(mxc_jpeg_driver);
+
+MODULE_AUTHOR("Zhengyu Shen <zhengyu.shen_1@nxp.com>");
+MODULE_AUTHOR("Mirela Rabulea <mirela.rabulea@nxp.com>");
+MODULE_DESCRIPTION("V4L2 driver for i.MX8 QXP/QM JPEG encoder/decoder");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/imx-jpeg/mxc-jpeg.h
new file mode 100644
index 000000000000..7697de490d2e
--- /dev/null
+++ b/drivers/media/platform/imx-jpeg/mxc-jpeg.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * i.MX8QXP/i.MX8QM JPEG encoder/decoder v4l2 driver
+ *
+ * Copyright 2018-2019 NXP
+ */
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+
+#ifndef _MXC_JPEG_CORE_H
+#define _MXC_JPEG_CORE_H
+
+#define MXC_JPEG_NAME "mxc-jpeg"
+#define MXC_JPEG_FMT_TYPE_ENC 0
+#define MXC_JPEG_FMT_TYPE_RAW 1
+#define MXC_JPEG_DEFAULT_WIDTH 1280
+#define MXC_JPEG_DEFAULT_HEIGHT 720
+#define MXC_JPEG_DEFAULT_PFMT V4L2_PIX_FMT_RGB24
+#define MXC_JPEG_MIN_WIDTH 64
+#define MXC_JPEG_MIN_HEIGHT 64
+#define MXC_JPEG_MAX_WIDTH 0x2000
+#define MXC_JPEG_MAX_HEIGHT 0x2000
+#define MXC_JPEG_MAX_CFG_STREAM 0x1000
+#define MXC_JPEG_H_ALIGN 3
+#define MXC_JPEG_W_ALIGN 3
+#define MXC_JPEG_MAX_SIZEIMAGE 0xFFFFFC00
+#define MXC_JPEG_MAX_PLANES 2
+
+enum mxc_jpeg_enc_state {
+ MXC_JPEG_ENCODING = 0, /* jpeg encode phase */
+ MXC_JPEG_ENC_CONF = 1, /* jpeg encoder config phase */
+};
+
+enum mxc_jpeg_mode {
+ MXC_JPEG_DECODE = 0, /* jpeg decode mode */
+ MXC_JPEG_ENCODE = 1, /* jpeg encode mode */
+};
+
+/**
+ * struct mxc_jpeg_fmt - driver's internal color format data
+ * @name: format description
+ * @fourcc: fourcc code, 0 if not applicable
+ * @subsampling: subsampling of jpeg components
+ * @nc: number of color components
+ * @depth: number of bits per pixel
+ * @colplanes: number of color planes (1 for packed formats)
+ * @h_align: horizontal alignment order (align to 2^h_align)
+ * @v_align: vertical alignment order (align to 2^v_align)
+ * @flags: flags describing format applicability
+ */
+struct mxc_jpeg_fmt {
+ char *name;
+ u32 fourcc;
+ enum v4l2_jpeg_chroma_subsampling subsampling;
+ int nc;
+ int depth;
+ int colplanes;
+ int h_align;
+ int v_align;
+ u32 flags;
+};
+
+struct mxc_jpeg_desc {
+ u32 next_descpt_ptr;
+ u32 buf_base0;
+ u32 buf_base1;
+ u32 line_pitch;
+ u32 stm_bufbase;
+ u32 stm_bufsize;
+ u32 imgsize;
+ u32 stm_ctrl;
+} __packed;
+
+struct mxc_jpeg_q_data {
+ struct mxc_jpeg_fmt *fmt;
+ u32 sizeimage[MXC_JPEG_MAX_PLANES];
+ u32 bytesperline[MXC_JPEG_MAX_PLANES];
+ int w;
+ int w_adjusted;
+ int h;
+ int h_adjusted;
+ unsigned int sequence;
+};
+
+struct mxc_jpeg_ctx {
+ struct mxc_jpeg_dev *mxc_jpeg;
+ struct mxc_jpeg_q_data out_q;
+ struct mxc_jpeg_q_data cap_q;
+ struct v4l2_fh fh;
+ enum mxc_jpeg_enc_state enc_state;
+ unsigned int stopping;
+ unsigned int slot;
+};
+
+struct mxc_jpeg_slot_data {
+ bool used;
+ struct mxc_jpeg_desc *desc; // enc/dec descriptor
+ struct mxc_jpeg_desc *cfg_desc; // configuration descriptor
+ void *cfg_stream_vaddr; // configuration bitstream virtual address
+ unsigned int cfg_stream_size;
+ dma_addr_t desc_handle;
+ dma_addr_t cfg_desc_handle; // configuration descriptor dma address
+ dma_addr_t cfg_stream_handle; // configuration bitstream dma address
+};
+
+struct mxc_jpeg_dev {
+ spinlock_t hw_lock; /* hardware access lock */
+ unsigned int mode;
+ struct mutex lock; /* v4l2 ioctls serialization */
+ struct platform_device *pdev;
+ struct device *dev;
+ void __iomem *base_reg;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct video_device *dec_vdev;
+ struct mxc_jpeg_slot_data slot_data[MXC_MAX_SLOTS];
+ int num_domains;
+ struct device **pd_dev;
+ struct device_link **pd_link;
+};
+
+/**
+ * struct mxc_jpeg_sof_comp - JPEG Start Of Frame component fields
+ * @id: component id
+ * @v: vertical sampling
+ * @h: horizontal sampling
+ * @quantization_table_no: id of quantization table
+ */
+struct mxc_jpeg_sof_comp {
+ u8 id;
+ u8 v :4;
+ u8 h :4;
+ u8 quantization_table_no;
+} __packed;
+
+#define MXC_JPEG_MAX_COMPONENTS 4
+/**
+ * struct mxc_jpeg_sof - JPEG Start Of Frame marker fields
+ * @length: Start of Frame length
+ * @precision: precision (bits per pixel per color component)
+ * @height: image height
+ * @width: image width
+ * @components_no: number of color components
+ * @comp: component fields for each color component
+ */
+struct mxc_jpeg_sof {
+ u16 length;
+ u8 precision;
+ u16 height, width;
+ u8 components_no;
+ struct mxc_jpeg_sof_comp comp[MXC_JPEG_MAX_COMPONENTS];
+} __packed;
+
+/**
+ * struct mxc_jpeg_sos_comp - JPEG Start Of Scan component fields
+ * @id: component id
+ * @huffman_table_no: id of the Huffman table
+ */
+struct mxc_jpeg_sos_comp {
+ u8 id; /*component id*/
+ u8 huffman_table_no;
+} __packed;
+
+/**
+ * struct mxc_jpeg_sos - JPEG Start Of Scan marker fields
+ * @length: Start of Frame length
+ * @components_no: number of color components
+ * @comp: SOS component fields for each color component
+ * @ignorable_bytes: ignorable bytes
+ */
+struct mxc_jpeg_sos {
+ u16 length;
+ u8 components_no;
+ struct mxc_jpeg_sos_comp comp[MXC_JPEG_MAX_COMPONENTS];
+ u8 ignorable_bytes[3];
+} __packed;
+
+#endif
diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c
index 08d76eb05ed1..4321edc0c23d 100644
--- a/drivers/media/platform/imx-pxp.c
+++ b/drivers/media/platform/imx-pxp.c
@@ -1654,11 +1654,8 @@ static int pxp_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->mmio = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(dev->mmio)) {
- ret = PTR_ERR(dev->mmio);
- dev_err(&pdev->dev, "Failed to map register space: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(dev->mmio))
+ return PTR_ERR(dev->mmio);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -1755,7 +1752,7 @@ static struct platform_driver pxp_driver = {
.remove = pxp_remove,
.driver = {
.name = MEM2MEM_NAME,
- .of_match_table = of_match_ptr(pxp_dt_ids),
+ .of_match_table = pxp_dt_ids,
},
};
diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/meson/ge2d/ge2d.c
index 153612ca96fc..a1393fefa8ae 100644
--- a/drivers/media/platform/meson/ge2d/ge2d.c
+++ b/drivers/media/platform/meson/ge2d/ge2d.c
@@ -757,7 +757,7 @@ static int ge2d_s_ctrl(struct v4l2_ctrl *ctrl)
if (ctrl->val == 90) {
ctx->hflip = 0;
- ctx->vflip = 0;
+ ctx->vflip = 1;
ctx->xy_swap = 1;
} else if (ctrl->val == 180) {
ctx->hflip = 1;
@@ -765,7 +765,7 @@ static int ge2d_s_ctrl(struct v4l2_ctrl *ctrl)
ctx->xy_swap = 0;
} else if (ctrl->val == 270) {
ctx->hflip = 1;
- ctx->vflip = 1;
+ ctx->vflip = 0;
ctx->xy_swap = 1;
} else {
ctx->hflip = 0;
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
index 68e634f02e00..595f7f10c9fd 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
@@ -45,11 +45,11 @@ enum mtk_jpeg_ctx_state {
};
/**
- * mtk_jpeg_variant - mtk jpeg driver variant
+ * struct mtk_jpeg_variant - mtk jpeg driver variant
* @clks: clock names
* @num_clks: numbers of clock
- * @format: jpeg driver's internal color format
- * @num_format: number of format
+ * @formats: jpeg driver's internal color format
+ * @num_formats: number of formats
* @qops: the callback of jpeg vb2_ops
* @irq_handler: jpeg irq handler callback
* @hw_reset: jpeg hardware reset callback
@@ -75,7 +75,7 @@ struct mtk_jpeg_variant {
};
/**
- * struct mt_jpeg - JPEG IP abstraction
+ * struct mtk_jpeg_dev - JPEG IP abstraction
* @lock: the mutex protecting this structure
* @hw_lock: spinlock protecting the hw device resource
* @workqueue: decode work queue
@@ -105,7 +105,7 @@ struct mtk_jpeg_dev {
};
/**
- * struct jpeg_fmt - driver's internal color format data
+ * struct mtk_jpeg_fmt - driver's internal color format data
* @fourcc: the fourcc code, 0 if not applicable
* @hw_format: hardware format value
* @h_sample: horizontal sample count of plane in 4 * 4 pixel image
@@ -127,7 +127,7 @@ struct mtk_jpeg_fmt {
};
/**
- * mtk_jpeg_q_data - parameters of one queue
+ * struct mtk_jpeg_q_data - parameters of one queue
* @fmt: driver-specific format of this queue
* @pix_mp: multiplanar format
* @enc_crop_rect: jpeg encoder crop information
@@ -139,7 +139,7 @@ struct mtk_jpeg_q_data {
};
/**
- * mtk_jpeg_ctx - the device context data
+ * struct mtk_jpeg_ctx - the device context data
* @jpeg: JPEG IP device for this context
* @out_q: source (output) queue information
* @cap_q: destination (capture) queue queue information
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
index 1bf0242cce46..7897766c96bb 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
@@ -19,7 +19,6 @@ enum mtk_mdp_comp_type {
MTK_MDP_RSZ,
MTK_MDP_WDMA,
MTK_MDP_WROT,
- MTK_MDP_COMP_TYPE_MAX,
};
/**
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
index a7da14b97077..a6e6dc36307b 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
+++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
@@ -52,8 +52,8 @@ struct mtk_mdp_pix_align {
* @depth: per plane driver's private 'number of bits per pixel'
* @row_depth: per plane driver's private 'number of bits per pixel per row'
* @flags: flags indicating which operation mode format applies to
- MTK_MDP_FMT_FLAG_OUTPUT is used in OUTPUT stream
- MTK_MDP_FMT_FLAG_CAPTURE is used in CAPTURE stream
+ * MTK_MDP_FMT_FLAG_OUTPUT is used in OUTPUT stream
+ * MTK_MDP_FMT_FLAG_CAPTURE is used in CAPTURE stream
* @align: pointer to a pixel alignment struct, NULL if using default value
*/
struct mtk_mdp_fmt {
@@ -168,14 +168,14 @@ struct mtk_mdp_dev {
};
/**
- * mtk_mdp_ctx - the device context data
+ * struct mtk_mdp_ctx - the device context data
* @list: link to ctx_list of mtk_mdp_dev
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @id: index of the context that this structure describes
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
- Protected by slock
+ * Protected by slock
* @rotation: rotates the image by specified angle
* @hflip: mirror the picture horizontally
* @vflip: mirror the picture vertically
@@ -183,7 +183,7 @@ struct mtk_mdp_dev {
* @m2m_ctx: memory-to-memory device context
* @fh: v4l2 file handle
* @ctrl_handler: v4l2 controls handler
- * @ctrls image processor control set
+ * @ctrls: image processor control set
* @ctrls_rdy: true if the control handler is initialized
* @colorspace: enum v4l2_colorspace; supplemental to pixelformat
* @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
index 3dd010cba23e..d03cca95e99b 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
@@ -59,12 +59,12 @@ enum mtk_instance_type {
/**
* enum mtk_instance_state - The state of an MTK Vcodec instance.
- * @MTK_STATE_FREE - default state when instance is created
- * @MTK_STATE_INIT - vcodec instance is initialized
- * @MTK_STATE_HEADER - vdec had sps/pps header parsed or venc
+ * @MTK_STATE_FREE: default state when instance is created
+ * @MTK_STATE_INIT: vcodec instance is initialized
+ * @MTK_STATE_HEADER: vdec had sps/pps header parsed or venc
* had sps/pps header encoded
- * @MTK_STATE_FLUSH - vdec is flushing. Only used by decoder
- * @MTK_STATE_ABORT - vcodec should be aborted
+ * @MTK_STATE_FLUSH: vdec is flushing. Only used by decoder
+ * @MTK_STATE_ABORT: vcodec should be aborted
*/
enum mtk_instance_state {
MTK_STATE_FREE = 0,
@@ -75,7 +75,7 @@ enum mtk_instance_state {
};
/**
- * struct mtk_encode_param - General encoding parameters type
+ * enum mtk_encode_param - General encoding parameters type
*/
enum mtk_encode_param {
MTK_ENCODE_PARAM_NONE = 0,
@@ -112,7 +112,7 @@ struct mtk_codec_framesizes {
};
/**
- * struct mtk_q_type - Type of queue
+ * enum mtk_q_type - Type of queue
*/
enum mtk_q_type {
MTK_Q_DATA_SRC = 0,
@@ -193,7 +193,6 @@ struct mtk_vcodec_pm {
struct mtk_vcodec_clk venc_clk;
struct device *larbvenc;
- struct device *larbvenclt;
struct device *dev;
struct mtk_vcodec_dev *mtkdev;
};
@@ -311,25 +310,25 @@ enum mtk_chip {
* @chip: chip this encoder is compatible with
*
* @uses_ext: whether the encoder uses the extended firmware messaging format
- * @has_lt_irq: whether the encoder uses the LT irq
* @min_birate: minimum supported encoding bitrate
* @max_bitrate: maximum supported encoding bitrate
* @capture_formats: array of supported capture formats
* @num_capture_formats: number of entries in capture_formats
* @output_formats: array of supported output formats
* @num_output_formats: number of entries in output_formats
+ * @core_id: stand for h264 or vp8 encode index
*/
struct mtk_vcodec_enc_pdata {
enum mtk_chip chip;
bool uses_ext;
- bool has_lt_irq;
unsigned long min_bitrate;
unsigned long max_bitrate;
const struct mtk_video_fmt *capture_formats;
size_t num_capture_formats;
const struct mtk_video_fmt *output_formats;
size_t num_output_formats;
+ int core_id;
};
#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext)
@@ -361,7 +360,6 @@ struct mtk_vcodec_enc_pdata {
*
* @dec_irq: decoder irq resource
* @enc_irq: h264 encoder irq resource
- * @enc_lt_irq: vp8 encoder irq resource
*
* @dec_mutex: decoder hardware lock
* @enc_mutex: encoder hardware lock.
@@ -397,7 +395,6 @@ struct mtk_vcodec_dev {
int dec_irq;
int enc_irq;
- int enc_lt_irq;
struct mutex dec_mutex;
struct mutex enc_mutex;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
index 8c917969c2f1..4831052f475d 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
@@ -9,6 +9,7 @@
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
#include <soc/mediatek/smi.h>
+#include <linux/pm_runtime.h>
#include "mtk_vcodec_drv.h"
#include "mtk_vcodec_enc.h"
@@ -787,7 +788,7 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count)
*/
if ((ctx->state == MTK_STATE_ABORT) || (ctx->state == MTK_STATE_FREE)) {
ret = -EIO;
- goto err_set_param;
+ goto err_start_stream;
}
/* Do the initialization when both start_streaming have been called */
@@ -799,6 +800,12 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
}
+ ret = pm_runtime_resume_and_get(&ctx->dev->plat_dev->dev);
+ if (ret < 0) {
+ mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret);
+ goto err_start_stream;
+ }
+
mtk_venc_set_param(ctx, &param);
ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, &param);
if (ret) {
@@ -825,6 +832,11 @@ static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
err_set_param:
+ ret = pm_runtime_put(&ctx->dev->plat_dev->dev);
+ if (ret < 0)
+ mtk_v4l2_err("pm_runtime_put fail %d", ret);
+
+err_start_stream:
for (i = 0; i < q->num_buffers; ++i) {
struct vb2_buffer *buf = vb2_get_buffer(q, i);
@@ -878,6 +890,10 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q)
if (ret)
mtk_v4l2_err("venc_if_deinit failed=%d", ret);
+ ret = pm_runtime_put(&ctx->dev->plat_dev->dev);
+ if (ret < 0)
+ mtk_v4l2_err("pm_runtime_put fail %d", ret);
+
ctx->state = MTK_STATE_FREE;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
index be3842e6ca47..7d7b8cfc2cc5 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
@@ -49,12 +49,15 @@ static const struct mtk_video_fmt mtk_video_formats_output_mt8173[] = {
},
};
-static const struct mtk_video_fmt mtk_video_formats_capture_mt8173[] = {
+static const struct mtk_video_fmt mtk_video_formats_capture_mt8173_avc[] = {
{
.fourcc = V4L2_PIX_FMT_H264,
.type = MTK_FMT_ENC,
.num_planes = 1,
},
+};
+
+static const struct mtk_video_fmt mtk_video_formats_capture_mt8173_vp8[] = {
{
.fourcc = V4L2_PIX_FMT_VP8,
.type = MTK_FMT_ENC,
@@ -110,10 +113,11 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv)
ctx = dev->curr_ctx;
spin_unlock_irqrestore(&dev->irqlock, flags);
- mtk_v4l2_debug(1, "id=%d", ctx->id);
- addr = dev->reg_base[VENC_SYS] + MTK_VENC_IRQ_ACK_OFFSET;
+ mtk_v4l2_debug(1, "id=%d coreid:%d", ctx->id, dev->venc_pdata->core_id);
+ addr = dev->reg_base[dev->venc_pdata->core_id] +
+ MTK_VENC_IRQ_ACK_OFFSET;
- ctx->irq_status = readl(dev->reg_base[VENC_SYS] +
+ ctx->irq_status = readl(dev->reg_base[dev->venc_pdata->core_id] +
(MTK_VENC_IRQ_STATUS_OFFSET));
clean_irq_status(ctx->irq_status, addr);
@@ -122,29 +126,6 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv)
return IRQ_HANDLED;
}
-static irqreturn_t mtk_vcodec_enc_lt_irq_handler(int irq, void *priv)
-{
- struct mtk_vcodec_dev *dev = priv;
- struct mtk_vcodec_ctx *ctx;
- unsigned long flags;
- void __iomem *addr;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- ctx = dev->curr_ctx;
- spin_unlock_irqrestore(&dev->irqlock, flags);
-
- mtk_v4l2_debug(1, "id=%d", ctx->id);
- ctx->irq_status = readl(dev->reg_base[VENC_LT_SYS] +
- (MTK_VENC_IRQ_STATUS_OFFSET));
-
- addr = dev->reg_base[VENC_LT_SYS] + MTK_VENC_IRQ_ACK_OFFSET;
-
- clean_irq_status(ctx->irq_status, addr);
-
- wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED);
- return IRQ_HANDLED;
-}
-
static int fops_vcodec_open(struct file *file)
{
struct mtk_vcodec_dev *dev = video_drvdata(file);
@@ -293,17 +274,18 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
dev->venc_pdata = of_device_get_match_data(&pdev->dev);
ret = mtk_vcodec_init_enc_pm(dev);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to get mt vcodec clock source!");
+ dev_err(&pdev->dev, "Failed to get mtk vcodec clock source!");
goto err_enc_pm;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->reg_base[VENC_SYS] = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR((__force void *)dev->reg_base[VENC_SYS])) {
- ret = PTR_ERR((__force void *)dev->reg_base[VENC_SYS]);
+ pm_runtime_enable(&pdev->dev);
+
+ dev->reg_base[dev->venc_pdata->core_id] =
+ devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dev->reg_base[dev->venc_pdata->core_id])) {
+ ret = PTR_ERR(dev->reg_base[dev->venc_pdata->core_id]);
goto err_res;
}
- mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_SYS, dev->reg_base[VENC_SYS]);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
@@ -318,37 +300,13 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
mtk_vcodec_enc_irq_handler,
0, pdev->name, dev);
if (ret) {
- dev_err(&pdev->dev, "Failed to install dev->enc_irq %d (%d)",
- dev->enc_irq,
- ret);
+ dev_err(&pdev->dev,
+ "Failed to install dev->enc_irq %d (%d) core_id (%d)",
+ dev->enc_irq, ret, dev->venc_pdata->core_id);
ret = -EINVAL;
goto err_res;
}
- if (dev->venc_pdata->has_lt_irq) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- dev->reg_base[VENC_LT_SYS] = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR((__force void *)dev->reg_base[VENC_LT_SYS])) {
- ret = PTR_ERR((__force void *)dev->reg_base[VENC_LT_SYS]);
- goto err_res;
- }
- mtk_v4l2_debug(2, "reg[%d] base=0x%p", VENC_LT_SYS, dev->reg_base[VENC_LT_SYS]);
-
- dev->enc_lt_irq = platform_get_irq(pdev, 1);
- irq_set_status_flags(dev->enc_lt_irq, IRQ_NOAUTOEN);
- ret = devm_request_irq(&pdev->dev,
- dev->enc_lt_irq,
- mtk_vcodec_enc_lt_irq_handler,
- 0, pdev->name, dev);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to install dev->enc_lt_irq %d (%d)",
- dev->enc_lt_irq, ret);
- ret = -EINVAL;
- goto err_res;
- }
- }
-
mutex_init(&dev->enc_mutex);
mutex_init(&dev->dev_mutex);
spin_lock_init(&dev->irqlock);
@@ -409,8 +367,8 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
goto err_enc_reg;
}
- mtk_v4l2_debug(0, "encoder registered as /dev/video%d",
- vfd_enc->num);
+ mtk_v4l2_debug(0, "encoder %d registered as /dev/video%d",
+ dev->venc_pdata->core_id, vfd_enc->num);
return 0;
@@ -429,20 +387,30 @@ err_enc_pm:
return ret;
}
-static const struct mtk_vcodec_enc_pdata mt8173_pdata = {
+static const struct mtk_vcodec_enc_pdata mt8173_avc_pdata = {
.chip = MTK_MT8173,
- .has_lt_irq = true,
- .capture_formats = mtk_video_formats_capture_mt8173,
- .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8173),
+ .capture_formats = mtk_video_formats_capture_mt8173_avc,
+ .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8173_avc),
.output_formats = mtk_video_formats_output_mt8173,
.num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173),
.min_bitrate = 1,
.max_bitrate = 4000000,
+ .core_id = VENC_SYS,
+};
+
+static const struct mtk_vcodec_enc_pdata mt8173_vp8_pdata = {
+ .chip = MTK_MT8173,
+ .capture_formats = mtk_video_formats_capture_mt8173_vp8,
+ .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8173_vp8),
+ .output_formats = mtk_video_formats_output_mt8173,
+ .num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173),
+ .min_bitrate = 64,
+ .max_bitrate = 4000000,
+ .core_id = VENC_LT_SYS,
};
static const struct mtk_vcodec_enc_pdata mt8183_pdata = {
.chip = MTK_MT8183,
- .has_lt_irq = false,
.uses_ext = true,
.capture_formats = mtk_video_formats_capture_mt8183,
.num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8183),
@@ -451,10 +419,14 @@ static const struct mtk_vcodec_enc_pdata mt8183_pdata = {
.num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173),
.min_bitrate = 64,
.max_bitrate = 40000000,
+ .core_id = VENC_SYS,
};
static const struct of_device_id mtk_vcodec_enc_match[] = {
- {.compatible = "mediatek,mt8173-vcodec-enc", .data = &mt8173_pdata},
+ {.compatible = "mediatek,mt8173-vcodec-enc",
+ .data = &mt8173_avc_pdata},
+ {.compatible = "mediatek,mt8173-vcodec-enc-vp8",
+ .data = &mt8173_vp8_pdata},
{.compatible = "mediatek,mt8183-vcodec-enc", .data = &mt8183_pdata},
{},
};
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
index 3b7c54d6aa8f..1b2e4930ed27 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
@@ -43,23 +43,6 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
return -ENODEV;
}
pm->larbvenc = &pdev->dev;
-
- node = of_parse_phandle(dev->of_node, "mediatek,larb", 1);
- if (!node) {
- mtk_v4l2_err("no mediatek,larb found");
- ret = -ENODEV;
- goto put_larbvenc;
- }
-
- pdev = of_find_device_by_node(node);
- of_node_put(node);
- if (!pdev) {
- mtk_v4l2_err("no mediatek,larb device found");
- ret = -ENODEV;
- goto put_larbvenc;
- }
-
- pm->larbvenclt = &pdev->dev;
pdev = mtkdev->plat_dev;
pm->dev = &pdev->dev;
@@ -71,12 +54,12 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
GFP_KERNEL);
if (!enc_clk->clk_info) {
ret = -ENOMEM;
- goto put_larbvenclt;
+ goto put_larbvenc;
}
} else {
mtk_v4l2_err("Failed to get venc clock count");
ret = -EINVAL;
- goto put_larbvenclt;
+ goto put_larbvenc;
}
for (i = 0; i < enc_clk->clk_num; i++) {
@@ -85,7 +68,7 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
"clock-names", i, &clk_info->clk_name);
if (ret) {
mtk_v4l2_err("venc failed to get clk name %d", i);
- goto put_larbvenclt;
+ goto put_larbvenc;
}
clk_info->vcodec_clk = devm_clk_get(&pdev->dev,
clk_info->clk_name);
@@ -93,14 +76,12 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i,
clk_info->clk_name);
ret = PTR_ERR(clk_info->vcodec_clk);
- goto put_larbvenclt;
+ goto put_larbvenc;
}
}
return 0;
-put_larbvenclt:
- put_device(pm->larbvenclt);
put_larbvenc:
put_device(pm->larbvenc);
return ret;
@@ -108,7 +89,7 @@ put_larbvenc:
void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev)
{
- put_device(mtkdev->pm.larbvenclt);
+ pm_runtime_disable(mtkdev->pm.dev);
put_device(mtkdev->pm.larbvenc);
}
@@ -130,18 +111,10 @@ void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm)
ret = mtk_smi_larb_get(pm->larbvenc);
if (ret) {
mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret);
- goto larbvencerr;
- }
- ret = mtk_smi_larb_get(pm->larbvenclt);
- if (ret) {
- mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret);
- goto larbvenclterr;
+ goto clkerr;
}
return;
-larbvenclterr:
- mtk_smi_larb_put(pm->larbvenc);
-larbvencerr:
clkerr:
for (i -= 1; i >= 0; i--)
clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk);
@@ -153,7 +126,6 @@ void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm)
int i = 0;
mtk_smi_larb_put(pm->larbvenc);
- mtk_smi_larb_put(pm->larbvenclt);
for (i = enc_clk->clk_num - 1; i >= 0; i--)
clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk);
}
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
index d9880210b2ab..71cdc3ddafcb 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
@@ -61,7 +61,7 @@ struct vp9_ref_cnt_buf {
};
/**
- * struct vp9_fb_info - contains current frame's reference buffer information
+ * struct vp9_ref_buf - contains current frame's reference buffer information
* @buf : reference buffer
* @idx : reference buffer index to frm_bufs
* @reserved : reserved field used by vpu
@@ -73,7 +73,7 @@ struct vp9_ref_buf {
};
/**
- * struct vp9_fb_info - contains frame buffer info
+ * struct vp9_sf_ref_fb - contains frame buffer info
* @fb : super frame reference frame buffer
* @used : this reference frame info entry is used
* @padding : for 64 bytes size align
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
index 270d8dc9984b..ec8f4e8d3d23 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
+++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
@@ -14,10 +14,10 @@
/**
- * struct vdec_fb_status - decoder frame buffer status
- * @FB_ST_NORMAL : initial state
- * @FB_ST_DISPLAY : frmae buffer is ready to be displayed
- * @FB_ST_FREE : frame buffer is not used by decoder any more
+ * enum vdec_fb_status - decoder frame buffer status
+ * @FB_ST_NORMAL: initial state
+ * @FB_ST_DISPLAY: frame buffer is ready to be displayed
+ * @FB_ST_FREE: frame buffer is not used by decoder any more
*/
enum vdec_fb_status {
FB_ST_NORMAL = 0,
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
index 11abb191ada5..8267a9c4fd25 100644
--- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
+++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
@@ -367,7 +367,7 @@ static int vp8_enc_encode(void *handle,
mtk_vcodec_debug_enter(inst);
- enable_irq(ctx->dev->enc_lt_irq);
+ enable_irq(ctx->dev->enc_irq);
switch (opt) {
case VENC_START_OPT_ENCODE_FRAME:
@@ -386,7 +386,7 @@ static int vp8_enc_encode(void *handle,
encode_err:
- disable_irq(ctx->dev->enc_lt_irq);
+ disable_irq(ctx->dev->enc_irq);
mtk_vcodec_debug_leave(inst);
return ret;
diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
index 2feb0365179f..5f53d4255c36 100644
--- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
+++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
@@ -52,7 +52,7 @@ struct venc_ap_ipi_msg_init {
* (struct venc_vp8_vsi/venc_h264_vsi *)
* @param_id: parameter id (venc_set_param_type)
* @data_item: number of items in the data array
- * @data[8]: data array to store the set parameters
+ * @data: data array to store the set parameters
*/
struct venc_ap_ipi_msg_set_param {
uint32_t msg_id;
@@ -92,7 +92,7 @@ struct venc_ap_ipi_msg_enc {
*
* @base: base msg structure
* @data_item: number of items in the data array
- * @data[8]: data array to store the set parameters
+ * @data: data array to store the set parameters
*/
struct venc_ap_ipi_msg_enc_ext {
struct venc_ap_ipi_msg_enc base;
@@ -158,7 +158,7 @@ struct venc_vpu_ipi_msg_init {
* @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
* @param_id: parameter id (venc_set_param_type)
* @data_item: number of items in the data array
- * @data[6]: data array to store the return result
+ * @data: data array to store the return result
*/
struct venc_vpu_ipi_msg_set_param {
uint32_t msg_id;
@@ -171,10 +171,10 @@ struct venc_vpu_ipi_msg_set_param {
/**
* enum venc_ipi_msg_enc_state - Type of encode state
- * VEN_IPI_MSG_ENC_STATE_FRAME: one frame being encoded
- * VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full
- * VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame
- * VEN_IPI_MSG_ENC_STATE_ERROR: encounter error
+ * @VEN_IPI_MSG_ENC_STATE_FRAME: one frame being encoded
+ * @VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full
+ * @VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame
+ * @VEN_IPI_MSG_ENC_STATE_ERROR: encounter error
*/
enum venc_ipi_msg_enc_state {
VEN_IPI_MSG_ENC_STATE_FRAME,
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c
index 043894f7188c..c8a56271b259 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.c
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -19,11 +19,11 @@
#include "mtk_vpu.h"
-/**
+/*
* VPU (video processor unit) is a tiny processor controlling video hardware
* related to video codec, scaling and color format converting.
* VPU interfaces with other blocks by share memory and interrupt.
- **/
+ */
#define INIT_TIMEOUT_MS 2000U
#define IPI_TIMEOUT_MS 2000U
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h
index ee7c552ce928..a56053ff135a 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.h
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h
@@ -10,10 +10,12 @@
#include <linux/platform_device.h>
/**
+ * DOC: VPU
+ *
* VPU (video processor unit) is a tiny processor controlling video hardware
* related to video codec, scaling and color format converting.
* VPU interfaces with other blocks by share memory and interrupt.
- **/
+ */
typedef void (*ipi_handler_t) (const void *data,
unsigned int len,
@@ -126,18 +128,18 @@ struct platform_device *vpu_get_plat_device(struct platform_device *pdev);
* vpu_wdt_reg_handler - register a VPU watchdog handler
*
* @pdev: VPU platform device
- * @vpu_wdt_reset_func: the callback reset function
- * @private_data: the private data for reset function
- * @rst_id: reset id
+ * @vpu_wdt_reset_func(): the callback reset function
+ * @priv: the private data for reset function
+ * @priv: the private data for reset function
+ * @id: reset id
*
* Register a handler performing own tasks when vpu reset by watchdog
*
* Return: Return 0 if the handler is added successfully,
* otherwise it is failed.
- *
**/
int vpu_wdt_reg_handler(struct platform_device *pdev,
- void vpu_wdt_reset_func(void *),
+ void vpu_wdt_reset_func(void *priv),
void *priv, enum rst_id id);
/**
@@ -171,8 +173,8 @@ int vpu_load_firmware(struct platform_device *pdev);
/**
* vpu_mapping_dm_addr - Mapping DTCM/DMEM to kernel virtual address
*
- * @pdev: VPU platform device
- * @dmem_addr: VPU's data memory address
+ * @pdev: VPU platform device
+ * @dtcm_dmem_addr: VPU's data memory address
*
* Mapping the VPU's DTCM (Data Tightly-Coupled Memory) /
* DMEM (Data Extended Memory) memory address to
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index a6bb7d9bf75f..53025c8c7531 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -691,6 +691,8 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
pipe->do_propagation = false;
+ mutex_lock(&isp->media_dev.graph_mutex);
+
entity = &pipe->output->video.entity;
while (1) {
pad = &entity->pads[0];
@@ -705,8 +707,10 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
subdev = media_entity_to_v4l2_subdev(entity);
ret = v4l2_subdev_call(subdev, video, s_stream, mode);
- if (ret < 0 && ret != -ENOIOCTLCMD)
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ mutex_unlock(&isp->media_dev.graph_mutex);
return ret;
+ }
if (subdev == &isp->isp_ccdc.subdev) {
v4l2_subdev_call(&isp->isp_aewb.subdev, video,
@@ -723,6 +727,8 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
break;
}
+ mutex_unlock(&isp->media_dev.graph_mutex);
+
return 0;
}
@@ -2028,6 +2034,8 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
struct v4l2_subdev *sd;
int ret;
+ mutex_lock(&isp->media_dev.graph_mutex);
+
ret = media_entity_enum_init(&isp->crashed, &isp->media_dev);
if (ret)
return ret;
@@ -2038,10 +2046,14 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
ret = isp_link_entity(isp, &sd->entity,
v4l2_subdev_to_bus_cfg(sd)->interface);
- if (ret < 0)
+ if (ret < 0) {
+ mutex_unlock(&isp->media_dev.graph_mutex);
return ret;
+ }
}
+ mutex_unlock(&isp->media_dev.graph_mutex);
+
ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index 14077797f5e1..dd510ee9b58a 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -2389,7 +2389,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
pxa_camera_activate(pcdev);
- dev_set_drvdata(&pdev->dev, pcdev);
+ platform_set_drvdata(pdev, pcdev);
err = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
if (err)
goto exit_deactivate;
@@ -2421,7 +2421,7 @@ exit_free_dma_y:
static int pxa_camera_remove(struct platform_device *pdev)
{
- struct pxa_camera_dev *pcdev = dev_get_drvdata(&pdev->dev);
+ struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev);
pxa_camera_deactivate(pcdev);
tasklet_kill(&pcdev->task_eof);
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 63c1b1b2943c..0752c46ea37b 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -4,12 +4,18 @@
qcom-camss-objs += \
camss.o \
camss-csid.o \
+ camss-csid-4-1.o \
+ camss-csid-4-7.o \
+ camss-csid-170.o \
camss-csiphy-2ph-1-0.o \
camss-csiphy-3ph-1-0.o \
camss-csiphy.o \
camss-ispif.o \
camss-vfe-4-1.o \
camss-vfe-4-7.o \
+ camss-vfe-4-8.o \
+ camss-vfe-170.o \
+ camss-vfe-gen1.o \
camss-vfe.o \
camss-video.o \
diff --git a/drivers/media/platform/qcom/camss/camss-csid-170.c b/drivers/media/platform/qcom/camss/camss-csid-170.c
new file mode 100644
index 000000000000..ac22ff29d2a9
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-170.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-csid-4-7.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (C) 2020 Linaro Ltd.
+ */
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include "camss-csid.h"
+#include "camss-csid-gen2.h"
+#include "camss.h"
+
+/* The CSID 2 IP-block is different from the others,
+ * and is of a bare-bones Lite version, with no PIX
+ * interface support. As a result of that it has an
+ * alternate register layout.
+ */
+#define IS_LITE (csid->id == 2 ? 1 : 0)
+
+#define CSID_HW_VERSION 0x0
+#define HW_VERSION_STEPPING 0
+#define HW_VERSION_REVISION 16
+#define HW_VERSION_GENERATION 28
+
+#define CSID_RST_STROBES 0x10
+#define RST_STROBES 0
+
+#define CSID_CSI2_RX_IRQ_STATUS 0x20
+#define CSID_CSI2_RX_IRQ_MASK 0x24
+#define CSID_CSI2_RX_IRQ_CLEAR 0x28
+
+#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) ((IS_LITE ? 0x30 : 0x40) \
+ + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_MASK(rdi) ((IS_LITE ? 0x34 : 0x44) \
+ + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) ((IS_LITE ? 0x38 : 0x48) \
+ + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_SET(rdi) ((IS_LITE ? 0x3C : 0x4C) \
+ + 0x10 * (rdi))
+
+#define CSID_TOP_IRQ_STATUS 0x70
+#define TOP_IRQ_STATUS_RESET_DONE 0
+#define CSID_TOP_IRQ_MASK 0x74
+#define CSID_TOP_IRQ_CLEAR 0x78
+#define CSID_TOP_IRQ_SET 0x7C
+#define CSID_IRQ_CMD 0x80
+#define IRQ_CMD_CLEAR 0
+#define IRQ_CMD_SET 4
+
+#define CSID_CSI2_RX_CFG0 0x100
+#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0
+#define CSI2_RX_CFG0_DL0_INPUT_SEL 4
+#define CSI2_RX_CFG0_DL1_INPUT_SEL 8
+#define CSI2_RX_CFG0_DL2_INPUT_SEL 12
+#define CSI2_RX_CFG0_DL3_INPUT_SEL 16
+#define CSI2_RX_CFG0_PHY_NUM_SEL 20
+#define CSI2_RX_CFG0_PHY_TYPE_SEL 24
+
+#define CSID_CSI2_RX_CFG1 0x104
+#define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN 0
+#define CSI2_RX_CFG1_DE_SCRAMBLE_EN 1
+#define CSI2_RX_CFG1_VC_MODE 2
+#define CSI2_RX_CFG1_COMPLETE_STREAM_EN 4
+#define CSI2_RX_CFG1_COMPLETE_STREAM_FRAME_TIMING 5
+#define CSI2_RX_CFG1_MISR_EN 6
+#define CSI2_RX_CFG1_CGC_MODE 7
+#define CGC_MODE_DYNAMIC_GATING 0
+#define CGC_MODE_ALWAYS_ON 1
+
+#define CSID_RDI_CFG0(rdi) ((IS_LITE ? 0x200 : 0x300) \
+ + 0x100 * (rdi))
+#define RDI_CFG0_BYTE_CNTR_EN 0
+#define RDI_CFG0_FORMAT_MEASURE_EN 1
+#define RDI_CFG0_TIMESTAMP_EN 2
+#define RDI_CFG0_DROP_H_EN 3
+#define RDI_CFG0_DROP_V_EN 4
+#define RDI_CFG0_CROP_H_EN 5
+#define RDI_CFG0_CROP_V_EN 6
+#define RDI_CFG0_MISR_EN 7
+#define RDI_CFG0_CGC_MODE 8
+#define CGC_MODE_DYNAMIC 0
+#define CGC_MODE_ALWAYS_ON 1
+#define RDI_CFG0_PLAIN_ALIGNMENT 9
+#define PLAIN_ALIGNMENT_LSB 0
+#define PLAIN_ALIGNMENT_MSB 1
+#define RDI_CFG0_PLAIN_FORMAT 10
+#define RDI_CFG0_DECODE_FORMAT 12
+#define RDI_CFG0_DATA_TYPE 16
+#define RDI_CFG0_VIRTUAL_CHANNEL 22
+#define RDI_CFG0_DT_ID 27
+#define RDI_CFG0_EARLY_EOF_EN 29
+#define RDI_CFG0_PACKING_FORMAT 30
+#define RDI_CFG0_ENABLE 31
+
+#define CSID_RDI_CFG1(rdi) ((IS_LITE ? 0x204 : 0x304)\
+ + 0x100 * (rdi))
+#define RDI_CFG1_TIMESTAMP_STB_SEL 0
+
+#define CSID_RDI_CTRL(rdi) ((IS_LITE ? 0x208 : 0x308)\
+ + 0x100 * (rdi))
+#define RDI_CTRL_HALT_CMD 0
+#define ALT_CMD_RESUME_AT_FRAME_BOUNDARY 1
+#define RDI_CTRL_HALT_MODE 2
+
+#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((IS_LITE ? 0x20C : 0x30C)\
+ + 0x100 * (rdi))
+#define CSID_RDI_FRM_DROP_PERIOD(rdi) ((IS_LITE ? 0x210 : 0x310)\
+ + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) ((IS_LITE ? 0x214 : 0x314)\
+ + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) ((IS_LITE ? 0x218 : 0x318)\
+ + 0x100 * (rdi))
+#define CSID_RDI_RPP_PIX_DROP_PATTERN(rdi) ((IS_LITE ? 0x224 : 0x324)\
+ + 0x100 * (rdi))
+#define CSID_RDI_RPP_PIX_DROP_PERIOD(rdi) ((IS_LITE ? 0x228 : 0x328)\
+ + 0x100 * (rdi))
+#define CSID_RDI_RPP_LINE_DROP_PATTERN(rdi) ((IS_LITE ? 0x22C : 0x32C)\
+ + 0x100 * (rdi))
+#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((IS_LITE ? 0x230 : 0x330)\
+ + 0x100 * (rdi))
+
+#define CSID_TPG_CTRL 0x600
+#define TPG_CTRL_TEST_EN 0
+#define TPG_CTRL_FS_PKT_EN 1
+#define TPG_CTRL_FE_PKT_EN 2
+#define TPG_CTRL_NUM_ACTIVE_LANES 4
+#define TPG_CTRL_CYCLES_BETWEEN_PKTS 8
+#define TPG_CTRL_NUM_TRAIL_BYTES 20
+
+#define CSID_TPG_VC_CFG0 0x604
+#define TPG_VC_CFG0_VC_NUM 0
+#define TPG_VC_CFG0_NUM_ACTIVE_SLOTS 8
+#define NUM_ACTIVE_SLOTS_0_ENABLED 0
+#define NUM_ACTIVE_SLOTS_0_1_ENABLED 1
+#define NUM_ACTIVE_SLOTS_0_1_2_ENABLED 2
+#define NUM_ACTIVE_SLOTS_0_1_3_ENABLED 3
+#define TPG_VC_CFG0_LINE_INTERLEAVING_MODE 10
+#define INTELEAVING_MODE_INTERLEAVED 0
+#define INTELEAVING_MODE_ONE_SHOT 1
+#define TPG_VC_CFG0_NUM_FRAMES 16
+
+#define CSID_TPG_VC_CFG1 0x608
+#define TPG_VC_CFG1_H_BLANKING_COUNT 0
+#define TPG_VC_CFG1_V_BLANKING_COUNT 12
+#define TPG_VC_CFG1_V_BLANK_FRAME_WIDTH_SEL 24
+
+#define CSID_TPG_LFSR_SEED 0x60C
+
+#define CSID_TPG_DT_n_CFG_0(n) (0x610 + (n) * 0xC)
+#define TPG_DT_n_CFG_0_FRAME_HEIGHT 0
+#define TPG_DT_n_CFG_0_FRAME_WIDTH 16
+
+#define CSID_TPG_DT_n_CFG_1(n) (0x614 + (n) * 0xC)
+#define TPG_DT_n_CFG_1_DATA_TYPE 0
+#define TPG_DT_n_CFG_1_ECC_XOR_MASK 8
+#define TPG_DT_n_CFG_1_CRC_XOR_MASK 16
+
+#define CSID_TPG_DT_n_CFG_2(n) (0x618 + (n) * 0xC)
+#define TPG_DT_n_CFG_2_PAYLOAD_MODE 0
+#define TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD 4
+#define TPG_DT_n_CFG_2_ENCODE_FORMAT 16
+
+#define CSID_TPG_COLOR_BARS_CFG 0x640
+#define TPG_COLOR_BARS_CFG_UNICOLOR_BAR_EN 0
+#define TPG_COLOR_BARS_CFG_UNICOLOR_BAR_SEL 4
+#define TPG_COLOR_BARS_CFG_SPLIT_EN 5
+#define TPG_COLOR_BARS_CFG_ROTATE_PERIOD 8
+
+#define CSID_TPG_COLOR_BOX_CFG 0x644
+#define TPG_COLOR_BOX_CFG_MODE 0
+#define TPG_COLOR_BOX_PATTERN_SEL 2
+
+static const struct csid_format csid_formats[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+};
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ struct csid_testgen_config *tg = &csid->testgen;
+ u32 val;
+ u32 phy_sel = 0;
+ u8 lane_cnt = csid->phy.lane_cnt;
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_SRC];
+ const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ input_format->code);
+
+ if (!lane_cnt)
+ lane_cnt = 4;
+
+ if (!tg->enabled)
+ phy_sel = csid->phy.csiphy_id;
+
+ if (enable) {
+ u8 vc = 0; /* Virtual Channel 0 */
+ u8 dt_id = vc * 4;
+
+ if (tg->enabled) {
+ /* Config Test Generator */
+ vc = 0xa;
+
+ /* configure one DT, infinite frames */
+ val = vc << TPG_VC_CFG0_VC_NUM;
+ val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE;
+ val |= 0 << TPG_VC_CFG0_NUM_FRAMES;
+ writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0);
+
+ val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT;
+ val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT;
+ writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1);
+
+ writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED);
+
+ val = input_format->height & 0x1fff << TPG_DT_n_CFG_0_FRAME_HEIGHT;
+ val |= input_format->width & 0x1fff << TPG_DT_n_CFG_0_FRAME_WIDTH;
+ writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0));
+
+ val = DATA_TYPE_RAW_10BIT << TPG_DT_n_CFG_1_DATA_TYPE;
+ writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0));
+
+ val = tg->mode << TPG_DT_n_CFG_2_PAYLOAD_MODE;
+ val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD;
+ val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT;
+ writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0));
+
+ writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG);
+
+ writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG);
+ }
+
+ val = 1 << RDI_CFG0_BYTE_CNTR_EN;
+ val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN;
+ val |= 1 << RDI_CFG0_TIMESTAMP_EN;
+ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
+ val |= DATA_TYPE_RAW_10BIT << RDI_CFG0_DATA_TYPE;
+ val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
+ val |= dt_id << RDI_CFG0_DT_ID;
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(0));
+
+ /* CSID_TIMESTAMP_STB_POST_IRQ */
+ val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
+ writel_relaxed(val, csid->base + CSID_RDI_CFG1(0));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0));
+
+ val = 0;
+ writel_relaxed(0, csid->base + CSID_RDI_FRM_DROP_PATTERN(0));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(0));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(0));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(0));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(0));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(0));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_CTRL(0));
+
+ val = readl_relaxed(csid->base + CSID_RDI_CFG0(0));
+ val |= 1 << RDI_CFG0_ENABLE;
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(0));
+ }
+
+ if (tg->enabled) {
+ val = enable << TPG_CTRL_TEST_EN;
+ val |= 1 << TPG_CTRL_FS_PKT_EN;
+ val |= 1 << TPG_CTRL_FE_PKT_EN;
+ val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES;
+ val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS;
+ val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES;
+ writel_relaxed(val, csid->base + CSID_TPG_CTRL);
+ }
+
+ val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
+ val |= csid->phy.lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
+ val |= phy_sel << CSI2_RX_CFG0_PHY_NUM_SEL;
+ writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0);
+
+ val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
+ val |= 1 << CSI2_RX_CFG1_MISR_EN;
+ writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); // csi2_vc_mode_shift_val ?
+
+ /* error irqs start at BIT(11) */
+ writel_relaxed(~0u, csid->base + CSID_CSI2_RX_IRQ_MASK);
+
+ /* RDI irq */
+ writel_relaxed(~0u, csid->base + CSID_TOP_IRQ_MASK);
+
+ val = 1 << RDI_CTRL_HALT_CMD;
+ writel_relaxed(val, csid->base + CSID_RDI_CTRL(0));
+}
+
+static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
+{
+ if (val > 0 && val <= csid->testgen.nmodes)
+ csid->testgen.mode = val;
+
+ return 0;
+}
+
+/*
+ * csid_hw_version - CSID hardware version query
+ * @csid: CSID device
+ *
+ * Return HW version or error
+ */
+static u32 csid_hw_version(struct csid_device *csid)
+{
+ u32 hw_version;
+ u32 hw_gen;
+ u32 hw_rev;
+ u32 hw_step;
+
+ hw_version = readl_relaxed(csid->base + CSID_HW_VERSION);
+ hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
+ hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
+ hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
+ dev_dbg(csid->camss->dev, "CSID HW Version = %u.%u.%u\n",
+ hw_gen, hw_rev, hw_step);
+
+ return hw_version;
+}
+
+/*
+ * csid_isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 val;
+ u8 reset_done;
+
+ val = readl_relaxed(csid->base + CSID_TOP_IRQ_STATUS);
+ writel_relaxed(val, csid->base + CSID_TOP_IRQ_CLEAR);
+ reset_done = val & BIT(TOP_IRQ_STATUS_RESET_DONE);
+
+ val = readl_relaxed(csid->base + CSID_CSI2_RX_IRQ_STATUS);
+ writel_relaxed(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR);
+
+ val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(0));
+ writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(0));
+
+ val = 1 << IRQ_CMD_CLEAR;
+ writel_relaxed(val, csid->base + CSID_IRQ_CMD);
+
+ if (reset_done)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+ u32 val;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel_relaxed(1, csid->base + CSID_TOP_IRQ_CLEAR);
+ writel_relaxed(1, csid->base + CSID_IRQ_CMD);
+ writel_relaxed(1, csid->base + CSID_TOP_IRQ_MASK);
+ writel_relaxed(1, csid->base + CSID_IRQ_CMD);
+
+ /* preserve registers */
+ val = 0x1e << RST_STROBES;
+ writel_relaxed(val, csid->base + CSID_RST_STROBES);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code)
+{
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ case MEDIA_BUS_FMT_Y10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ default:
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+ }
+}
+
+static void csid_subdev_init(struct csid_device *csid)
+{
+ csid->formats = csid_formats;
+ csid->nformats = ARRAY_SIZE(csid_formats);
+ csid->testgen.modes = csid_testgen_modes;
+ csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2;
+}
+
+const struct csid_hw_ops csid_ops_170 = {
+ .configure_stream = csid_configure_stream,
+ .configure_testgen_pattern = csid_configure_testgen_pattern,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c
new file mode 100644
index 000000000000..d2aec0679dfc
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-csid-4-1.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (C) 2020 Linaro Ltd.
+ */
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include "camss-csid.h"
+#include "camss-csid-gen1.h"
+#include "camss.h"
+
+#define CAMSS_CSID_HW_VERSION 0x0
+#define CAMSS_CSID_CORE_CTRL_0 0x004
+#define CAMSS_CSID_CORE_CTRL_1 0x008
+#define CAMSS_CSID_RST_CMD 0x00c
+#define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0)
+#define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1)
+#define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4
+#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (PLAIN_FORMAT_PLAIN8 << 8)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (PLAIN_FORMAT_PLAIN16 << 8)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9)
+#define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10)
+#define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10)
+#define CAMSS_CSID_IRQ_CLEAR_CMD 0x060
+#define CAMSS_CSID_IRQ_MASK 0x064
+#define CAMSS_CSID_IRQ_STATUS 0x068
+#define CAMSS_CSID_TG_CTRL 0x0a0
+#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436
+#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437
+#define CAMSS_CSID_TG_VC_CFG 0x0a4
+#define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff
+#define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f
+#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n))
+
+static const struct csid_format csid_formats[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+};
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ struct csid_testgen_config *tg = &csid->testgen;
+ u32 val;
+
+ if (enable) {
+ struct v4l2_mbus_framefmt *input_format;
+ const struct csid_format *format;
+ u8 vc = 0; /* Virtual Channel 0 */
+ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */
+ u8 dt_shift;
+
+ if (tg->enabled) {
+ /* Config Test Generator */
+ u32 num_lines, num_bytes_per_line;
+
+ input_format = &csid->fmt[MSM_CSID_PAD_SRC];
+ format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ input_format->code);
+ num_bytes_per_line = input_format->width * format->bpp * format->spp / 8;
+ num_lines = input_format->height;
+
+ /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
+ /* 1:0 VC */
+ val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) |
+ ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13);
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG);
+
+ /* 28:16 bytes per lines, 12:0 num of lines */
+ val = ((num_bytes_per_line & 0x1fff) << 16) |
+ (num_lines & 0x1fff);
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_0(0));
+
+ /* 5:0 data type */
+ val = format->data_type;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_1(0));
+
+ /* 2:0 output test pattern */
+ val = tg->mode - 1;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0));
+ } else {
+ struct csid_phy_config *phy = &csid->phy;
+
+ input_format = &csid->fmt[MSM_CSID_PAD_SINK];
+ format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ input_format->code);
+
+ val = phy->lane_cnt - 1;
+ val |= phy->lane_assign << 4;
+
+ writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_0);
+
+ val = phy->csiphy_id << 17;
+ val |= 0x9;
+
+ writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1);
+ }
+
+ /* Config LUT */
+
+ dt_shift = (cid % 4) * 8;
+ val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+ val &= ~(0xff << dt_shift);
+ val |= format->data_type << dt_shift;
+ writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+
+ val = CAMSS_CSID_CID_n_CFG_ISPIF_EN;
+ val |= CAMSS_CSID_CID_n_CFG_RDI_EN;
+ val |= format->decode_format << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT;
+ val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP;
+ writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid));
+
+ if (tg->enabled) {
+ val = CAMSS_CSID_TG_CTRL_ENABLE;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+ }
+ } else {
+ if (tg->enabled) {
+ val = CAMSS_CSID_TG_CTRL_DISABLE;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+ }
+ }
+}
+
+static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
+{
+ if (val > 0 && val <= csid->testgen.nmodes)
+ csid->testgen.mode = val;
+
+ return 0;
+}
+
+static u32 csid_hw_version(struct csid_device *csid)
+{
+ u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
+
+ dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version);
+
+ return hw_version;
+}
+
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 value;
+
+ value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS);
+ writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD);
+
+ if ((value >> 11) & 0x1)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code)
+{
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+}
+
+static void csid_subdev_init(struct csid_device *csid)
+{
+ csid->formats = csid_formats;
+ csid->nformats = ARRAY_SIZE(csid_formats);
+ csid->testgen.modes = csid_testgen_modes;
+ csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1;
+}
+
+const struct csid_hw_ops csid_ops_4_1 = {
+ .configure_stream = csid_configure_stream,
+ .configure_testgen_pattern = csid_configure_testgen_pattern,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c
new file mode 100644
index 000000000000..e7436ec6d02b
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-csid-4-7.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (C) 2020 Linaro Ltd.
+ */
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include "camss-csid.h"
+#include "camss-csid-gen1.h"
+#include "camss.h"
+
+#define CAMSS_CSID_HW_VERSION 0x0
+#define CAMSS_CSID_CORE_CTRL_0 0x004
+#define CAMSS_CSID_CORE_CTRL_1 0x008
+#define CAMSS_CSID_RST_CMD 0x010
+#define CAMSS_CSID_CID_LUT_VC_n(n) (0x014 + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG(n) (0x024 + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0)
+#define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1)
+#define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4
+#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (PLAIN_FORMAT_PLAIN8 << 8)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (PLAIN_FORMAT_PLAIN16 << 8)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9)
+#define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10)
+#define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10)
+#define CAMSS_CSID_IRQ_CLEAR_CMD 0x064
+#define CAMSS_CSID_IRQ_MASK 0x068
+#define CAMSS_CSID_IRQ_STATUS 0x06c
+#define CAMSS_CSID_TG_CTRL 0x0a8
+#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436
+#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437
+#define CAMSS_CSID_TG_VC_CFG 0x0ac
+#define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff
+#define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f
+#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0b4 + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n))
+
+static const struct csid_format csid_formats[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+};
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ struct csid_testgen_config *tg = &csid->testgen;
+ u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code;
+ u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code;
+ u32 val;
+
+ if (enable) {
+ struct v4l2_mbus_framefmt *input_format;
+ const struct csid_format *format;
+ u8 vc = 0; /* Virtual Channel 0 */
+ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */
+ u8 dt_shift;
+
+ if (tg->enabled) {
+ /* Config Test Generator */
+ u32 num_bytes_per_line, num_lines;
+
+ input_format = &csid->fmt[MSM_CSID_PAD_SRC];
+ format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ input_format->code);
+ num_bytes_per_line = input_format->width * format->bpp * format->spp / 8;
+ num_lines = input_format->height;
+
+ /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
+ /* 1:0 VC */
+ val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) |
+ ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13);
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG);
+
+ /* 28:16 bytes per lines, 12:0 num of lines */
+ val = ((num_bytes_per_line & 0x1fff) << 16) |
+ (num_lines & 0x1fff);
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_0(0));
+
+ /* 5:0 data type */
+ val = format->data_type;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_1(0));
+
+ /* 2:0 output test pattern */
+ val = tg->mode - 1;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0));
+ } else {
+ struct csid_phy_config *phy = &csid->phy;
+
+ input_format = &csid->fmt[MSM_CSID_PAD_SINK];
+ format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ input_format->code);
+
+ val = phy->lane_cnt - 1;
+ val |= phy->lane_assign << 4;
+
+ writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_0);
+
+ val = phy->csiphy_id << 17;
+ val |= 0x9;
+
+ writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1);
+ }
+
+ /* Config LUT */
+
+ dt_shift = (cid % 4) * 8;
+
+ val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+ val &= ~(0xff << dt_shift);
+ val |= format->data_type << dt_shift;
+ writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+
+ val = CAMSS_CSID_CID_n_CFG_ISPIF_EN;
+ val |= CAMSS_CSID_CID_n_CFG_RDI_EN;
+ val |= format->decode_format << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT;
+ val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP;
+
+ if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
+ src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) ||
+ (sink_code == MEDIA_BUS_FMT_Y10_1X10 &&
+ src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) {
+ val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING;
+ val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16;
+ val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB;
+ }
+
+ writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid));
+
+ if (tg->enabled) {
+ val = CAMSS_CSID_TG_CTRL_ENABLE;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+ }
+ } else {
+ if (tg->enabled) {
+ val = CAMSS_CSID_TG_CTRL_DISABLE;
+ writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+ }
+ }
+}
+
+static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
+{
+ if (val > 0 && val <= csid->testgen.nmodes)
+ csid->testgen.mode = val;
+
+ return 0;
+}
+
+static u32 csid_hw_version(struct csid_device *csid)
+{
+ u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
+
+ dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version);
+
+ return hw_version;
+}
+
+/*
+ * isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 value;
+
+ value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS);
+ writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD);
+
+ if ((value >> 11) & 0x1)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code)
+{
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ case MEDIA_BUS_FMT_Y10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ default:
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+ }
+}
+
+static void csid_subdev_init(struct csid_device *csid)
+{
+ csid->formats = csid_formats;
+ csid->nformats = ARRAY_SIZE(csid_formats);
+ csid->testgen.modes = csid_testgen_modes;
+ csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1;
+}
+
+const struct csid_hw_ops csid_ops_4_7 = {
+ .configure_stream = csid_configure_stream,
+ .configure_testgen_pattern = csid_configure_testgen_pattern,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen1.h b/drivers/media/platform/qcom/camss/camss-csid-gen1.h
new file mode 100644
index 000000000000..80a2bc6efff6
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen1.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-csid-gen1.h
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module Generation 1
+ *
+ * Copyright (C) 2021 Linaro Ltd.
+ */
+#ifndef QC_MSM_CAMSS_CSID_GEN1_H
+#define QC_MSM_CAMSS_CSID_GEN1_H
+
+#define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0
+#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
+#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
+#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
+#define DECODE_FORMAT_DPCM_10_6_10 0x4
+#define DECODE_FORMAT_DPCM_10_8_10 0x5
+#define DECODE_FORMAT_DPCM_12_6_12 0x6
+#define DECODE_FORMAT_DPCM_12_8_12 0x7
+#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8
+#define DECODE_FORMAT_DPCM_14_8_14 0x9
+#define DECODE_FORMAT_DPCM_14_10_14 0xa
+
+#define PLAIN_FORMAT_PLAIN8 0x0 /* supports DPCM, UNCOMPRESSED_6/8_BIT */
+#define PLAIN_FORMAT_PLAIN16 0x1 /* supports DPCM, UNCOMPRESSED_10/16_BIT */
+
+#endif /* QC_MSM_CAMSS_CSID_GEN1_H */
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.h b/drivers/media/platform/qcom/camss/camss-csid-gen2.h
new file mode 100644
index 000000000000..3a8ad001b3e8
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-csid-gen1.h
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module Generation 1
+ *
+ * Copyright (C) 2021 Linaro Ltd.
+ */
+#ifndef QC_MSM_CAMSS_CSID_GEN2_H
+#define QC_MSM_CAMSS_CSID_GEN2_H
+
+#define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0
+#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
+#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
+#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
+#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x4
+#define DECODE_FORMAT_UNCOMPRESSED_16_BIT 0x5
+#define DECODE_FORMAT_UNCOMPRESSED_20_BIT 0x6
+#define DECODE_FORMAT_DPCM_10_6_10 0x7
+#define DECODE_FORMAT_DPCM_10_8_10 0x8
+#define DECODE_FORMAT_DPCM_12_6_12 0x9
+#define DECODE_FORMAT_DPCM_12_8_12 0xa
+#define DECODE_FORMAT_DPCM_14_8_14 0xb
+#define DECODE_FORMAT_DPCM_14_10_14 0xc
+#define DECODE_FORMAT_DPCM_12_10_12 0xd
+#define DECODE_FORMAT_USER_DEFINED 0xe
+#define DECODE_FORMAT_PAYLOAD_ONLY 0xf
+
+#define ENCODE_FORMAT_RAW_8_BIT 0x1
+#define ENCODE_FORMAT_RAW_10_BIT 0x2
+#define ENCODE_FORMAT_RAW_12_BIT 0x3
+#define ENCODE_FORMAT_RAW_14_BIT 0x4
+#define ENCODE_FORMAT_RAW_16_BIT 0x5
+
+#define PLAIN_FORMAT_PLAIN8 0x0 /* supports DPCM, UNCOMPRESSED_6/8_BIT */
+#define PLAIN_FORMAT_PLAIN16 0x1 /* supports DPCM, UNCOMPRESSED_10/16_BIT */
+#define PLAIN_FORMAT_PLAIN32 0x2 /* supports UNCOMPRESSED_20_BIT */
+
+#endif /* QC_MSM_CAMSS_CSID_GEN2_H */
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index be3fe76f3dc3..cc11fbfdae13 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -22,409 +22,52 @@
#include <media/v4l2-subdev.h>
#include "camss-csid.h"
+#include "camss-csid-gen1.h"
#include "camss.h"
#define MSM_CSID_NAME "msm_csid"
-#define CAMSS_CSID_HW_VERSION 0x0
-#define CAMSS_CSID_CORE_CTRL_0 0x004
-#define CAMSS_CSID_CORE_CTRL_1 0x008
-#define CAMSS_CSID_RST_CMD(v) ((v) == CAMSS_8x16 ? 0x00c : 0x010)
-#define CAMSS_CSID_CID_LUT_VC_n(v, n) \
- (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n))
-#define CAMSS_CSID_CID_n_CFG(v, n) \
- (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n))
-#define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0)
-#define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1)
-#define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4
-#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (0 << 8)
-#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (1 << 8)
-#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9)
-#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9)
-#define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10)
-#define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10)
-#define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064)
-#define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068)
-#define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c)
-#define CAMSS_CSID_TG_CTRL(v) ((v) == CAMSS_8x16 ? 0x0a0 : 0x0a8)
-#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436
-#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437
-#define CAMSS_CSID_TG_VC_CFG(v) ((v) == CAMSS_8x16 ? 0x0a4 : 0x0ac)
-#define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff
-#define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f
-#define CAMSS_CSID_TG_DT_n_CGG_0(v, n) \
- (((v) == CAMSS_8x16 ? 0x0ac : 0x0b4) + 0xc * (n))
-#define CAMSS_CSID_TG_DT_n_CGG_1(v, n) \
- (((v) == CAMSS_8x16 ? 0x0b0 : 0x0b8) + 0xc * (n))
-#define CAMSS_CSID_TG_DT_n_CGG_2(v, n) \
- (((v) == CAMSS_8x16 ? 0x0b4 : 0x0bc) + 0xc * (n))
-
-#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12
-#define DATA_TYPE_YUV422_8BIT 0x1e
-#define DATA_TYPE_RAW_6BIT 0x28
-#define DATA_TYPE_RAW_8BIT 0x2a
-#define DATA_TYPE_RAW_10BIT 0x2b
-#define DATA_TYPE_RAW_12BIT 0x2c
-#define DATA_TYPE_RAW_14BIT 0x2d
-
-#define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0
-#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
-#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
-#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
-#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8
-
-#define CSID_RESET_TIMEOUT_MS 500
-
-struct csid_format {
- u32 code;
- u8 data_type;
- u8 decode_format;
- u8 bpp;
- u8 spp; /* bus samples per pixel */
-};
-
-static const struct csid_format csid_formats_8x16[] = {
- {
- MEDIA_BUS_FMT_UYVY8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_VYUY8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YUYV8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YVYU8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
-};
-
-static const struct csid_format csid_formats_8x96[] = {
- {
- MEDIA_BUS_FMT_UYVY8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_VYUY8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YUYV8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YVYU8_2X8,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
+const char * const csid_testgen_modes[] = {
+ "Disabled",
+ "Incrementing",
+ "Alternating 0x55/0xAA",
+ "All Zeros 0x00",
+ "All Ones 0xFF",
+ "Pseudo-random Data",
+ "User Specified",
+ "Complex pattern",
+ "Color box",
+ "Color bars",
+ NULL
};
-static u32 csid_find_code(u32 *code, unsigned int n_code,
- unsigned int index, u32 req_code)
+u32 csid_find_code(u32 *codes, unsigned int ncodes,
+ unsigned int match_format_idx, u32 match_code)
{
int i;
- if (!req_code && (index >= n_code))
+ if (!match_code && (match_format_idx >= ncodes))
return 0;
- for (i = 0; i < n_code; i++)
- if (req_code) {
- if (req_code == code[i])
- return req_code;
+ for (i = 0; i < ncodes; i++)
+ if (match_code) {
+ if (codes[i] == match_code)
+ return match_code;
} else {
- if (i == index)
- return code[i];
+ if (i == match_format_idx)
+ return codes[i];
}
- return code[0];
+ return codes[0];
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int index, u32 src_req_code)
-{
- if (csid->camss->version == CAMSS_8x16) {
- if (index > 0)
- return 0;
-
- return sink_code;
- } else if (csid->camss->version == CAMSS_8x96 ||
- csid->camss->version == CAMSS_660) {
- switch (sink_code) {
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
- }
- case MEDIA_BUS_FMT_Y10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_Y10_1X10,
- MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
- }
- default:
- if (index > 0)
- return 0;
-
- return sink_code;
- }
- } else {
- return 0;
- }
-}
-
-static const struct csid_format *csid_get_fmt_entry(
- const struct csid_format *formats,
- unsigned int nformat,
- u32 code)
+const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats,
+ unsigned int nformats,
+ u32 code)
{
unsigned int i;
- for (i = 0; i < nformat; i++)
+ for (i = 0; i < nformats; i++)
if (code == formats[i].code)
return &formats[i];
@@ -434,41 +77,23 @@ static const struct csid_format *csid_get_fmt_entry(
}
/*
- * csid_isr - CSID module interrupt handler
- * @irq: Interrupt line
- * @dev: CSID device
- *
- * Return IRQ_HANDLED on success
- */
-static irqreturn_t csid_isr(int irq, void *dev)
-{
- struct csid_device *csid = dev;
- enum camss_version ver = csid->camss->version;
- u32 value;
-
- value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS(ver));
- writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver));
-
- if ((value >> 11) & 0x1)
- complete(&csid->reset_complete);
-
- return IRQ_HANDLED;
-}
-
-/*
* csid_set_clock_rates - Calculate and set clock rates on CSID module
* @csiphy: CSID device
*/
static int csid_set_clock_rates(struct csid_device *csid)
{
struct device *dev = csid->camss->dev;
- u32 pixel_clock;
+ const struct csid_format *fmt;
+ s64 link_freq;
int i, j;
int ret;
- ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock);
- if (ret)
- pixel_clock = 0;
+ fmt = csid_get_fmt_entry(csid->formats, csid->nformats,
+ csid->fmt[MSM_CSIPHY_PAD_SINK].code);
+ link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp,
+ csid->phy.lane_cnt);
+ if (link_freq < 0)
+ link_freq = 0;
for (i = 0; i < csid->nclocks; i++) {
struct camss_clock *clock = &csid->clock[i];
@@ -477,13 +102,7 @@ static int csid_set_clock_rates(struct csid_device *csid)
!strcmp(clock->name, "csi1") ||
!strcmp(clock->name, "csi2") ||
!strcmp(clock->name, "csi3")) {
- const struct csid_format *f = csid_get_fmt_entry(
- csid->formats,
- csid->nformats,
- csid->fmt[MSM_CSIPHY_PAD_SINK].code);
- u8 num_lanes = csid->phy.lane_cnt;
- u64 min_rate = pixel_clock * f->bpp /
- (2 * num_lanes * 4);
+ u64 min_rate = link_freq / 4;
long rate;
camss_add_clock_margin(&min_rate);
@@ -515,6 +134,8 @@ static int csid_set_clock_rates(struct csid_device *csid)
dev_err(dev, "clk set rate failed: %d\n", ret);
return ret;
}
+ } else if (clock->nfreqs) {
+ clk_set_rate(clock->clk, clock->freq[0]);
}
}
@@ -522,31 +143,6 @@ static int csid_set_clock_rates(struct csid_device *csid)
}
/*
- * csid_reset - Trigger reset on CSID module and wait to complete
- * @csid: CSID device
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int csid_reset(struct csid_device *csid)
-{
- unsigned long time;
-
- reinit_completion(&csid->reset_complete);
-
- writel_relaxed(0x7fff, csid->base +
- CAMSS_CSID_RST_CMD(csid->camss->version));
-
- time = wait_for_completion_timeout(&csid->reset_complete,
- msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
- if (!time) {
- dev_err(csid->camss->dev, "CSID reset timeout\n");
- return -EIO;
- }
-
- return 0;
-}
-
-/*
* csid_set_power - Power on/off CSID module
* @sd: CSID V4L2 subdevice
* @on: Requested power state
@@ -560,8 +156,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
int ret;
if (on) {
- u32 hw_version;
-
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_sync(dev);
@@ -590,7 +184,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
enable_irq(csid->irq);
- ret = csid_reset(csid);
+ ret = csid->ops->reset(csid);
if (ret < 0) {
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
@@ -599,8 +193,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
return ret;
}
- hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
- dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version);
+ csid->ops->hw_version(csid);
} else {
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
@@ -623,16 +216,9 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
static int csid_set_stream(struct v4l2_subdev *sd, int enable)
{
struct csid_device *csid = v4l2_get_subdevdata(sd);
- struct csid_testgen_config *tg = &csid->testgen;
- enum camss_version ver = csid->camss->version;
- u32 val;
+ int ret;
if (enable) {
- u8 vc = 0; /* Virtual Channel 0 */
- u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */
- u8 dt, dt_shift, df;
- int ret;
-
ret = v4l2_ctrl_handler_setup(&csid->ctrls);
if (ret < 0) {
dev_err(csid->camss->dev,
@@ -640,116 +226,13 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
- if (!tg->enabled &&
+ if (!csid->testgen.enabled &&
!media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK]))
return -ENOLINK;
-
- if (tg->enabled) {
- /* Config Test Generator */
- struct v4l2_mbus_framefmt *f =
- &csid->fmt[MSM_CSID_PAD_SRC];
- const struct csid_format *format = csid_get_fmt_entry(
- csid->formats, csid->nformats, f->code);
- u32 num_bytes_per_line =
- f->width * format->bpp * format->spp / 8;
- u32 num_lines = f->height;
-
- /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
- /* 1:0 VC */
- val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) |
- ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13);
- writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_VC_CFG(ver));
-
- /* 28:16 bytes per lines, 12:0 num of lines */
- val = ((num_bytes_per_line & 0x1fff) << 16) |
- (num_lines & 0x1fff);
- writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_DT_n_CGG_0(ver, 0));
-
- dt = format->data_type;
-
- /* 5:0 data type */
- val = dt;
- writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_DT_n_CGG_1(ver, 0));
-
- /* 2:0 output test pattern */
- val = tg->payload_mode;
- writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_DT_n_CGG_2(ver, 0));
-
- df = format->decode_format;
- } else {
- struct v4l2_mbus_framefmt *f =
- &csid->fmt[MSM_CSID_PAD_SINK];
- const struct csid_format *format = csid_get_fmt_entry(
- csid->formats, csid->nformats, f->code);
- struct csid_phy_config *phy = &csid->phy;
-
- val = phy->lane_cnt - 1;
- val |= phy->lane_assign << 4;
-
- writel_relaxed(val,
- csid->base + CAMSS_CSID_CORE_CTRL_0);
-
- val = phy->csiphy_id << 17;
- val |= 0x9;
-
- writel_relaxed(val,
- csid->base + CAMSS_CSID_CORE_CTRL_1);
-
- dt = format->data_type;
- df = format->decode_format;
- }
-
- /* Config LUT */
-
- dt_shift = (cid % 4) * 8;
-
- val = readl_relaxed(csid->base +
- CAMSS_CSID_CID_LUT_VC_n(ver, vc));
- val &= ~(0xff << dt_shift);
- val |= dt << dt_shift;
- writel_relaxed(val, csid->base +
- CAMSS_CSID_CID_LUT_VC_n(ver, vc));
-
- val = CAMSS_CSID_CID_n_CFG_ISPIF_EN;
- val |= CAMSS_CSID_CID_n_CFG_RDI_EN;
- val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT;
- val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP;
-
- if (csid->camss->version == CAMSS_8x96 ||
- csid->camss->version == CAMSS_660) {
- u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code;
- u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code;
-
- if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
- src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) ||
- (sink_code == MEDIA_BUS_FMT_Y10_1X10 &&
- src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) {
- val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING;
- val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16;
- val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB;
- }
- }
-
- writel_relaxed(val, csid->base +
- CAMSS_CSID_CID_n_CFG(ver, cid));
-
- if (tg->enabled) {
- val = CAMSS_CSID_TG_CTRL_ENABLE;
- writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_CTRL(ver));
- }
- } else {
- if (tg->enabled) {
- val = CAMSS_CSID_TG_CTRL_DISABLE;
- writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_CTRL(ver));
- }
}
+ csid->ops->configure_stream(csid, enable);
+
return 0;
}
@@ -818,7 +301,7 @@ static void csid_try_format(struct csid_device *csid,
*fmt = *__csid_get_format(csid, cfg,
MSM_CSID_PAD_SINK, which);
- fmt->code = csid_src_pad_code(csid, fmt->code, 0, code);
+ fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code);
} else {
/* Test generator is enabled, set format on source */
/* pad to allow test generator usage */
@@ -868,7 +351,7 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd,
MSM_CSID_PAD_SINK,
code->which);
- code->code = csid_src_pad_code(csid, sink_fmt->code,
+ code->code = csid->ops->src_pad_code(csid, sink_fmt->code,
code->index, 0);
if (!code->code)
return -EINVAL;
@@ -1004,15 +487,6 @@ static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return csid_set_format(sd, fh ? fh->pad : NULL, &format);
}
-static const char * const csid_test_pattern_menu[] = {
- "Disabled",
- "Incrementing",
- "Alternating 0x55/0xAA",
- "All Zeros 0x00",
- "All Ones 0xFF",
- "Pseudo-random Data",
-};
-
/*
* csid_set_test_pattern - Set test generator's pattern mode
* @csid: CSID device
@@ -1030,25 +504,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value)
tg->enabled = !!value;
- switch (value) {
- case 1:
- tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING;
- break;
- case 2:
- tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA;
- break;
- case 3:
- tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES;
- break;
- case 4:
- tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES;
- break;
- case 5:
- tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM;
- break;
- }
-
- return 0;
+ return csid->ops->configure_testgen_pattern(csid, value);
}
/*
@@ -1097,26 +553,23 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
csid->id = id;
if (camss->version == CAMSS_8x16) {
- csid->formats = csid_formats_8x16;
- csid->nformats =
- ARRAY_SIZE(csid_formats_8x16);
+ csid->ops = &csid_ops_4_1;
} else if (camss->version == CAMSS_8x96 ||
camss->version == CAMSS_660) {
- csid->formats = csid_formats_8x96;
- csid->nformats =
- ARRAY_SIZE(csid_formats_8x96);
+ csid->ops = &csid_ops_4_7;
+ } else if (camss->version == CAMSS_845) {
+ csid->ops = &csid_ops_170;
} else {
return -EINVAL;
}
+ csid->ops->subdev_init(csid);
/* Memory */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
csid->base = devm_ioremap_resource(dev, r);
- if (IS_ERR(csid->base)) {
- dev_err(dev, "could not map memory\n");
+ if (IS_ERR(csid->base))
return PTR_ERR(csid->base);
- }
/* Interrupt */
@@ -1130,8 +583,8 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
csid->irq = r->start;
snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d",
dev_name(dev), MSM_CSID_NAME, csid->id);
- ret = devm_request_irq(dev, csid->irq, csid_isr,
- IRQF_TRIGGER_RISING, csid->irq_name, csid);
+ ret = devm_request_irq(dev, csid->irq, csid->ops->isr,
+ IRQF_TRIGGER_RISING, csid->irq_name, csid);
if (ret < 0) {
dev_err(dev, "request_irq failed: %d\n", ret);
return ret;
@@ -1341,8 +794,8 @@ int msm_csid_register_entity(struct csid_device *csid,
csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls,
&csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
- ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0,
- csid_test_pattern_menu);
+ csid->testgen.nmodes, 0, 0,
+ csid->testgen.modes);
if (csid->ctrls.error) {
dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index 1824b3745e10..814ebc7c29d6 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -11,6 +11,7 @@
#define QC_MSM_CAMSS_CSID_H
#include <linux/clk.h>
+#include <linux/interrupt.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -21,18 +22,59 @@
#define MSM_CSID_PAD_SRC 1
#define MSM_CSID_PADS_NUM 2
-enum csid_payload_mode {
- CSID_PAYLOAD_MODE_INCREMENTING = 0,
- CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1,
- CSID_PAYLOAD_MODE_ALL_ZEROES = 2,
- CSID_PAYLOAD_MODE_ALL_ONES = 3,
- CSID_PAYLOAD_MODE_RANDOM = 4,
- CSID_PAYLOAD_MODE_USER_SPECIFIED = 5,
+#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12
+#define DATA_TYPE_YUV420_8BIT 0x18
+#define DATA_TYPE_YUV420_10BIT 0x19
+#define DATA_TYPE_YUV420_8BIT_LEGACY 0x1a
+#define DATA_TYPE_YUV420_8BIT_SHIFTED 0x1c /* Chroma Shifted Pixel Sampling */
+#define DATA_TYPE_YUV420_10BIT_SHIFTED 0x1d /* Chroma Shifted Pixel Sampling */
+#define DATA_TYPE_YUV422_8BIT 0x1e
+#define DATA_TYPE_YUV422_10BIT 0x1f
+#define DATA_TYPE_RGB444 0x20
+#define DATA_TYPE_RGB555 0x21
+#define DATA_TYPE_RGB565 0x22
+#define DATA_TYPE_RGB666 0x23
+#define DATA_TYPE_RGB888 0x24
+#define DATA_TYPE_RAW_24BIT 0x27
+#define DATA_TYPE_RAW_6BIT 0x28
+#define DATA_TYPE_RAW_7BIT 0x29
+#define DATA_TYPE_RAW_8BIT 0x2a
+#define DATA_TYPE_RAW_10BIT 0x2b
+#define DATA_TYPE_RAW_12BIT 0x2c
+#define DATA_TYPE_RAW_14BIT 0x2d
+#define DATA_TYPE_RAW_16BIT 0x2e
+#define DATA_TYPE_RAW_20BIT 0x2f
+
+#define CSID_RESET_TIMEOUT_MS 500
+
+enum csid_testgen_mode {
+ CSID_PAYLOAD_MODE_DISABLED = 0,
+ CSID_PAYLOAD_MODE_INCREMENTING = 1,
+ CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 2,
+ CSID_PAYLOAD_MODE_ALL_ZEROES = 3,
+ CSID_PAYLOAD_MODE_ALL_ONES = 4,
+ CSID_PAYLOAD_MODE_RANDOM = 5,
+ CSID_PAYLOAD_MODE_USER_SPECIFIED = 6,
+ CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1 = 6, /* excluding disabled */
+ CSID_PAYLOAD_MODE_COMPLEX_PATTERN = 7,
+ CSID_PAYLOAD_MODE_COLOR_BOX = 8,
+ CSID_PAYLOAD_MODE_COLOR_BARS = 9,
+ CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2 = 9, /* excluding disabled */
+};
+
+struct csid_format {
+ u32 code;
+ u8 data_type;
+ u8 decode_format;
+ u8 bpp;
+ u8 spp; /* bus samples per pixel */
};
struct csid_testgen_config {
+ enum csid_testgen_mode mode;
+ const char * const*modes;
+ u8 nmodes;
u8 enabled;
- enum csid_payload_mode payload_mode;
};
struct csid_phy_config {
@@ -41,6 +83,65 @@ struct csid_phy_config {
u32 lane_assign;
};
+struct csid_device;
+
+struct csid_hw_ops {
+ /*
+ * configure_stream - Configures and starts CSID input stream
+ * @csid: CSID device
+ */
+ void (*configure_stream)(struct csid_device *csid, u8 enable);
+
+ /*
+ * configure_testgen_pattern - Validates and configures output pattern mode
+ * of test pattern generator
+ * @csid: CSID device
+ */
+ int (*configure_testgen_pattern)(struct csid_device *csid, s32 val);
+
+ /*
+ * hw_version - Read hardware version register from hardware
+ * @csid: CSID device
+ */
+ u32 (*hw_version)(struct csid_device *csid);
+
+ /*
+ * isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+ irqreturn_t (*isr)(int irq, void *dev);
+
+ /*
+ * reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+ int (*reset)(struct csid_device *csid);
+
+ /*
+ * src_pad_code - Pick an output/src format based on the input/sink format
+ * @csid: CSID device
+ * @sink_code: The sink format of the input
+ * @match_format_idx: Request preferred index, as defined by subdevice csid_format.
+ * Set @match_code to 0 if used.
+ * @match_code: Request preferred code, set @match_format_idx to 0 if used
+ *
+ * Return 0 on failure or src format code otherwise
+ */
+ u32 (*src_pad_code)(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code);
+
+ /*
+ * subdev_init - Initialize CSID device according for hardware revision
+ * @csid: CSID device
+ */
+ void (*subdev_init)(struct csid_device *csid);
+};
+
struct csid_device {
struct camss *camss;
u8 id;
@@ -60,10 +161,36 @@ struct csid_device {
struct v4l2_ctrl *testgen_mode;
const struct csid_format *formats;
unsigned int nformats;
+ const struct csid_hw_ops *ops;
};
struct resources;
+/*
+ * csid_find_code - Find a format code in an array using array index or format code
+ * @codes: Array of format codes
+ * @ncodes: Length of @code array
+ * @req_format_idx: Request preferred index, as defined by subdevice csid_format.
+ * Set @match_code to 0 if used.
+ * @match_code: Request preferred code, set @req_format_idx to 0 if used
+ *
+ * Return 0 on failure or format code otherwise
+ */
+u32 csid_find_code(u32 *codes, unsigned int ncode,
+ unsigned int match_format_idx, u32 match_code);
+
+/*
+ * csid_get_fmt_entry - Find csid_format entry with matching format code
+ * @formats: Array of format csid_format entries
+ * @nformats: Length of @nformats array
+ * @code: Desired format code
+ *
+ * Return formats[0] on failure to find code
+ */
+const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats,
+ unsigned int nformats,
+ u32 code);
+
int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
const struct resources *res, u8 id);
@@ -74,4 +201,11 @@ void msm_csid_unregister_entity(struct csid_device *csid);
void msm_csid_get_csid_id(struct media_entity *entity, u8 *id);
+extern const char * const csid_testgen_modes[];
+
+extern const struct csid_hw_ops csid_ops_4_1;
+extern const struct csid_hw_ops csid_ops_4_7;
+extern const struct csid_hw_ops csid_ops_170;
+
+
#endif /* QC_MSM_CAMSS_CSID_H */
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
index 12bce391d71f..30b454c369ab 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
@@ -51,16 +51,13 @@ static void csiphy_reset(struct csiphy_device *csiphy)
*
* Helper function to calculate settle count value. This is
* based on the CSI2 T_hs_settle parameter which in turn
- * is calculated based on the CSI2 transmitter pixel clock
- * frequency.
+ * is calculated based on the CSI2 transmitter link frequency.
*
- * Return settle count value or 0 if the CSI2 pixel clock
- * frequency is not available
+ * Return settle count value or 0 if the CSI2 link frequency
+ * is not available
*/
-static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
- u32 timer_clk_rate)
+static u8 csiphy_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
{
- u32 mipi_clock; /* Hz */
u32 ui; /* ps */
u32 timer_period; /* ps */
u32 t_hs_prepare_max; /* ps */
@@ -68,8 +65,10 @@ static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
u32 t_hs_settle; /* ps */
u8 settle_cnt;
- mipi_clock = pixel_clock * bpp / (2 * num_lanes);
- ui = div_u64(1000000000000LL, mipi_clock);
+ if (link_freq <= 0)
+ return 0;
+
+ ui = div_u64(1000000000000LL, link_freq);
ui /= 2;
t_hs_prepare_max = 85000 + 6 * ui;
t_hs_prepare_zero_min = 145000 + 10 * ui;
@@ -83,15 +82,14 @@ static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
static void csiphy_lanes_enable(struct csiphy_device *csiphy,
struct csiphy_config *cfg,
- u32 pixel_clock, u8 bpp, u8 lane_mask)
+ s64 link_freq, u8 lane_mask)
{
struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
u8 settle_cnt;
u8 val, l = 0;
int i = 0;
- settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data,
- csiphy->timer_clk_rate);
+ settle_cnt = csiphy_settle_cnt_calc(link_freq, csiphy->timer_clk_rate);
writel_relaxed(0x1, csiphy->base +
CAMSS_CSI_PHY_GLBL_T_INIT_CFG0);
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index 97cb9de85031..e318c822ab04 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -47,6 +47,105 @@
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n))
+#define CSIPHY_DEFAULT_PARAMS 0
+#define CSIPHY_LANE_ENABLE 1
+#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
+#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
+#define CSIPHY_DNP_PARAMS 4
+#define CSIPHY_2PH_REGS 5
+#define CSIPHY_3PH_REGS 6
+
+struct csiphy_reg_t {
+ s32 reg_addr;
+ s32 reg_data;
+ s32 delay;
+ u32 csiphy_param_type;
+};
+
+static const struct
+csiphy_reg_t lane_regs_sdm845[5][14] = {
+ {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+ {
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ },
+};
+
static void csiphy_hw_version_read(struct csiphy_device *csiphy,
struct device *dev)
{
@@ -107,24 +206,23 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
*
* Helper function to calculate settle count value. This is
* based on the CSI2 T_hs_settle parameter which in turn
- * is calculated based on the CSI2 transmitter pixel clock
- * frequency.
+ * is calculated based on the CSI2 transmitter link frequency.
*
- * Return settle count value or 0 if the CSI2 pixel clock
- * frequency is not available
+ * Return settle count value or 0 if the CSI2 link frequency
+ * is not available
*/
-static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
- u32 timer_clk_rate)
+static u8 csiphy_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
{
- u32 mipi_clock; /* Hz */
u32 ui; /* ps */
u32 timer_period; /* ps */
u32 t_hs_prepare_max; /* ps */
u32 t_hs_settle; /* ps */
u8 settle_cnt;
- mipi_clock = pixel_clock * bpp / (2 * num_lanes);
- ui = div_u64(1000000000000LL, mipi_clock);
+ if (link_freq <= 0)
+ return 0;
+
+ ui = div_u64(1000000000000LL, link_freq);
ui /= 2;
t_hs_prepare_max = 85000 + 6 * ui;
t_hs_settle = t_hs_prepare_max;
@@ -135,26 +233,13 @@ static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
return settle_cnt;
}
-static void csiphy_lanes_enable(struct csiphy_device *csiphy,
- struct csiphy_config *cfg,
- u32 pixel_clock, u8 bpp, u8 lane_mask)
+static void csiphy_gen1_config_lanes(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg,
+ u8 settle_cnt)
{
struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
- u8 settle_cnt;
- u8 val, l = 0;
- int i;
-
- settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data,
- csiphy->timer_clk_rate);
-
- val = BIT(c->clk.pos);
- for (i = 0; i < c->num_data; i++)
- val |= BIT(c->data[i].pos * 2);
-
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
-
- val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ int i, l = 0;
+ u8 val;
for (i = 0; i <= c->num_data; i++) {
if (i == c->num_data)
@@ -208,6 +293,64 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE;
writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_MISC1(l));
+}
+
+static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
+ u8 settle_cnt)
+{
+ int i, l;
+ u32 val;
+
+ for (l = 0; l < 5; l++) {
+ for (i = 0; i < 14; i++) {
+ const struct csiphy_reg_t *r = &lane_regs_sdm845[l][i];
+
+ switch (r->csiphy_param_type) {
+ case CSIPHY_SETTLE_CNT_LOWER_BYTE:
+ val = settle_cnt & 0xff;
+ break;
+ case CSIPHY_DNP_PARAMS:
+ continue;
+ default:
+ val = r->reg_data;
+ break;
+ }
+ writel_relaxed(val, csiphy->base + r->reg_addr);
+ }
+ }
+}
+
+static void csiphy_lanes_enable(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg,
+ s64 link_freq, u8 lane_mask)
+{
+ struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
+ u8 settle_cnt;
+ u8 val;
+ int i;
+
+ settle_cnt = csiphy_settle_cnt_calc(link_freq, csiphy->timer_clk_rate);
+
+ val = BIT(c->clk.pos);
+ for (i = 0; i < c->num_data; i++)
+ val |= BIT(c->data[i].pos * 2);
+
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+
+ val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+
+ val = 0x02;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(7));
+
+ val = 0x00;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+
+ if (csiphy->camss->version == CAMSS_8x16 ||
+ csiphy->camss->version == CAMSS_8x96)
+ csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt);
+ else if (csiphy->camss->version == CAMSS_845)
+ csiphy_gen2_config_lanes(csiphy, settle_cnt);
val = 0xff;
writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11));
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 509c9a59c09c..b3c3bf19e522 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -73,6 +73,30 @@ static const struct csiphy_format csiphy_formats_8x96[] = {
{ MEDIA_BUS_FMT_Y10_1X10, 10 },
};
+static const struct csiphy_format csiphy_formats_sdm845[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
+ { MEDIA_BUS_FMT_Y10_1X10, 10 },
+};
+
/*
* csiphy_get_bpp - map media bus format to bits per pixel
* @formats: supported media bus formats array
@@ -102,23 +126,23 @@ static u8 csiphy_get_bpp(const struct csiphy_format *formats,
static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
{
struct device *dev = csiphy->camss->dev;
- u32 pixel_clock;
+ s64 link_freq;
int i, j;
int ret;
- ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
- if (ret)
- pixel_clock = 0;
+ u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
+ csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
+ u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
+
+ link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes);
+ if (link_freq < 0)
+ link_freq = 0;
for (i = 0; i < csiphy->nclocks; i++) {
struct camss_clock *clock = &csiphy->clock[i];
if (csiphy->rate_set[i]) {
- u8 bpp = csiphy_get_bpp(csiphy->formats,
- csiphy->nformats,
- csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
- u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
- u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
+ u64 min_rate = link_freq / 4;
long round_rate;
camss_add_clock_margin(&min_rate);
@@ -238,37 +262,37 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
static int csiphy_stream_on(struct csiphy_device *csiphy)
{
struct csiphy_config *cfg = &csiphy->cfg;
- u32 pixel_clock;
+ s64 link_freq;
u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg);
u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
+ u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
u8 val;
- int ret;
- ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
- if (ret) {
- dev_err(csiphy->camss->dev,
- "Cannot get CSI2 transmitter's pixel clock\n");
- return -EINVAL;
- }
- if (!pixel_clock) {
+ link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes);
+
+ if (link_freq < 0) {
dev_err(csiphy->camss->dev,
- "Got pixel clock == 0, cannot continue\n");
+ "Cannot get CSI2 transmitter's link frequency\n");
return -EINVAL;
}
- val = readl_relaxed(csiphy->base_clk_mux);
- if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
- val &= ~0xf0;
- val |= cfg->csid_id << 4;
- } else {
- val &= ~0xf;
- val |= cfg->csid_id;
+ if (csiphy->base_clk_mux) {
+ val = readl_relaxed(csiphy->base_clk_mux);
+ if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
+ val &= ~0xf0;
+ val |= cfg->csid_id << 4;
+ } else {
+ val &= ~0xf;
+ val |= cfg->csid_id;
+ }
+ writel_relaxed(val, csiphy->base_clk_mux);
+
+ /* Enforce reg write ordering between clk mux & lane enabling */
+ wmb();
}
- writel_relaxed(val, csiphy->base_clk_mux);
- wmb();
- csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask);
+ csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask);
return 0;
}
@@ -557,6 +581,10 @@ int msm_csiphy_subdev_init(struct camss *camss,
csiphy->ops = &csiphy_ops_3ph_1_0;
csiphy->formats = csiphy_formats_8x96;
csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96);
+ } else if (camss->version == CAMSS_845) {
+ csiphy->ops = &csiphy_ops_3ph_1_0;
+ csiphy->formats = csiphy_formats_sdm845;
+ csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845);
} else {
return -EINVAL;
}
@@ -565,16 +593,18 @@ int msm_csiphy_subdev_init(struct camss *camss,
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
csiphy->base = devm_ioremap_resource(dev, r);
- if (IS_ERR(csiphy->base)) {
- dev_err(dev, "could not map memory\n");
+ if (IS_ERR(csiphy->base))
return PTR_ERR(csiphy->base);
- }
- r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
- csiphy->base_clk_mux = devm_ioremap_resource(dev, r);
- if (IS_ERR(csiphy->base_clk_mux)) {
- dev_err(dev, "could not map memory\n");
- return PTR_ERR(csiphy->base_clk_mux);
+ if (camss->version == CAMSS_8x16 ||
+ camss->version == CAMSS_8x96) {
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ res->reg[1]);
+ csiphy->base_clk_mux = devm_ioremap_resource(dev, r);
+ if (IS_ERR(csiphy->base_clk_mux))
+ return PTR_ERR(csiphy->base_clk_mux);
+ } else {
+ csiphy->base_clk_mux = NULL;
}
/* Interrupt */
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index f7967ef836dc..d71b8bc6ec00 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -50,7 +50,7 @@ struct csiphy_hw_ops {
void (*reset)(struct csiphy_device *csiphy);
void (*lanes_enable)(struct csiphy_device *csiphy,
struct csiphy_config *cfg,
- u32 pixel_clock, u8 bpp, u8 lane_mask);
+ s64 link_freq, u8 lane_mask);
void (*lanes_disable)(struct csiphy_device *csiphy,
struct csiphy_config *cfg);
irqreturn_t (*isr)(int irq, void *dev);
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index adeb92808998..37611c8861da 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -161,6 +161,7 @@ static const u32 ispif_formats_8x96[] = {
static irqreturn_t ispif_isr_8x96(int irq, void *dev)
{
struct ispif_device *ispif = dev;
+ struct camss *camss = ispif->camss;
u32 value0, value1, value2, value3, value4, value5;
value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0));
@@ -186,34 +187,34 @@ static irqreturn_t ispif_isr_8x96(int irq, void *dev)
complete(&ispif->reset_complete[1]);
if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 pix0 overflow\n");
if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 rdi0 overflow\n");
if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 pix1 overflow\n");
if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 rdi1 overflow\n");
if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 rdi2 overflow\n");
if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE1 pix0 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE1 pix0 overflow\n");
if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE1 rdi0 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE1 rdi0 overflow\n");
if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE1 pix1 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE1 pix1 overflow\n");
if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE1 rdi1 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE1 rdi1 overflow\n");
if (unlikely(value5 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE1 rdi2 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE1 rdi2 overflow\n");
return IRQ_HANDLED;
}
@@ -228,6 +229,7 @@ static irqreturn_t ispif_isr_8x96(int irq, void *dev)
static irqreturn_t ispif_isr_8x16(int irq, void *dev)
{
struct ispif_device *ispif = dev;
+ struct camss *camss = ispif->camss;
u32 value0, value1, value2;
value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0));
@@ -244,30 +246,32 @@ static irqreturn_t ispif_isr_8x16(int irq, void *dev)
complete(&ispif->reset_complete[0]);
if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 pix0 overflow\n");
if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 rdi0 overflow\n");
if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 pix1 overflow\n");
if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 rdi1 overflow\n");
if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
- dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n");
+ dev_err_ratelimited(camss->dev, "VFE0 rdi2 overflow\n");
return IRQ_HANDLED;
}
static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id)
{
+ struct camss *camss = ispif->camss;
+
unsigned long time;
u32 val;
- if (vfe_id > (to_camss(ispif)->vfe_num - 1)) {
- dev_err(to_device(ispif),
+ if (vfe_id > (camss->vfe_num - 1)) {
+ dev_err(camss->dev,
"Error: asked reset for invalid VFE%d\n", vfe_id);
return -ENOENT;
}
@@ -300,7 +304,7 @@ static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id)
time = wait_for_completion_timeout(&ispif->reset_complete[vfe_id],
msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
if (!time) {
- dev_err(to_device(ispif),
+ dev_err(camss->dev,
"ISPIF for VFE%d reset timeout\n", vfe_id);
return -EIO;
}
@@ -316,30 +320,31 @@ static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id)
*/
static int ispif_reset(struct ispif_device *ispif, u8 vfe_id)
{
+ struct camss *camss = ispif->camss;
int ret;
- ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0);
+ ret = camss_pm_domain_on(camss, PM_DOMAIN_VFE0);
if (ret < 0)
return ret;
- ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1);
+ ret = camss_pm_domain_on(camss, PM_DOMAIN_VFE1);
if (ret < 0)
return ret;
ret = camss_enable_clocks(ispif->nclocks_for_reset,
ispif->clock_for_reset,
- to_device(ispif));
+ camss->dev);
if (ret < 0)
return ret;
ret = ispif_vfe_reset(ispif, vfe_id);
if (ret)
- dev_dbg(to_device(ispif), "ISPIF Reset failed\n");
+ dev_dbg(camss->dev, "ISPIF Reset failed\n");
camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);
- camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0);
- camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1);
+ camss_pm_domain_off(camss, PM_DOMAIN_VFE0);
+ camss_pm_domain_off(camss, PM_DOMAIN_VFE1);
return ret;
}
@@ -355,7 +360,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
{
struct ispif_line *line = v4l2_get_subdevdata(sd);
struct ispif_device *ispif = line->ispif;
- struct device *dev = to_device(ispif);
+ struct device *dev = ispif->camss->dev;
int ret = 0;
mutex_lock(&ispif->power_lock);
@@ -505,7 +510,7 @@ static int ispif_validate_intf_status(struct ispif_device *ispif,
}
if ((val & 0xf) != 0xf) {
- dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n",
+ dev_err(ispif->camss->dev, "%s: ispif is busy: 0x%x\n",
__func__, val);
ret = -EBUSY;
}
@@ -552,7 +557,7 @@ static int ispif_wait_for_stop(struct ispif_device *ispif,
ISPIF_TIMEOUT_SLEEP_US,
ISPIF_TIMEOUT_ALL_US);
if (ret < 0)
- dev_err(to_device(ispif), "%s: ispif stop timeout\n",
+ dev_err(ispif->camss->dev, "%s: ispif stop timeout\n",
__func__);
return ret;
@@ -800,6 +805,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
{
struct ispif_line *line = v4l2_get_subdevdata(sd);
struct ispif_device *ispif = line->ispif;
+ struct camss *camss = ispif->camss;
enum ispif_intf intf = line->interface;
u8 csid = line->csid_id;
u8 vfe = line->vfe_id;
@@ -825,8 +831,8 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
ispif_select_csid(ispif, intf, csid, vfe, 1);
ispif_select_cid(ispif, intf, cid, vfe, 1);
ispif_config_irq(ispif, intf, vfe, 1);
- if (to_camss(ispif)->version == CAMSS_8x96 ||
- to_camss(ispif)->version == CAMSS_660)
+ if (camss->version == CAMSS_8x96 ||
+ camss->version == CAMSS_660)
ispif_config_pack(ispif,
line->fmt[MSM_ISPIF_PAD_SINK].code,
intf, cid, vfe, 1);
@@ -843,8 +849,8 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
return ret;
mutex_lock(&ispif->config_lock);
- if (to_camss(ispif)->version == CAMSS_8x96 ||
- to_camss(ispif)->version == CAMSS_660)
+ if (camss->version == CAMSS_8x96 ||
+ camss->version == CAMSS_660)
ispif_config_pack(ispif,
line->fmt[MSM_ISPIF_PAD_SINK].code,
intf, cid, vfe, 0);
@@ -1088,26 +1094,32 @@ static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
*
* Return 0 on success or a negative error code otherwise
*/
-int msm_ispif_subdev_init(struct ispif_device *ispif,
+int msm_ispif_subdev_init(struct camss *camss,
const struct resources_ispif *res)
{
- struct device *dev = to_device(ispif);
+ struct device *dev = camss->dev;
+ struct ispif_device *ispif = camss->ispif;
struct platform_device *pdev = to_platform_device(dev);
struct resource *r;
int i;
int ret;
+ if (!camss->ispif)
+ return 0;
+
+ ispif->camss = camss;
+
/* Number of ISPIF lines - same as number of CSID hardware modules */
- if (to_camss(ispif)->version == CAMSS_8x16)
+ if (camss->version == CAMSS_8x16)
ispif->line_num = 2;
- else if (to_camss(ispif)->version == CAMSS_8x96 ||
- to_camss(ispif)->version == CAMSS_660)
+ else if (camss->version == CAMSS_8x96 ||
+ camss->version == CAMSS_660)
ispif->line_num = 4;
else
return -EINVAL;
- ispif->line = devm_kcalloc(dev, ispif->line_num, sizeof(*ispif->line),
- GFP_KERNEL);
+ ispif->line = devm_kcalloc(dev, ispif->line_num,
+ sizeof(*ispif->line), GFP_KERNEL);
if (!ispif->line)
return -ENOMEM;
@@ -1115,12 +1127,12 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
ispif->line[i].ispif = ispif;
ispif->line[i].id = i;
- if (to_camss(ispif)->version == CAMSS_8x16) {
+ if (camss->version == CAMSS_8x16) {
ispif->line[i].formats = ispif_formats_8x16;
ispif->line[i].nformats =
ARRAY_SIZE(ispif_formats_8x16);
- } else if (to_camss(ispif)->version == CAMSS_8x96 ||
- to_camss(ispif)->version == CAMSS_660) {
+ } else if (camss->version == CAMSS_8x96 ||
+ camss->version == CAMSS_660) {
ispif->line[i].formats = ispif_formats_8x96;
ispif->line[i].nformats =
ARRAY_SIZE(ispif_formats_8x96);
@@ -1133,17 +1145,13 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
ispif->base = devm_ioremap_resource(dev, r);
- if (IS_ERR(ispif->base)) {
- dev_err(dev, "could not map memory\n");
+ if (IS_ERR(ispif->base))
return PTR_ERR(ispif->base);
- }
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
ispif->base_clk_mux = devm_ioremap_resource(dev, r);
- if (IS_ERR(ispif->base_clk_mux)) {
- dev_err(dev, "could not map memory\n");
+ if (IS_ERR(ispif->base_clk_mux))
return PTR_ERR(ispif->base_clk_mux);
- }
/* Interrupt */
@@ -1157,15 +1165,16 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
ispif->irq = r->start;
snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s",
dev_name(dev), MSM_ISPIF_NAME);
- if (to_camss(ispif)->version == CAMSS_8x16)
+ if (camss->version == CAMSS_8x16)
ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16,
IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
- else if (to_camss(ispif)->version == CAMSS_8x96 ||
- to_camss(ispif)->version == CAMSS_660)
+ else if (camss->version == CAMSS_8x96 ||
+ camss->version == CAMSS_660)
ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96,
IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
else
ret = -EINVAL;
+
if (ret < 0) {
dev_err(dev, "request_irq failed: %d\n", ret);
return ret;
@@ -1331,10 +1340,15 @@ static const struct media_entity_operations ispif_media_ops = {
int msm_ispif_register_entities(struct ispif_device *ispif,
struct v4l2_device *v4l2_dev)
{
- struct device *dev = to_device(ispif);
+ struct camss *camss;
int ret;
int i;
+ if (!ispif)
+ return 0;
+
+ camss = ispif->camss;
+
for (i = 0; i < ispif->line_num; i++) {
struct v4l2_subdev *sd = &ispif->line[i].subdev;
struct media_pad *pads = ispif->line[i].pads;
@@ -1348,7 +1362,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif,
ret = ispif_init_formats(sd, NULL);
if (ret < 0) {
- dev_err(dev, "Failed to init format: %d\n", ret);
+ dev_err(camss->dev, "Failed to init format: %d\n", ret);
goto error;
}
@@ -1360,13 +1374,15 @@ int msm_ispif_register_entities(struct ispif_device *ispif,
ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM,
pads);
if (ret < 0) {
- dev_err(dev, "Failed to init media entity: %d\n", ret);
+ dev_err(camss->dev, "Failed to init media entity: %d\n",
+ ret);
goto error;
}
ret = v4l2_device_register_subdev(v4l2_dev, sd);
if (ret < 0) {
- dev_err(dev, "Failed to register subdev: %d\n", ret);
+ dev_err(camss->dev, "Failed to register subdev: %d\n",
+ ret);
media_entity_cleanup(&sd->entity);
goto error;
}
@@ -1393,6 +1409,9 @@ void msm_ispif_unregister_entities(struct ispif_device *ispif)
{
int i;
+ if (!ispif)
+ return;
+
mutex_destroy(&ispif->power_lock);
mutex_destroy(&ispif->config_lock);
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h
index 4132174f7ea1..fdf28e68cc7d 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.h
+++ b/drivers/media/platform/qcom/camss/camss-ispif.h
@@ -63,11 +63,12 @@ struct ispif_device {
struct mutex config_lock;
unsigned int line_num;
struct ispif_line *line;
+ struct camss *camss;
};
struct resources_ispif;
-int msm_ispif_subdev_init(struct ispif_device *ispif,
+int msm_ispif_subdev_init(struct camss *camss,
const struct resources_ispif *res);
int msm_ispif_register_entities(struct ispif_device *ispif,
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c
new file mode 100644
index 000000000000..8594d275b41d
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c
@@ -0,0 +1,786 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-170.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v170
+ *
+ * Copyright (C) 2020-2021 Linaro Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+
+#define VFE_HW_VERSION (0x000)
+
+#define VFE_GLOBAL_RESET_CMD (0x018)
+#define GLOBAL_RESET_CMD_CORE BIT(0)
+#define GLOBAL_RESET_CMD_CAMIF BIT(1)
+#define GLOBAL_RESET_CMD_BUS BIT(2)
+#define GLOBAL_RESET_CMD_BUS_BDG BIT(3)
+#define GLOBAL_RESET_CMD_REGISTER BIT(4)
+#define GLOBAL_RESET_CMD_PM BIT(5)
+#define GLOBAL_RESET_CMD_BUS_MISR BIT(6)
+#define GLOBAL_RESET_CMD_TESTGEN BIT(7)
+#define GLOBAL_RESET_CMD_DSP BIT(8)
+#define GLOBAL_RESET_CMD_IDLE_CGC BIT(9)
+#define GLOBAL_RESET_CMD_RDI0 BIT(10)
+#define GLOBAL_RESET_CMD_RDI1 BIT(11)
+#define GLOBAL_RESET_CMD_RDI2 BIT(12)
+#define GLOBAL_RESET_CMD_RDI3 BIT(13)
+#define GLOBAL_RESET_CMD_VFE_DOMAIN BIT(30)
+#define GLOBAL_RESET_CMD_RESET_BYPASS BIT(31)
+
+#define VFE_CORE_CFG (0x050)
+#define CFG_PIXEL_PATTERN_YCBYCR (0x4)
+#define CFG_PIXEL_PATTERN_YCRYCB (0x5)
+#define CFG_PIXEL_PATTERN_CBYCRY (0x6)
+#define CFG_PIXEL_PATTERN_CRYCBY (0x7)
+#define CFG_COMPOSITE_REG_UPDATE_EN BIT(4)
+
+#define VFE_IRQ_CMD (0x058)
+#define CMD_GLOBAL_CLEAR BIT(0)
+
+#define VFE_IRQ_MASK_0 (0x05c)
+#define MASK_0_CAMIF_SOF BIT(0)
+#define MASK_0_CAMIF_EOF BIT(1)
+#define MASK_0_RDI_REG_UPDATE(n) BIT((n) + 5)
+#define MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define MASK_0_RESET_ACK BIT(31)
+
+#define VFE_IRQ_MASK_1 (0x060)
+#define MASK_1_CAMIF_ERROR BIT(0)
+#define MASK_1_VIOLATION BIT(7)
+#define MASK_1_BUS_BDG_HALT_ACK BIT(8)
+#define MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9)
+#define MASK_1_RDI_SOF(n) BIT((n) + 29)
+
+#define VFE_IRQ_CLEAR_0 (0x064)
+#define VFE_IRQ_CLEAR_1 (0x068)
+
+#define VFE_IRQ_STATUS_0 (0x06c)
+#define STATUS_0_CAMIF_SOF BIT(0)
+#define STATUS_0_RDI_REG_UPDATE(n) BIT((n) + 5)
+#define STATUS_0_IMAGE_MASTER_PING_PONG(n) BIT((n) + 8)
+#define STATUS_0_IMAGE_COMPOSITE_DONE(n) BIT((n) + 25)
+#define STATUS_0_RESET_ACK BIT(31)
+
+#define VFE_IRQ_STATUS_1 (0x070)
+#define STATUS_1_VIOLATION BIT(7)
+#define STATUS_1_BUS_BDG_HALT_ACK BIT(8)
+#define STATUS_1_RDI_SOF(n) BIT((n) + 27)
+
+#define VFE_VIOLATION_STATUS (0x07c)
+
+#define VFE_CAMIF_CMD (0x478)
+#define CMD_CLEAR_CAMIF_STATUS BIT(2)
+
+#define VFE_CAMIF_CFG (0x47c)
+#define CFG_VSYNC_SYNC_EDGE (0)
+#define VSYNC_ACTIVE_HIGH (0)
+#define VSYNC_ACTIVE_LOW (1)
+#define CFG_HSYNC_SYNC_EDGE (1)
+#define HSYNC_ACTIVE_HIGH (0)
+#define HSYNC_ACTIVE_LOW (1)
+#define CFG_VFE_SUBSAMPLE_ENABLE BIT(4)
+#define CFG_BUS_SUBSAMPLE_ENABLE BIT(5)
+#define CFG_VFE_OUTPUT_EN BIT(6)
+#define CFG_BUS_OUTPUT_EN BIT(7)
+#define CFG_BINNING_EN BIT(9)
+#define CFG_FRAME_BASED_EN BIT(10)
+#define CFG_RAW_CROP_EN BIT(22)
+
+#define VFE_REG_UPDATE_CMD (0x4ac)
+#define REG_UPDATE_RDI(n) BIT(1 + (n))
+
+#define VFE_BUS_IRQ_MASK(n) (0x2044 + (n) * 4)
+#define VFE_BUS_IRQ_CLEAR(n) (0x2050 + (n) * 4)
+#define VFE_BUS_IRQ_STATUS(n) (0x205c + (n) * 4)
+#define STATUS0_COMP_RESET_DONE BIT(0)
+#define STATUS0_COMP_REG_UPDATE0_DONE BIT(1)
+#define STATUS0_COMP_REG_UPDATE1_DONE BIT(2)
+#define STATUS0_COMP_REG_UPDATE2_DONE BIT(3)
+#define STATUS0_COMP_REG_UPDATE3_DONE BIT(4)
+#define STATUS0_COMP_REG_UPDATE_DONE(n) BIT((n) + 1)
+#define STATUS0_COMP0_BUF_DONE BIT(5)
+#define STATUS0_COMP1_BUF_DONE BIT(6)
+#define STATUS0_COMP2_BUF_DONE BIT(7)
+#define STATUS0_COMP3_BUF_DONE BIT(8)
+#define STATUS0_COMP4_BUF_DONE BIT(9)
+#define STATUS0_COMP5_BUF_DONE BIT(10)
+#define STATUS0_COMP_BUF_DONE(n) BIT((n) + 5)
+#define STATUS0_COMP_ERROR BIT(11)
+#define STATUS0_COMP_OVERWRITE BIT(12)
+#define STATUS0_OVERFLOW BIT(13)
+#define STATUS0_VIOLATION BIT(14)
+/* WM_CLIENT_BUF_DONE defined for buffers 0:19 */
+#define STATUS1_WM_CLIENT_BUF_DONE(n) BIT(n)
+#define STATUS1_EARLY_DONE BIT(24)
+#define STATUS2_DUAL_COMP0_BUF_DONE BIT(0)
+#define STATUS2_DUAL_COMP1_BUF_DONE BIT(1)
+#define STATUS2_DUAL_COMP2_BUF_DONE BIT(2)
+#define STATUS2_DUAL_COMP3_BUF_DONE BIT(3)
+#define STATUS2_DUAL_COMP4_BUF_DONE BIT(4)
+#define STATUS2_DUAL_COMP5_BUF_DONE BIT(5)
+#define STATUS2_DUAL_COMP_BUF_DONE(n) BIT(n)
+#define STATUS2_DUAL_COMP_ERROR BIT(6)
+#define STATUS2_DUAL_COMP_OVERWRITE BIT(7)
+
+#define VFE_BUS_IRQ_CLEAR_GLOBAL (0x2068)
+
+#define VFE_BUS_WM_DEBUG_STATUS_CFG (0x226c)
+#define DEBUG_STATUS_CFG_STATUS0(n) BIT(n)
+#define DEBUG_STATUS_CFG_STATUS1(n) BIT(8 + (n))
+
+#define VFE_BUS_WM_ADDR_SYNC_FRAME_HEADER (0x2080)
+
+#define VFE_BUS_WM_ADDR_SYNC_NO_SYNC (0x2084)
+#define BUS_VER2_MAX_CLIENTS (24)
+#define WM_ADDR_NO_SYNC_DEFAULT_VAL \
+ ((1 << BUS_VER2_MAX_CLIENTS) - 1)
+
+#define VFE_BUS_WM_CGC_OVERRIDE (0x200c)
+#define WM_CGC_OVERRIDE_ALL (0xFFFFF)
+
+#define VFE_BUS_WM_TEST_BUS_CTRL (0x211c)
+
+#define VFE_BUS_WM_STATUS0(n) (0x2200 + (n) * 0x100)
+#define VFE_BUS_WM_STATUS1(n) (0x2204 + (n) * 0x100)
+#define VFE_BUS_WM_CFG(n) (0x2208 + (n) * 0x100)
+#define WM_CFG_EN (0)
+#define WM_CFG_MODE (1)
+#define MODE_QCOM_PLAIN (0)
+#define MODE_MIPI_RAW (1)
+#define WM_CFG_VIRTUALFRAME (2)
+#define VFE_BUS_WM_HEADER_ADDR(n) (0x220c + (n) * 0x100)
+#define VFE_BUS_WM_HEADER_CFG(n) (0x2210 + (n) * 0x100)
+#define VFE_BUS_WM_IMAGE_ADDR(n) (0x2214 + (n) * 0x100)
+#define VFE_BUS_WM_IMAGE_ADDR_OFFSET(n) (0x2218 + (n) * 0x100)
+#define VFE_BUS_WM_BUFFER_WIDTH_CFG(n) (0x221c + (n) * 0x100)
+#define WM_BUFFER_DEFAULT_WIDTH (0xFF01)
+
+#define VFE_BUS_WM_BUFFER_HEIGHT_CFG(n) (0x2220 + (n) * 0x100)
+#define VFE_BUS_WM_PACKER_CFG(n) (0x2224 + (n) * 0x100)
+
+#define VFE_BUS_WM_STRIDE(n) (0x2228 + (n) * 0x100)
+#define WM_STRIDE_DEFAULT_STRIDE (0xFF01)
+
+#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (0x2248 + (n) * 0x100)
+#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (0x224c + (n) * 0x100)
+#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (0x2250 + (n) * 0x100)
+#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (0x2254 + (n) * 0x100)
+#define VFE_BUS_WM_FRAME_INC(n) (0x2258 + (n) * 0x100)
+#define VFE_BUS_WM_BURST_LIMIT(n) (0x225c + (n) * 0x100)
+
+static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
+
+ u32 gen = (hw_version >> 28) & 0xF;
+ u32 rev = (hw_version >> 16) & 0xFFF;
+ u32 step = hw_version & 0xFFFF;
+
+ dev_err(dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step);
+}
+
+static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits & ~clr_bits, vfe->base + reg);
+}
+
+static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits | set_bits, vfe->base + reg);
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ u32 reset_bits = GLOBAL_RESET_CMD_CORE |
+ GLOBAL_RESET_CMD_CAMIF |
+ GLOBAL_RESET_CMD_BUS |
+ GLOBAL_RESET_CMD_BUS_BDG |
+ GLOBAL_RESET_CMD_REGISTER |
+ GLOBAL_RESET_CMD_TESTGEN |
+ GLOBAL_RESET_CMD_DSP |
+ GLOBAL_RESET_CMD_IDLE_CGC |
+ GLOBAL_RESET_CMD_RDI0 |
+ GLOBAL_RESET_CMD_RDI1 |
+ GLOBAL_RESET_CMD_RDI2;
+
+ writel_relaxed(BIT(31), vfe->base + VFE_IRQ_MASK_0);
+
+ /* Make sure IRQ mask has been written before resetting */
+ wmb();
+
+ writel_relaxed(reset_bits, vfe->base + VFE_GLOBAL_RESET_CMD);
+}
+
+static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
+{
+ u32 val;
+
+ /*Set Debug Registers*/
+ val = DEBUG_STATUS_CFG_STATUS0(1) |
+ DEBUG_STATUS_CFG_STATUS0(7);
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_DEBUG_STATUS_CFG);
+
+ /* BUS_WM_INPUT_IF_ADDR_SYNC_FRAME_HEADER */
+ writel_relaxed(0, vfe->base + VFE_BUS_WM_ADDR_SYNC_FRAME_HEADER);
+
+ /* no clock gating at bus input */
+ val = WM_CGC_OVERRIDE_ALL;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
+
+ writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
+
+ /* if addr_no_sync has default value then config the addr no sync reg */
+ val = WM_ADDR_NO_SYNC_DEFAULT_VAL;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_ADDR_SYNC_NO_SYNC);
+
+ writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm));
+
+ val = WM_BUFFER_DEFAULT_WIDTH;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_BUFFER_WIDTH_CFG(wm));
+
+ val = 0;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_BUFFER_HEIGHT_CFG(wm));
+
+ val = 0;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_PACKER_CFG(wm)); // XXX 1 for PLAIN8?
+
+ /* Configure stride for RDIs */
+ val = WM_STRIDE_DEFAULT_STRIDE;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_STRIDE(wm));
+
+ /* Enable WM */
+ val = 1 << WM_CFG_EN |
+ MODE_MIPI_RAW << WM_CFG_MODE;
+ writel_relaxed(val, vfe->base + VFE_BUS_WM_CFG(wm));
+}
+
+static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
+{
+ /* Disable WM */
+ writel_relaxed(0, vfe->base + VFE_BUS_WM_CFG(wm));
+}
+
+static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
+ struct vfe_line *line)
+{
+ struct v4l2_pix_format_mplane *pix =
+ &line->video_out.active_fmt.fmt.pix_mp;
+ u32 stride = pix->plane_fmt[0].bytesperline;
+
+ writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
+ writel_relaxed(stride * pix->height, vfe->base + VFE_BUS_WM_FRAME_INC(wm));
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ vfe->reg_update |= REG_UPDATE_RDI(line_id);
+
+ /* Enforce ordering between previous reg writes and reg update */
+ wmb();
+
+ writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD);
+
+ /* Enforce ordering between reg update and subsequent reg writes */
+ wmb();
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ vfe->reg_update &= ~REG_UPDATE_RDI(line_id);
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+ vfe_reg_set(vfe, VFE_IRQ_MASK_0, ~0u);
+ vfe_reg_set(vfe, VFE_IRQ_MASK_1, ~0u);
+
+ writel_relaxed(~0u, vfe->base + VFE_BUS_IRQ_MASK(0));
+ writel_relaxed(~0u, vfe->base + VFE_BUS_IRQ_MASK(1));
+ writel_relaxed(~0u, vfe->base + VFE_BUS_IRQ_MASK(2));
+}
+
+static void vfe_isr_halt_ack(struct vfe_device *vfe)
+{
+ complete(&vfe->halt_complete);
+}
+
+static void vfe_isr_read(struct vfe_device *vfe, u32 *status0, u32 *status1)
+{
+ *status0 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_0);
+ *status1 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_1);
+
+ writel_relaxed(*status0, vfe->base + VFE_IRQ_CLEAR_0);
+ writel_relaxed(*status1, vfe->base + VFE_IRQ_CLEAR_1);
+
+ /* Enforce ordering between IRQ Clear and Global IRQ Clear */
+ wmb();
+ writel_relaxed(CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD);
+}
+
+static void vfe_violation_read(struct vfe_device *vfe)
+{
+ u32 violation = readl_relaxed(vfe->base + VFE_VIOLATION_STATUS);
+
+ pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
+}
+
+/*
+ * vfe_isr - VFE module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ struct vfe_device *vfe = dev;
+ u32 status0, status1, vfe_bus_status[3];
+ int i, wm;
+
+ status0 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_0);
+ status1 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_1);
+
+ writel_relaxed(status0, vfe->base + VFE_IRQ_CLEAR_0);
+ writel_relaxed(status1, vfe->base + VFE_IRQ_CLEAR_1);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) {
+ vfe_bus_status[i] = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(i));
+ writel_relaxed(vfe_bus_status[i], vfe->base + VFE_BUS_IRQ_CLEAR(i));
+ }
+
+ /* Enforce ordering between IRQ reading and interpretation */
+ wmb();
+
+ writel_relaxed(CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD);
+ writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL);
+
+ if (status0 & STATUS_0_RESET_ACK)
+ vfe->isr_ops.reset_ack(vfe);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+ if (status0 & STATUS_0_RDI_REG_UPDATE(i))
+ vfe->isr_ops.reg_update(vfe, i);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+ if (status0 & STATUS_1_RDI_SOF(i))
+ vfe->isr_ops.sof(vfe, i);
+
+ for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
+ if (vfe_bus_status[0] & STATUS0_COMP_BUF_DONE(i))
+ vfe->isr_ops.comp_done(vfe, i);
+
+ for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++)
+ if (status0 & BIT(9))
+ if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
+ vfe->isr_ops.wm_done(vfe, wm);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * vfe_halt - Trigger halt on VFE module and wait to complete
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_halt(struct vfe_device *vfe)
+{
+ unsigned long time;
+
+ reinit_completion(&vfe->halt_complete);
+
+ time = wait_for_completion_timeout(&vfe->halt_complete,
+ msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
+ if (!time) {
+ dev_err(vfe->camss->dev, "VFE halt timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int vfe_get_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ unsigned long flags;
+ int wm_idx;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ output = &line->output;
+ if (output->state != VFE_OUTPUT_OFF) {
+ dev_err(vfe->camss->dev, "Output is running\n");
+ goto error;
+ }
+
+ output->wm_num = 1;
+
+ wm_idx = vfe_reserve_wm(vfe, line->id);
+ if (wm_idx < 0) {
+ dev_err(vfe->camss->dev, "Can not reserve wm\n");
+ goto error_get_wm;
+ }
+ output->wm_idx[0] = wm_idx;
+
+ output->drop_update_idx = 0;
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+
+error_get_wm:
+ vfe_release_wm(vfe, output->wm_idx[0]);
+ output->state = VFE_OUTPUT_OFF;
+error:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return -EINVAL;
+}
+
+static int vfe_enable_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->ops;
+ struct media_entity *sensor;
+ unsigned long flags;
+ unsigned int frame_skip = 0;
+ unsigned int i;
+
+ sensor = camss_find_sensor(&line->subdev.entity);
+ if (sensor) {
+ struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
+
+ v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
+ /* Max frame skip is 29 frames */
+ if (frame_skip > VFE_FRAME_DROP_VAL - 1)
+ frame_skip = VFE_FRAME_DROP_VAL - 1;
+ }
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ ops->reg_update_clear(vfe, line->id);
+
+ if (output->state != VFE_OUTPUT_OFF) {
+ dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
+ output->state);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ return -EINVAL;
+ }
+
+ WARN_ON(output->gen2.active_num);
+
+ output->state = VFE_OUTPUT_ON;
+
+ output->sequence = 0;
+ output->wait_reg_update = 0;
+ reinit_completion(&output->reg_update);
+
+ vfe_wm_start(vfe, output->wm_idx[0], line);
+
+ for (i = 0; i < 2; i++) {
+ output->buf[i] = vfe_buf_get_pending(output);
+ if (!output->buf[i])
+ break;
+ output->gen2.active_num++;
+ vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
+ }
+
+ ops->reg_update(vfe, line->id);
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+static int vfe_disable_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ unsigned long flags;
+ unsigned int i;
+ bool done;
+ int timeout = 0;
+
+ do {
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ done = !output->gen2.active_num;
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ usleep_range(10000, 20000);
+
+ if (timeout++ == 100) {
+ dev_err(vfe->camss->dev, "VFE idle timeout - resetting\n");
+ vfe_reset(vfe);
+ output->gen2.active_num = 0;
+ return 0;
+ }
+ } while (!done);
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ for (i = 0; i < output->wm_num; i++)
+ vfe_wm_stop(vfe, output->wm_idx[i]);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+/*
+ * vfe_enable - Enable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_enable(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ int ret;
+
+ mutex_lock(&vfe->stream_lock);
+
+ if (!vfe->stream_count)
+ vfe_enable_irq_common(vfe);
+
+ vfe->stream_count++;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ ret = vfe_get_output(line);
+ if (ret < 0)
+ goto error_get_output;
+
+ ret = vfe_enable_output(line);
+ if (ret < 0)
+ goto error_enable_output;
+
+ vfe->was_streaming = 1;
+
+ return 0;
+
+error_enable_output:
+ vfe_put_output(line);
+
+error_get_output:
+ mutex_lock(&vfe->stream_lock);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return ret;
+}
+
+/*
+ * vfe_disable - Disable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_disable(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+
+ vfe_disable_output(line);
+
+ vfe_put_output(line);
+
+ mutex_lock(&vfe->stream_lock);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return 0;
+}
+
+/*
+ * vfe_isr_sof - Process start of frame interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ /* nop */
+}
+
+/*
+ * vfe_isr_reg_update - Process reg update interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ struct vfe_output *output;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ vfe->ops->reg_update_clear(vfe, line_id);
+
+ output = &vfe->line[line_id].output;
+
+ if (output->wait_reg_update) {
+ output->wait_reg_update = 0;
+ complete(&output->reg_update);
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_isr_wm_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
+{
+ struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
+ struct camss_buffer *ready_buf;
+ struct vfe_output *output;
+ unsigned long flags;
+ u32 index;
+ u64 ts = ktime_get_ns();
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Received wm done for unmapped index\n");
+ goto out_unlock;
+ }
+ output = &vfe->line[vfe->wm_output_map[wm]].output;
+
+ ready_buf = output->buf[0];
+ if (!ready_buf) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Missing ready buf %d!\n", output->state);
+ goto out_unlock;
+ }
+
+ ready_buf->vb.vb2_buf.timestamp = ts;
+ ready_buf->vb.sequence = output->sequence++;
+
+ index = 0;
+ output->buf[0] = output->buf[1];
+ if (output->buf[0])
+ index = 1;
+
+ output->buf[index] = vfe_buf_get_pending(output);
+
+ if (output->buf[index])
+ vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line);
+ else
+ output->gen2.active_num--;
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+ return;
+
+out_unlock:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+ /* nop */
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+ return 0;
+}
+
+/*
+ * vfe_queue_buffer - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_queue_buffer(struct camss_video *vid,
+ struct camss_buffer *buf)
+{
+ struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ unsigned long flags;
+
+ output = &line->output;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
+ output->buf[output->gen2.active_num++] = buf;
+ vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
+ } else {
+ vfe_buf_add_pending(output, buf);
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+static const struct vfe_isr_ops vfe_isr_ops_170 = {
+ .reset_ack = vfe_isr_reset_ack,
+ .halt_ack = vfe_isr_halt_ack,
+ .reg_update = vfe_isr_reg_update,
+ .sof = vfe_isr_sof,
+ .comp_done = vfe_isr_comp_done,
+ .wm_done = vfe_isr_wm_done,
+};
+
+static const struct camss_video_ops vfe_video_ops_170 = {
+ .queue_buffer = vfe_queue_buffer,
+ .flush_buffers = vfe_flush_buffers,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->isr_ops = vfe_isr_ops_170;
+ vfe->video_ops = vfe_video_ops_170;
+
+ vfe->line_num = VFE_LINE_NUM_GEN2;
+}
+
+const struct vfe_hw_ops vfe_ops_170 = {
+ .global_reset = vfe_global_reset,
+ .hw_version_read = vfe_hw_version_read,
+ .isr_read = vfe_isr_read,
+ .isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .reg_update_clear = vfe_reg_update_clear,
+ .reg_update = vfe_reg_update,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_disable,
+ .vfe_enable = vfe_enable,
+ .vfe_halt = vfe_halt,
+ .violation_read = vfe_violation_read,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
index 174a36be6f5d..53c56a8d4545 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
@@ -12,7 +12,9 @@
#include <linux/io.h>
#include <linux/iopoll.h>
+#include "camss.h"
#include "camss-vfe.h"
+#include "camss-vfe-gen1.h"
#define VFE_0_HW_VERSION 0x000
@@ -283,30 +285,6 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
}
-#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
-
-static int vfe_word_per_line(u32 format, u32 pixel_per_line)
-{
- int val = 0;
-
- switch (format) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- val = CALC_WORD(pixel_per_line, 1, 8);
- break;
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- val = CALC_WORD(pixel_per_line, 2, 8);
- break;
- }
-
- return val;
-}
-
static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
u16 *width, u16 *height, u16 *bytesperline)
{
@@ -665,20 +643,6 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
}
-static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
-{
- if (input / output >= 16)
- return 0;
-
- if (input / output >= 8)
- return 1;
-
- if (input / output >= 4)
- return 2;
-
- return 3;
-}
-
static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
{
u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
@@ -922,7 +886,7 @@ static void vfe_violation_read(struct vfe_device *vfe)
}
/*
- * vfe_isr - ISPIF module interrupt handler
+ * vfe_isr - VFE module interrupt handler
* @irq: Interrupt line
* @dev: VFE device
*
@@ -936,8 +900,8 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->ops->isr_read(vfe, &value0, &value1);
- trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n",
- value0, value1);
+ dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
+ value0, value1);
if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
vfe->isr_ops.reset_ack(vfe);
@@ -974,46 +938,82 @@ static irqreturn_t vfe_isr(int irq, void *dev)
return IRQ_HANDLED;
}
-const struct vfe_hw_ops vfe_ops_4_1 = {
- .hw_version_read = vfe_hw_version_read,
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+ /* nop */
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+ return 0;
+}
+
+static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = {
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .bus_enable_wr_if = vfe_bus_enable_wr_if,
+ .bus_reload_wm = vfe_bus_reload_wm,
+ .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .enable_irq_common = vfe_enable_irq_common,
+ .enable_irq_pix_line = vfe_enable_irq_pix_line,
+ .enable_irq_wm_line = vfe_enable_irq_wm_line,
.get_ub_size = vfe_get_ub_size,
- .global_reset = vfe_global_reset,
- .halt_request = vfe_halt_request,
.halt_clear = vfe_halt_clear,
+ .halt_request = vfe_halt_request,
+ .set_camif_cfg = vfe_set_camif_cfg,
+ .set_camif_cmd = vfe_set_camif_cmd,
+ .set_cgc_override = vfe_set_cgc_override,
+ .set_clamp_cfg = vfe_set_clamp_cfg,
+ .set_crop_cfg = vfe_set_crop_cfg,
+ .set_demux_cfg = vfe_set_demux_cfg,
+ .set_ds = vfe_set_ds,
+ .set_module_cfg = vfe_set_module_cfg,
+ .set_qos = vfe_set_qos,
+ .set_rdi_cid = vfe_set_rdi_cid,
+ .set_realign_cfg = vfe_set_realign_cfg,
+ .set_scale_cfg = vfe_set_scale_cfg,
+ .set_xbar_cfg = vfe_set_xbar_cfg,
.wm_enable = vfe_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
+ .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.wm_line_based = vfe_wm_line_based,
- .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
- .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
- .bus_reload_wm = vfe_bus_reload_wm,
+ .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
- .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
- .bus_enable_wr_if = vfe_bus_enable_wr_if,
- .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
.wm_set_subsample = vfe_wm_set_subsample,
- .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
- .set_xbar_cfg = vfe_set_xbar_cfg,
- .set_realign_cfg = vfe_set_realign_cfg,
- .set_rdi_cid = vfe_set_rdi_cid,
- .reg_update = vfe_reg_update,
- .reg_update_clear = vfe_reg_update_clear,
- .enable_irq_wm_line = vfe_enable_irq_wm_line,
- .enable_irq_pix_line = vfe_enable_irq_pix_line,
- .enable_irq_common = vfe_enable_irq_common,
- .set_demux_cfg = vfe_set_demux_cfg,
- .set_scale_cfg = vfe_set_scale_cfg,
- .set_crop_cfg = vfe_set_crop_cfg,
- .set_clamp_cfg = vfe_set_clamp_cfg,
- .set_qos = vfe_set_qos,
- .set_ds = vfe_set_ds,
- .set_cgc_override = vfe_set_cgc_override,
- .set_camif_cfg = vfe_set_camif_cfg,
- .set_camif_cmd = vfe_set_camif_cmd,
- .set_module_cfg = vfe_set_module_cfg,
- .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->isr_ops = vfe_isr_ops_gen1;
+ vfe->ops_gen1 = &vfe_ops_gen1_4_1;
+ vfe->video_ops = vfe_video_ops_gen1;
+
+ vfe->line_num = VFE_LINE_NUM_GEN1;
+}
+
+const struct vfe_hw_ops vfe_ops_4_1 = {
+ .global_reset = vfe_global_reset,
+ .hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read,
- .violation_read = vfe_violation_read,
.isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .reg_update_clear = vfe_reg_update_clear,
+ .reg_update = vfe_reg_update,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_gen1_disable,
+ .vfe_enable = vfe_gen1_enable,
+ .vfe_halt = vfe_gen1_halt,
+ .violation_read = vfe_violation_read,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
index b5704a2f119b..a59635217758 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
@@ -8,11 +8,15 @@
* Copyright (C) 2015-2018 Linaro Ltd.
*/
+#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include "camss.h"
#include "camss-vfe.h"
+#include "camss-vfe-gen1.h"
+
#define VFE_0_HW_VERSION 0x000
@@ -257,7 +261,7 @@ static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version);
}
-static u16 vfe47_get_ub_size(u8 vfe_id)
+static u16 vfe_get_ub_size(u8 vfe_id)
{
if (vfe_id == 0)
return MSM_VFE_VFE0_UB_SIZE_RDI;
@@ -295,6 +299,8 @@ static void vfe_global_reset(struct vfe_device *vfe)
VFE_0_GLOBAL_RESET_CMD_CORE;
writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0);
+
+ /* Enforce barrier between IRQ mask setup and global reset */
wmb();
writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
}
@@ -310,7 +316,7 @@ static void vfe_halt_clear(struct vfe_device *vfe)
writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
}
-static void vfe47_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
{
if (enable)
vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
@@ -459,8 +465,12 @@ static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm,
static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
{
+ /* Enforce barrier between any outstanding register write */
wmb();
+
writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
+
+ /* Use barrier to make sure bus reload is issued before anything else */
wmb();
}
@@ -674,8 +684,12 @@ static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
{
vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
+
+ /* Enforce barrier between line update and commit */
wmb();
writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
+
+ /* Make sure register update is issued before further reg writes */
wmb();
}
@@ -779,20 +793,6 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
}
-static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
-{
- if (input / output >= 16)
- return 0;
-
- if (input / output >= 8)
- return 1;
-
- if (input / output >= 4)
- return 2;
-
- return 3;
-}
-
static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
{
u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
@@ -894,7 +894,7 @@ static void vfe_set_clamp_cfg(struct vfe_device *vfe)
writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
}
-static void vfe47_set_qos(struct vfe_device *vfe)
+static void vfe_set_qos(struct vfe_device *vfe)
{
u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
@@ -909,7 +909,7 @@ static void vfe47_set_qos(struct vfe_device *vfe)
writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
}
-static void vfe47_set_ds(struct vfe_device *vfe)
+static void vfe_set_ds(struct vfe_device *vfe)
{
u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG;
u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG;
@@ -993,6 +993,8 @@ static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable)
cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE;
writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+
+ /* Make sure camif command is issued written before it is changed again */
wmb();
if (enable)
@@ -1035,27 +1037,10 @@ static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev)
return ret;
}
-static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
-{
- *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
- *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
- writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
- writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
-
- wmb();
- writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
-}
-
-static void vfe_violation_read(struct vfe_device *vfe)
-{
- u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
-
- pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
-}
/*
- * vfe_isr - ISPIF module interrupt handler
+ * vfe_isr - VFE module interrupt handler
* @irq: Interrupt line
* @dev: VFE device
*
@@ -1069,8 +1054,8 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->ops->isr_read(vfe, &value0, &value1);
- trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n",
- value0, value1);
+ dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
+ value0, value1);
if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
vfe->isr_ops.reset_ack(vfe);
@@ -1081,7 +1066,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
vfe->isr_ops.halt_ack(vfe);
- for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++)
+ for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
vfe->isr_ops.reg_update(vfe, i);
@@ -1107,150 +1092,120 @@ static irqreturn_t vfe_isr(int irq, void *dev)
return IRQ_HANDLED;
}
-const struct vfe_hw_ops vfe_ops_4_7 = {
- .hw_version_read = vfe_hw_version_read,
- .get_ub_size = vfe47_get_ub_size,
- .global_reset = vfe_global_reset,
- .halt_request = vfe_halt_request,
- .halt_clear = vfe_halt_clear,
- .wm_enable = vfe47_wm_enable,
- .wm_frame_based = vfe_wm_frame_based,
- .wm_line_based = vfe_wm_line_based,
- .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
- .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
- .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
- .bus_reload_wm = vfe_bus_reload_wm,
- .wm_set_ping_addr = vfe_wm_set_ping_addr,
- .wm_set_pong_addr = vfe_wm_set_pong_addr,
- .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
- .bus_enable_wr_if = vfe_bus_enable_wr_if,
- .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
- .wm_set_subsample = vfe_wm_set_subsample,
- .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
- .set_xbar_cfg = vfe_set_xbar_cfg,
- .set_realign_cfg = vfe_set_realign_cfg,
- .set_rdi_cid = vfe_set_rdi_cid,
- .reg_update = vfe_reg_update,
- .reg_update_clear = vfe_reg_update_clear,
- .enable_irq_wm_line = vfe_enable_irq_wm_line,
- .enable_irq_pix_line = vfe_enable_irq_pix_line,
- .enable_irq_common = vfe_enable_irq_common,
- .set_demux_cfg = vfe_set_demux_cfg,
- .set_scale_cfg = vfe_set_scale_cfg,
- .set_crop_cfg = vfe_set_crop_cfg,
- .set_clamp_cfg = vfe_set_clamp_cfg,
- .set_qos = vfe47_set_qos,
- .set_ds = vfe47_set_ds,
- .set_cgc_override = vfe_set_cgc_override,
- .set_camif_cfg = vfe_set_camif_cfg,
- .set_camif_cmd = vfe_set_camif_cmd,
- .set_module_cfg = vfe_set_module_cfg,
- .camif_wait_for_stop = vfe_camif_wait_for_stop,
- .isr_read = vfe_isr_read,
- .violation_read = vfe_violation_read,
- .isr = vfe_isr,
-};
-
-static u16 vfe48_get_ub_size(u8 vfe_id)
+static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
{
- /* On VFE4.8 the ub-size is the same on both instances */
- return MSM_VFE_VFE0_UB_SIZE_RDI;
+ *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
+ *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
+
+ writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
+ writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
+
+ /* Enforce barrier between local & global IRQ clear */
+ wmb();
+ writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
}
-static void vfe48_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
{
- if (enable)
- writel_relaxed(2 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
- vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD);
- else
- writel_relaxed(1 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
- vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD);
+ struct camss *camss;
- /* The WM must be enabled before sending other commands */
- wmb();
+ if (!vfe)
+ return;
+
+ camss = vfe->camss;
+
+ device_link_del(camss->genpd_link[vfe->id]);
}
-static void vfe48_set_qos(struct vfe_device *vfe)
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
{
- u32 val = VFE48_0_BUS_BDG_QOS_CFG_0_CFG;
- u32 val3 = VFE48_0_BUS_BDG_QOS_CFG_3_CFG;
- u32 val4 = VFE48_0_BUS_BDG_QOS_CFG_4_CFG;
- u32 val7 = VFE48_0_BUS_BDG_QOS_CFG_7_CFG;
+ struct camss *camss = vfe->camss;
+ enum vfe_line_id id = vfe->id;
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
- writel_relaxed(val3, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
- writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
- writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
- writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
- writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
+ camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+ if (!camss->genpd_link[id]) {
+ dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id);
+ return -EINVAL;
+ }
+
+ return 0;
}
-static void vfe48_set_ds(struct vfe_device *vfe)
+static void vfe_violation_read(struct vfe_device *vfe)
{
- u32 val = VFE48_0_BUS_BDG_DS_CFG_0_CFG;
- u32 val16 = VFE48_0_BUS_BDG_DS_CFG_16_CFG;
+ u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15);
- writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16);
+ pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
}
-const struct vfe_hw_ops vfe_ops_4_8 = {
- .hw_version_read = vfe_hw_version_read,
- .get_ub_size = vfe48_get_ub_size,
- .global_reset = vfe_global_reset,
- .halt_request = vfe_halt_request,
+static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_7 = {
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .bus_enable_wr_if = vfe_bus_enable_wr_if,
+ .bus_reload_wm = vfe_bus_reload_wm,
+ .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .enable_irq_common = vfe_enable_irq_common,
+ .enable_irq_pix_line = vfe_enable_irq_pix_line,
+ .enable_irq_wm_line = vfe_enable_irq_wm_line,
+ .get_ub_size = vfe_get_ub_size,
.halt_clear = vfe_halt_clear,
- .wm_enable = vfe48_wm_enable,
+ .halt_request = vfe_halt_request,
+ .set_camif_cfg = vfe_set_camif_cfg,
+ .set_camif_cmd = vfe_set_camif_cmd,
+ .set_cgc_override = vfe_set_cgc_override,
+ .set_clamp_cfg = vfe_set_clamp_cfg,
+ .set_crop_cfg = vfe_set_crop_cfg,
+ .set_demux_cfg = vfe_set_demux_cfg,
+ .set_ds = vfe_set_ds,
+ .set_module_cfg = vfe_set_module_cfg,
+ .set_qos = vfe_set_qos,
+ .set_rdi_cid = vfe_set_rdi_cid,
+ .set_realign_cfg = vfe_set_realign_cfg,
+ .set_scale_cfg = vfe_set_scale_cfg,
+ .set_xbar_cfg = vfe_set_xbar_cfg,
+ .wm_enable = vfe_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
+ .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.wm_line_based = vfe_wm_line_based,
- .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
- .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
- .bus_reload_wm = vfe_bus_reload_wm,
+ .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
- .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
- .bus_enable_wr_if = vfe_bus_enable_wr_if,
- .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
.wm_set_subsample = vfe_wm_set_subsample,
- .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
- .set_xbar_cfg = vfe_set_xbar_cfg,
- .set_realign_cfg = vfe_set_realign_cfg,
- .set_rdi_cid = vfe_set_rdi_cid,
- .reg_update = vfe_reg_update,
- .reg_update_clear = vfe_reg_update_clear,
- .enable_irq_wm_line = vfe_enable_irq_wm_line,
- .enable_irq_pix_line = vfe_enable_irq_pix_line,
- .enable_irq_common = vfe_enable_irq_common,
- .set_demux_cfg = vfe_set_demux_cfg,
- .set_scale_cfg = vfe_set_scale_cfg,
- .set_crop_cfg = vfe_set_crop_cfg,
- .set_clamp_cfg = vfe_set_clamp_cfg,
- .set_qos = vfe48_set_qos,
- .set_ds = vfe48_set_ds,
- .set_cgc_override = vfe_set_cgc_override,
- .set_camif_cfg = vfe_set_camif_cfg,
- .set_camif_cmd = vfe_set_camif_cmd,
- .set_module_cfg = vfe_set_module_cfg,
- .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->isr_ops = vfe_isr_ops_gen1;
+ vfe->ops_gen1 = &vfe_ops_gen1_4_7;
+ vfe->video_ops = vfe_video_ops_gen1;
+
+ vfe->line_num = VFE_LINE_NUM_GEN1;
+}
+
+const struct vfe_hw_ops vfe_ops_4_7 = {
+ .global_reset = vfe_global_reset,
+ .hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read,
- .violation_read = vfe_violation_read,
.isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .reg_update_clear = vfe_reg_update_clear,
+ .reg_update = vfe_reg_update,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_gen1_disable,
+ .vfe_enable = vfe_gen1_enable,
+ .vfe_halt = vfe_gen1_halt,
+ .violation_read = vfe_violation_read,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
new file mode 100644
index 000000000000..998429dbb65c
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
@@ -0,0 +1,1195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-4-8.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.8
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2021 Linaro Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+#include "camss-vfe-gen1.h"
+
+#define VFE_0_HW_VERSION 0x000
+
+#define VFE_0_GLOBAL_RESET_CMD 0x018
+#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
+#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
+#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3)
+#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4)
+#define VFE_0_GLOBAL_RESET_CMD_PM BIT(5)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(6)
+#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(7)
+#define VFE_0_GLOBAL_RESET_CMD_DSP BIT(8)
+#define VFE_0_GLOBAL_RESET_CMD_IDLE_CGC BIT(9)
+
+#define VFE_0_MODULE_LENS_EN 0x040
+#define VFE_0_MODULE_LENS_EN_DEMUX BIT(2)
+#define VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE BIT(3)
+
+#define VFE_0_MODULE_ZOOM_EN 0x04c
+#define VFE_0_MODULE_ZOOM_EN_SCALE_ENC BIT(1)
+#define VFE_0_MODULE_ZOOM_EN_CROP_ENC BIT(2)
+#define VFE_0_MODULE_ZOOM_EN_REALIGN_BUF BIT(9)
+
+#define VFE_0_CORE_CFG 0x050
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7
+#define VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN BIT(4)
+
+#define VFE_0_IRQ_CMD 0x058
+#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0)
+
+#define VFE_0_IRQ_MASK_0 0x05c
+#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1)
+#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_MASK_1 0x060
+#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0)
+#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9)
+#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_CLEAR_0 0x064
+#define VFE_0_IRQ_CLEAR_1 0x068
+
+#define VFE_0_IRQ_STATUS_0 0x06c
+#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_STATUS_1 0x070
+#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_COMPOSITE_MASK_0 0x074
+#define VFE_0_VIOLATION_STATUS 0x07c
+
+#define VFE_0_BUS_CMD 0x80
+#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x)
+
+#define VFE_0_BUS_CFG 0x084
+
+#define VFE_0_BUS_XBAR_CFG_x(x) (0x90 + 0x4 * ((x) / 2))
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(2)
+#define VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN BIT(3)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTRA (0x1 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER (0x2 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0x0
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 0xc
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 0xd
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 0xe
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x0a0 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x0a4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x0ac + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x0b4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT 1
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2)
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x0b8 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x0bc + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x0c0 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \
+ (0x0c4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \
+ (0x0c8 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff
+
+#define VFE_0_BUS_PING_PONG_STATUS 0x338
+
+#define VFE_0_BUS_BDG_CMD 0x400
+#define VFE_0_BUS_BDG_CMD_HALT_REQ 1
+
+#define VFE_0_BUS_BDG_QOS_CFG_0 0x404
+#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5
+#define VFE_0_BUS_BDG_QOS_CFG_1 0x408
+#define VFE_0_BUS_BDG_QOS_CFG_2 0x40c
+#define VFE_0_BUS_BDG_QOS_CFG_3 0x410
+#define VFE_0_BUS_BDG_QOS_CFG_3_CFG 0xaa55aaa5
+#define VFE_0_BUS_BDG_QOS_CFG_4 0x414
+#define VFE_0_BUS_BDG_QOS_CFG_4_CFG 0xaa55aa55
+#define VFE_0_BUS_BDG_QOS_CFG_5 0x418
+#define VFE_0_BUS_BDG_QOS_CFG_6 0x41c
+#define VFE_0_BUS_BDG_QOS_CFG_7 0x420
+#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0005aa55
+
+#define VFE_0_BUS_BDG_DS_CFG_0 0x424
+#define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc1111
+#define VFE_0_BUS_BDG_DS_CFG_1 0x428
+#define VFE_0_BUS_BDG_DS_CFG_2 0x42c
+#define VFE_0_BUS_BDG_DS_CFG_3 0x430
+#define VFE_0_BUS_BDG_DS_CFG_4 0x434
+#define VFE_0_BUS_BDG_DS_CFG_5 0x438
+#define VFE_0_BUS_BDG_DS_CFG_6 0x43c
+#define VFE_0_BUS_BDG_DS_CFG_7 0x440
+#define VFE_0_BUS_BDG_DS_CFG_8 0x444
+#define VFE_0_BUS_BDG_DS_CFG_9 0x448
+#define VFE_0_BUS_BDG_DS_CFG_10 0x44c
+#define VFE_0_BUS_BDG_DS_CFG_11 0x450
+#define VFE_0_BUS_BDG_DS_CFG_12 0x454
+#define VFE_0_BUS_BDG_DS_CFG_13 0x458
+#define VFE_0_BUS_BDG_DS_CFG_14 0x45c
+#define VFE_0_BUS_BDG_DS_CFG_15 0x460
+#define VFE_0_BUS_BDG_DS_CFG_16 0x464
+#define VFE_0_BUS_BDG_DS_CFG_16_CFG 0x00000110
+
+#define VFE_0_RDI_CFG_x(x) (0x46c + (0x4 * (x)))
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28)
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4)
+#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2)
+#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3
+
+#define VFE_0_CAMIF_CMD 0x478
+#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0
+#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1
+#define VFE_0_CAMIF_CMD_NO_CHANGE 3
+#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2)
+#define VFE_0_CAMIF_CFG 0x47c
+#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6)
+#define VFE_0_CAMIF_FRAME_CFG 0x484
+#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x488
+#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x48c
+#define VFE_0_CAMIF_SUBSAMPLE_CFG 0x490
+#define VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN 0x498
+#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x49c
+#define VFE_0_CAMIF_STATUS 0x4a4
+#define VFE_0_CAMIF_STATUS_HALT BIT(31)
+
+#define VFE_0_REG_UPDATE 0x4ac
+#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n))
+#define VFE_0_REG_UPDATE_line_n(n) \
+ ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
+
+#define VFE_0_DEMUX_CFG 0x560
+#define VFE_0_DEMUX_CFG_PERIOD 0x3
+#define VFE_0_DEMUX_GAIN_0 0x564
+#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16)
+#define VFE_0_DEMUX_GAIN_1 0x568
+#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16)
+#define VFE_0_DEMUX_EVEN_CFG 0x574
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9
+#define VFE_0_DEMUX_ODD_CFG 0x578
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9
+
+#define VFE_0_SCALE_ENC_Y_CFG 0x91c
+#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x920
+#define VFE_0_SCALE_ENC_Y_H_PHASE 0x924
+#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x934
+#define VFE_0_SCALE_ENC_Y_V_PHASE 0x938
+#define VFE_0_SCALE_ENC_CBCR_CFG 0x948
+#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x94c
+#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x950
+#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x960
+#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x964
+
+#define VFE_0_CROP_ENC_Y_WIDTH 0x974
+#define VFE_0_CROP_ENC_Y_HEIGHT 0x978
+#define VFE_0_CROP_ENC_CBCR_WIDTH 0x97c
+#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x980
+
+#define VFE_0_CLAMP_ENC_MAX_CFG 0x984
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16)
+#define VFE_0_CLAMP_ENC_MIN_CFG 0x988
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16)
+
+#define VFE_0_REALIGN_BUF_CFG 0xaac
+#define VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL BIT(2)
+#define VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL BIT(3)
+#define VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE BIT(4)
+
+#define VFE_0_BUS_IMAGE_MASTER_CMD 0xcec
+#define VFE_0_BUS_IMAGE_MASTER_n_SHIFT(x) (2 * (x))
+
+#define CAMIF_TIMEOUT_SLEEP_US 1000
+#define CAMIF_TIMEOUT_ALL_US 1000000
+
+#define MSM_VFE_VFE0_UB_SIZE 2047
+#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
+#define MSM_VFE_VFE1_UB_SIZE 1535
+#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
+
+static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
+
+ dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version);
+}
+
+static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits & ~clr_bits, vfe->base + reg);
+}
+
+static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits | set_bits, vfe->base + reg);
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_IDLE_CGC |
+ VFE_0_GLOBAL_RESET_CMD_DSP |
+ VFE_0_GLOBAL_RESET_CMD_TESTGEN |
+ VFE_0_GLOBAL_RESET_CMD_BUS_MISR |
+ VFE_0_GLOBAL_RESET_CMD_PM |
+ VFE_0_GLOBAL_RESET_CMD_REGISTER |
+ VFE_0_GLOBAL_RESET_CMD_BUS_BDG |
+ VFE_0_GLOBAL_RESET_CMD_BUS |
+ VFE_0_GLOBAL_RESET_CMD_CAMIF |
+ VFE_0_GLOBAL_RESET_CMD_CORE;
+
+ writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0);
+
+ /* Enforce barrier between IRQ mask setup and global reset */
+ wmb();
+ writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
+}
+
+static void vfe_halt_request(struct vfe_device *vfe)
+{
+ writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
+ vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_halt_clear(struct vfe_device *vfe)
+{
+ writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT);
+ else
+ vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT);
+}
+
+#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
+
+static int vfe_word_per_line_by_pixel(u32 format, u32 pixel_per_line)
+{
+ int val = 0;
+
+ switch (format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ val = CALC_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ val = CALC_WORD(pixel_per_line, 2, 8);
+ break;
+ }
+
+ return val;
+}
+
+static int vfe_word_per_line_by_bytes(u32 bytes_per_line)
+{
+ return CALC_WORD(bytes_per_line, 1, 8);
+}
+
+static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
+ u16 *width, u16 *height, u16 *bytesperline)
+{
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ if (plane == 1)
+ *height /= 2;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_UYVY:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[plane].bytesperline;
+ break;
+ }
+}
+
+static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
+ struct v4l2_pix_format_mplane *pix,
+ u8 plane, u32 enable)
+{
+ u32 reg;
+
+ if (enable) {
+ u16 width = 0, height = 0, bytesperline = 0, wpl;
+
+ vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
+
+ wpl = vfe_word_per_line_by_pixel(pix->pixelformat, width);
+
+ reg = height - 1;
+ reg |= ((wpl + 3) / 4 - 1) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+
+ wpl = vfe_word_per_line_by_bytes(bytesperline);
+
+ reg = 0x3;
+ reg |= (height - 1) << 2;
+ reg |= ((wpl + 1) / 2) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ } else {
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ }
+}
+
+static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+
+ reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
+
+ reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
+ & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
+
+ writel_relaxed(reg,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+}
+
+static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
+ u32 pattern)
+{
+ writel_relaxed(pattern, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
+}
+
+static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm,
+ u16 offset, u16 depth)
+{
+ u32 reg;
+
+ reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
+ depth;
+ writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm));
+}
+
+static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
+{
+ /* Enforce barrier between any outstanding register write */
+ wmb();
+
+ writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
+
+ /* Use barrier to make sure bus reload is issued before anything else */
+ wmb();
+}
+
+static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
+}
+
+static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
+}
+
+static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
+
+ return (reg >> wm) & 0x1;
+}
+
+static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
+{
+ if (enable)
+ writel_relaxed(0x101, vfe->base + VFE_0_BUS_CFG);
+ else
+ writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
+}
+
+static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
+ VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
+{
+ writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
+}
+
+static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
+ u8 enable)
+{
+ struct vfe_line *line = container_of(output, struct vfe_line, output);
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+
+ switch (p) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+
+ if (output->wm_idx[0] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+
+ reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+
+ if (output->wm_idx[1] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]),
+ reg);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_UYVY:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN;
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+
+ if (p == V4L2_PIX_FMT_YUYV || p == V4L2_PIX_FMT_YVYU)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+
+ if (output->wm_idx[0] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line,
+ u8 enable)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 val = VFE_0_MODULE_ZOOM_EN_REALIGN_BUF;
+
+ if (p != V4L2_PIX_FMT_YUYV && p != V4L2_PIX_FMT_YVYU &&
+ p != V4L2_PIX_FMT_VYUY && p != V4L2_PIX_FMT_UYVY)
+ return;
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val);
+ return;
+ }
+
+ val = VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE;
+
+ if (p == V4L2_PIX_FMT_UYVY || p == V4L2_PIX_FMT_YUYV)
+ val |= VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL;
+ else
+ val |= VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL;
+
+ writel_relaxed(val, vfe->base + VFE_0_REALIGN_BUF_CFG);
+}
+
+static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
+{
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
+ VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
+
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
+ cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
+
+ /* Enforce barrier between line update and commit */
+ wmb();
+
+ writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
+
+ /* Make sure register update is issued before further reg writes */
+ wmb();
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id);
+}
+
+static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id line_id, u8 enable)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
+ VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
+ VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ }
+}
+
+static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
+ enum vfe_line_id line_id, u8 enable)
+{
+ struct vfe_output *output = &vfe->line[line_id].output;
+ unsigned int i;
+ u32 irq_en0;
+ u32 irq_en1;
+ u32 comp_mask = 0;
+
+ irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
+ irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
+ for (i = 0; i < output->wm_num; i++) {
+ irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(output->wm_idx[i]);
+ comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
+ }
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ }
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
+ VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
+
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+}
+
+static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val, even_cfg, odd_cfg;
+
+ writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG);
+
+ val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0);
+
+ val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1);
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY;
+ break;
+ }
+
+ writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
+ writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
+}
+
+static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 input, output;
+ u8 interp_reso;
+ u32 phase_mult;
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width - 1;
+ output = line->compose.width - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ output = line->compose.height - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE);
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width - 1;
+ output = line->compose.width / 2 - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ output = line->compose.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
+ output = line->compose.height / 2 - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
+}
+
+static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 first, last;
+
+ first = line->crop.left;
+ last = line->crop.left + line->crop.width - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT);
+
+ first = line->crop.left / 2;
+ last = line->crop.left / 2 + line->crop.width / 2 - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) {
+ first = line->crop.top / 2;
+ last = line->crop.top / 2 + line->crop.height / 2 - 1;
+ }
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT);
+}
+
+static void vfe_set_clamp_cfg(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
+
+ val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
+}
+
+static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ /* empty */
+}
+
+static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val;
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY;
+ break;
+ }
+
+ val |= VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CORE_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ val |= (line->fmt[MSM_VFE_PAD_SINK].height - 1) << 16;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN);
+
+ val = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val);
+
+ val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG);
+}
+
+static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable)
+{
+ u32 cmd;
+
+ cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE;
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+
+ /* Make sure camif command is issued written before it is changed again */
+ wmb();
+
+ if (enable)
+ cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY;
+ else
+ cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY;
+
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+}
+
+static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
+{
+ u32 val_lens = VFE_0_MODULE_LENS_EN_DEMUX |
+ VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE;
+ u32 val_zoom = VFE_0_MODULE_ZOOM_EN_SCALE_ENC |
+ VFE_0_MODULE_ZOOM_EN_CROP_ENC;
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_MODULE_LENS_EN, val_lens);
+ vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_MODULE_LENS_EN, val_lens);
+ vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom);
+ }
+}
+
+static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS,
+ val,
+ (val & VFE_0_CAMIF_STATUS_HALT),
+ CAMIF_TIMEOUT_SLEEP_US,
+ CAMIF_TIMEOUT_ALL_US);
+ if (ret < 0)
+ dev_err(dev, "%s: camif stop timeout\n", __func__);
+
+ return ret;
+}
+
+/*
+ * vfe_isr - VFE module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ struct vfe_device *vfe = dev;
+ u32 value0, value1;
+ int i, j;
+
+ vfe->ops->isr_read(vfe, &value0, &value1);
+
+ dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
+ value0, value1);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
+ vfe->isr_ops.reset_ack(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
+ vfe->ops->violation_read(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
+ vfe->isr_ops.halt_ack(vfe);
+
+ for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
+ vfe->isr_ops.reg_update(vfe, i);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF)
+ vfe->isr_ops.sof(vfe, VFE_LINE_PIX);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+ if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i))
+ vfe->isr_ops.sof(vfe, i);
+
+ for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) {
+ vfe->isr_ops.comp_done(vfe, i);
+ for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++)
+ if (vfe->wm_output_map[j] == VFE_LINE_PIX)
+ value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j);
+ }
+
+ for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
+ vfe->isr_ops.wm_done(vfe, i);
+
+ return IRQ_HANDLED;
+}
+
+static u16 vfe_get_ub_size(u8 vfe_id)
+{
+ /* On VFE4.8 the ub-size is the same on both instances */
+ return MSM_VFE_VFE0_UB_SIZE_RDI;
+}
+
+static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ writel_relaxed(2 << VFE_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_CMD);
+ else
+ writel_relaxed(1 << VFE_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_CMD);
+
+ /* The WM must be enabled before sending other commands */
+ wmb();
+}
+
+static void vfe_set_qos(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
+ u32 val3 = VFE_0_BUS_BDG_QOS_CFG_3_CFG;
+ u32 val4 = VFE_0_BUS_BDG_QOS_CFG_4_CFG;
+ u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
+
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
+ writel_relaxed(val3, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
+ writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
+ writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
+ writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
+ writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
+}
+
+static void vfe_set_ds(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG;
+ u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG;
+
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15);
+ writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16);
+}
+
+static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
+{
+ *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
+ *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
+
+ writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
+ writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
+
+ /* Enforce barrier between local & global IRQ clear */
+ wmb();
+ writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
+}
+
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+ struct camss *camss = vfe->camss;
+
+ device_link_del(camss->genpd_link[vfe->id]);
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+ struct camss *camss = vfe->camss;
+ enum vfe_line_id id = vfe->id;
+
+ camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+ if (!camss->genpd_link[id]) {
+ dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void vfe_violation_read(struct vfe_device *vfe)
+{
+ u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
+
+ pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
+}
+
+static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_8 = {
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .bus_enable_wr_if = vfe_bus_enable_wr_if,
+ .bus_reload_wm = vfe_bus_reload_wm,
+ .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .enable_irq_common = vfe_enable_irq_common,
+ .enable_irq_pix_line = vfe_enable_irq_pix_line,
+ .enable_irq_wm_line = vfe_enable_irq_wm_line,
+ .get_ub_size = vfe_get_ub_size,
+ .halt_clear = vfe_halt_clear,
+ .halt_request = vfe_halt_request,
+ .set_camif_cfg = vfe_set_camif_cfg,
+ .set_camif_cmd = vfe_set_camif_cmd,
+ .set_cgc_override = vfe_set_cgc_override,
+ .set_clamp_cfg = vfe_set_clamp_cfg,
+ .set_crop_cfg = vfe_set_crop_cfg,
+ .set_demux_cfg = vfe_set_demux_cfg,
+ .set_ds = vfe_set_ds,
+ .set_module_cfg = vfe_set_module_cfg,
+ .set_qos = vfe_set_qos,
+ .set_rdi_cid = vfe_set_rdi_cid,
+ .set_realign_cfg = vfe_set_realign_cfg,
+ .set_scale_cfg = vfe_set_scale_cfg,
+ .set_xbar_cfg = vfe_set_xbar_cfg,
+ .wm_enable = vfe_wm_enable,
+ .wm_frame_based = vfe_wm_frame_based,
+ .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
+ .wm_line_based = vfe_wm_line_based,
+ .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
+ .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
+ .wm_set_ping_addr = vfe_wm_set_ping_addr,
+ .wm_set_pong_addr = vfe_wm_set_pong_addr,
+ .wm_set_subsample = vfe_wm_set_subsample,
+ .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->isr_ops = vfe_isr_ops_gen1;
+ vfe->ops_gen1 = &vfe_ops_gen1_4_8;
+ vfe->video_ops = vfe_video_ops_gen1;
+
+ vfe->line_num = VFE_LINE_NUM_GEN1;
+}
+
+const struct vfe_hw_ops vfe_ops_4_8 = {
+ .global_reset = vfe_global_reset,
+ .hw_version_read = vfe_hw_version_read,
+ .isr_read = vfe_isr_read,
+ .isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .reg_update_clear = vfe_reg_update_clear,
+ .reg_update = vfe_reg_update,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_gen1_disable,
+ .vfe_enable = vfe_gen1_enable,
+ .vfe_halt = vfe_gen1_halt,
+ .violation_read = vfe_violation_read,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
new file mode 100644
index 000000000000..4fd265d01883
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-gen1.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE Common functionality for Gen 1 versions of hw (4.1, 4.7..)
+ *
+ * Copyright (C) 2020 Linaro Ltd.
+ */
+
+#include "camss.h"
+#include "camss-vfe.h"
+#include "camss-vfe-gen1.h"
+
+/* Max number of frame drop updates per frame */
+#define VFE_FRAME_DROP_UPDATES 2
+#define VFE_NEXT_SOF_MS 500
+
+int vfe_gen1_halt(struct vfe_device *vfe)
+{
+ unsigned long time;
+
+ reinit_completion(&vfe->halt_complete);
+
+ vfe->ops_gen1->halt_request(vfe);
+
+ time = wait_for_completion_timeout(&vfe->halt_complete,
+ msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
+ if (!time) {
+ dev_err(vfe->camss->dev, "VFE halt timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int vfe_disable_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->ops;
+ unsigned long flags;
+ unsigned long time;
+ unsigned int i;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ output->gen1.wait_sof = 1;
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ time = wait_for_completion_timeout(&output->sof, msecs_to_jiffies(VFE_NEXT_SOF_MS));
+ if (!time)
+ dev_err(vfe->camss->dev, "VFE sof timeout\n");
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ for (i = 0; i < output->wm_num; i++)
+ vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 0);
+
+ ops->reg_update(vfe, line->id);
+ output->wait_reg_update = 1;
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ time = wait_for_completion_timeout(&output->reg_update, msecs_to_jiffies(VFE_NEXT_SOF_MS));
+ if (!time)
+ dev_err(vfe->camss->dev, "VFE reg update timeout\n");
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (line->id != VFE_LINE_PIX) {
+ vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 0);
+ vfe->ops_gen1->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id);
+ vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
+ vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 0);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ } else {
+ for (i = 0; i < output->wm_num; i++) {
+ vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
+ vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 0);
+ }
+
+ vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 0);
+ vfe->ops_gen1->set_module_cfg(vfe, 0);
+ vfe->ops_gen1->set_realign_cfg(vfe, line, 0);
+ vfe->ops_gen1->set_xbar_cfg(vfe, output, 0);
+ vfe->ops_gen1->set_camif_cmd(vfe, 0);
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ vfe->ops_gen1->camif_wait_for_stop(vfe, vfe->camss->dev);
+ }
+
+ return 0;
+}
+
+/*
+ * vfe_gen1_disable - Disable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_gen1_disable(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+
+ vfe_disable_output(line);
+
+ vfe_put_output(line);
+
+ mutex_lock(&vfe->stream_lock);
+
+ if (vfe->stream_count == 1)
+ vfe->ops_gen1->bus_enable_wr_if(vfe, 0);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return 0;
+}
+
+static void vfe_output_init_addrs(struct vfe_device *vfe,
+ struct vfe_output *output, u8 sync,
+ struct vfe_line *line)
+{
+ u32 ping_addr;
+ u32 pong_addr;
+ unsigned int i;
+
+ output->gen1.active_buf = 0;
+
+ for (i = 0; i < output->wm_num; i++) {
+ if (output->buf[0])
+ ping_addr = output->buf[0]->addr[i];
+ else
+ ping_addr = 0;
+
+ if (output->buf[1])
+ pong_addr = output->buf[1]->addr[i];
+ else
+ pong_addr = ping_addr;
+
+ vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
+ vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
+ if (sync)
+ vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
+ }
+}
+
+static void vfe_output_frame_drop(struct vfe_device *vfe,
+ struct vfe_output *output,
+ u32 drop_pattern)
+{
+ u8 drop_period;
+ unsigned int i;
+
+ /* We need to toggle update period to be valid on next frame */
+ output->drop_update_idx++;
+ output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
+ drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
+
+ for (i = 0; i < output->wm_num; i++) {
+ vfe->ops_gen1->wm_set_framedrop_period(vfe, output->wm_idx[i], drop_period);
+ vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern);
+ }
+
+ vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id);
+}
+
+static int vfe_enable_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->ops;
+ struct media_entity *sensor;
+ unsigned long flags;
+ unsigned int frame_skip = 0;
+ unsigned int i;
+ u16 ub_size;
+
+ ub_size = vfe->ops_gen1->get_ub_size(vfe->id);
+ if (!ub_size)
+ return -EINVAL;
+
+ sensor = camss_find_sensor(&line->subdev.entity);
+ if (sensor) {
+ struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
+
+ v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
+ /* Max frame skip is 29 frames */
+ if (frame_skip > VFE_FRAME_DROP_VAL - 1)
+ frame_skip = VFE_FRAME_DROP_VAL - 1;
+ }
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ ops->reg_update_clear(vfe, line->id);
+
+ if (output->state != VFE_OUTPUT_RESERVED) {
+ dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ return -EINVAL;
+ }
+ output->state = VFE_OUTPUT_IDLE;
+
+ output->buf[0] = vfe_buf_get_pending(output);
+ output->buf[1] = vfe_buf_get_pending(output);
+
+ if (!output->buf[0] && output->buf[1]) {
+ output->buf[0] = output->buf[1];
+ output->buf[1] = NULL;
+ }
+
+ if (output->buf[0])
+ output->state = VFE_OUTPUT_SINGLE;
+
+ if (output->buf[1])
+ output->state = VFE_OUTPUT_CONTINUOUS;
+
+ switch (output->state) {
+ case VFE_OUTPUT_SINGLE:
+ vfe_output_frame_drop(vfe, output, 1 << frame_skip);
+ break;
+ case VFE_OUTPUT_CONTINUOUS:
+ vfe_output_frame_drop(vfe, output, 3 << frame_skip);
+ break;
+ default:
+ vfe_output_frame_drop(vfe, output, 0);
+ break;
+ }
+
+ output->sequence = 0;
+ output->gen1.wait_sof = 0;
+ output->wait_reg_update = 0;
+ reinit_completion(&output->sof);
+ reinit_completion(&output->reg_update);
+
+ vfe_output_init_addrs(vfe, output, 0, line);
+
+ if (line->id != VFE_LINE_PIX) {
+ vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 1);
+ vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
+ vfe->ops_gen1->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
+ vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[0]);
+ vfe->ops_gen1->set_rdi_cid(vfe, line->id, 0);
+ vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[0],
+ (ub_size + 1) * output->wm_idx[0], ub_size);
+ vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 1);
+ vfe->ops_gen1->wm_enable(vfe, output->wm_idx[0], 1);
+ vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[0]);
+ } else {
+ ub_size /= output->wm_num;
+ for (i = 0; i < output->wm_num; i++) {
+ vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 1);
+ vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[i]);
+ vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[i],
+ (ub_size + 1) * output->wm_idx[i], ub_size);
+ vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i],
+ &line->video_out.active_fmt.fmt.pix_mp, i, 1);
+ vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 1);
+ vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
+ }
+ vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 1);
+ vfe->ops_gen1->set_module_cfg(vfe, 1);
+ vfe->ops_gen1->set_camif_cfg(vfe, line);
+ vfe->ops_gen1->set_realign_cfg(vfe, line, 1);
+ vfe->ops_gen1->set_xbar_cfg(vfe, output, 1);
+ vfe->ops_gen1->set_demux_cfg(vfe, line);
+ vfe->ops_gen1->set_scale_cfg(vfe, line);
+ vfe->ops_gen1->set_crop_cfg(vfe, line);
+ vfe->ops_gen1->set_clamp_cfg(vfe);
+ vfe->ops_gen1->set_camif_cmd(vfe, 1);
+ }
+
+ ops->reg_update(vfe, line->id);
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+static int vfe_get_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ struct v4l2_format *f = &line->video_out.active_fmt;
+ unsigned long flags;
+ int i;
+ int wm_idx;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ output = &line->output;
+ if (output->state != VFE_OUTPUT_OFF) {
+ dev_err(vfe->camss->dev, "Output is running\n");
+ goto error;
+ }
+ output->state = VFE_OUTPUT_RESERVED;
+
+ output->gen1.active_buf = 0;
+
+ switch (f->fmt.pix_mp.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ output->wm_num = 2;
+ break;
+ default:
+ output->wm_num = 1;
+ break;
+ }
+
+ for (i = 0; i < output->wm_num; i++) {
+ wm_idx = vfe_reserve_wm(vfe, line->id);
+ if (wm_idx < 0) {
+ dev_err(vfe->camss->dev, "Can not reserve wm\n");
+ goto error_get_wm;
+ }
+ output->wm_idx[i] = wm_idx;
+ }
+
+ output->drop_update_idx = 0;
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+
+error_get_wm:
+ for (i--; i >= 0; i--)
+ vfe_release_wm(vfe, output->wm_idx[i]);
+ output->state = VFE_OUTPUT_OFF;
+error:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return -EINVAL;
+}
+
+int vfe_gen1_enable(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ int ret;
+
+ mutex_lock(&vfe->stream_lock);
+
+ if (!vfe->stream_count) {
+ vfe->ops_gen1->enable_irq_common(vfe);
+ vfe->ops_gen1->bus_enable_wr_if(vfe, 1);
+ vfe->ops_gen1->set_qos(vfe);
+ vfe->ops_gen1->set_ds(vfe);
+ }
+
+ vfe->stream_count++;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ ret = vfe_get_output(line);
+ if (ret < 0)
+ goto error_get_output;
+
+ ret = vfe_enable_output(line);
+ if (ret < 0)
+ goto error_enable_output;
+
+ vfe->was_streaming = 1;
+
+ return 0;
+
+error_enable_output:
+ vfe_put_output(line);
+
+error_get_output:
+ mutex_lock(&vfe->stream_lock);
+
+ if (vfe->stream_count == 1)
+ vfe->ops_gen1->bus_enable_wr_if(vfe, 0);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return ret;
+}
+
+static void vfe_output_update_ping_addr(struct vfe_device *vfe,
+ struct vfe_output *output, u8 sync,
+ struct vfe_line *line)
+{
+ u32 addr;
+ unsigned int i;
+
+ for (i = 0; i < output->wm_num; i++) {
+ if (output->buf[0])
+ addr = output->buf[0]->addr[i];
+ else
+ addr = 0;
+
+ vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
+ if (sync)
+ vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
+ }
+}
+
+static void vfe_output_update_pong_addr(struct vfe_device *vfe,
+ struct vfe_output *output, u8 sync,
+ struct vfe_line *line)
+{
+ u32 addr;
+ unsigned int i;
+
+ for (i = 0; i < output->wm_num; i++) {
+ if (output->buf[1])
+ addr = output->buf[1]->addr[i];
+ else
+ addr = 0;
+
+ vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
+ if (sync)
+ vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
+ }
+}
+
+static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
+ struct vfe_output *output)
+{
+ switch (output->state) {
+ case VFE_OUTPUT_CONTINUOUS:
+ vfe_output_frame_drop(vfe, output, 3);
+ break;
+ case VFE_OUTPUT_SINGLE:
+ default:
+ dev_err_ratelimited(vfe->camss->dev,
+ "Next buf in wrong state! %d\n",
+ output->state);
+ break;
+ }
+}
+
+static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
+ struct vfe_output *output)
+{
+ switch (output->state) {
+ case VFE_OUTPUT_CONTINUOUS:
+ output->state = VFE_OUTPUT_SINGLE;
+ vfe_output_frame_drop(vfe, output, 1);
+ break;
+ case VFE_OUTPUT_SINGLE:
+ output->state = VFE_OUTPUT_STOPPING;
+ vfe_output_frame_drop(vfe, output, 0);
+ break;
+ default:
+ dev_err_ratelimited(vfe->camss->dev,
+ "Last buff in wrong state! %d\n",
+ output->state);
+ break;
+ }
+}
+
+static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
+ struct vfe_output *output,
+ struct camss_buffer *new_buf,
+ struct vfe_line *line)
+{
+ int inactive_idx;
+
+ switch (output->state) {
+ case VFE_OUTPUT_SINGLE:
+ inactive_idx = !output->gen1.active_buf;
+
+ if (!output->buf[inactive_idx]) {
+ output->buf[inactive_idx] = new_buf;
+
+ if (inactive_idx)
+ vfe_output_update_pong_addr(vfe, output, 0, line);
+ else
+ vfe_output_update_ping_addr(vfe, output, 0, line);
+
+ vfe_output_frame_drop(vfe, output, 3);
+ output->state = VFE_OUTPUT_CONTINUOUS;
+ } else {
+ vfe_buf_add_pending(output, new_buf);
+ dev_err_ratelimited(vfe->camss->dev,
+ "Inactive buffer is busy\n");
+ }
+ break;
+
+ case VFE_OUTPUT_IDLE:
+ if (!output->buf[0]) {
+ output->buf[0] = new_buf;
+
+ vfe_output_init_addrs(vfe, output, 1, line);
+ vfe_output_frame_drop(vfe, output, 1);
+
+ output->state = VFE_OUTPUT_SINGLE;
+ } else {
+ vfe_buf_add_pending(output, new_buf);
+ dev_err_ratelimited(vfe->camss->dev,
+ "Output idle with buffer set!\n");
+ }
+ break;
+
+ case VFE_OUTPUT_CONTINUOUS:
+ default:
+ vfe_buf_add_pending(output, new_buf);
+ break;
+ }
+}
+
+/*
+ * vfe_isr_halt_ack - Process halt ack
+ * @vfe: VFE Device
+ */
+static void vfe_isr_halt_ack(struct vfe_device *vfe)
+{
+ complete(&vfe->halt_complete);
+ vfe->ops_gen1->halt_clear(vfe);
+}
+
+/*
+ * vfe_isr_sof - Process start of frame interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ struct vfe_output *output;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ output = &vfe->line[line_id].output;
+ if (output->gen1.wait_sof) {
+ output->gen1.wait_sof = 0;
+ complete(&output->sof);
+ }
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_isr_reg_update - Process reg update interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ struct vfe_output *output;
+ struct vfe_line *line = &vfe->line[line_id];
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ vfe->ops->reg_update_clear(vfe, line_id);
+
+ output = &line->output;
+
+ if (output->wait_reg_update) {
+ output->wait_reg_update = 0;
+ complete(&output->reg_update);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ return;
+ }
+
+ if (output->state == VFE_OUTPUT_STOPPING) {
+ /* Release last buffer when hw is idle */
+ if (output->last_buffer) {
+ vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
+ VB2_BUF_STATE_DONE);
+ output->last_buffer = NULL;
+ }
+ output->state = VFE_OUTPUT_IDLE;
+
+ /* Buffers received in stopping state are queued in */
+ /* dma pending queue, start next capture here */
+
+ output->buf[0] = vfe_buf_get_pending(output);
+ output->buf[1] = vfe_buf_get_pending(output);
+
+ if (!output->buf[0] && output->buf[1]) {
+ output->buf[0] = output->buf[1];
+ output->buf[1] = NULL;
+ }
+
+ if (output->buf[0])
+ output->state = VFE_OUTPUT_SINGLE;
+
+ if (output->buf[1])
+ output->state = VFE_OUTPUT_CONTINUOUS;
+
+ switch (output->state) {
+ case VFE_OUTPUT_SINGLE:
+ vfe_output_frame_drop(vfe, output, 2);
+ break;
+ case VFE_OUTPUT_CONTINUOUS:
+ vfe_output_frame_drop(vfe, output, 3);
+ break;
+ default:
+ vfe_output_frame_drop(vfe, output, 0);
+ break;
+ }
+
+ vfe_output_init_addrs(vfe, output, 1, &vfe->line[line_id]);
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_isr_wm_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
+{
+ struct camss_buffer *ready_buf;
+ struct vfe_output *output;
+ dma_addr_t *new_addr;
+ unsigned long flags;
+ u32 active_index;
+ u64 ts = ktime_get_ns();
+ unsigned int i;
+
+ active_index = vfe->ops_gen1->wm_get_ping_pong_status(vfe, wm);
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Received wm done for unmapped index\n");
+ goto out_unlock;
+ }
+ output = &vfe->line[vfe->wm_output_map[wm]].output;
+
+ if (output->gen1.active_buf == active_index && 0) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Active buffer mismatch!\n");
+ goto out_unlock;
+ }
+ output->gen1.active_buf = active_index;
+
+ ready_buf = output->buf[!active_index];
+ if (!ready_buf) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Missing ready buf %d %d!\n",
+ !active_index, output->state);
+ goto out_unlock;
+ }
+
+ ready_buf->vb.vb2_buf.timestamp = ts;
+ ready_buf->vb.sequence = output->sequence++;
+
+ /* Get next buffer */
+ output->buf[!active_index] = vfe_buf_get_pending(output);
+ if (!output->buf[!active_index]) {
+ /* No next buffer - set same address */
+ new_addr = ready_buf->addr;
+ vfe_buf_update_wm_on_last(vfe, output);
+ } else {
+ new_addr = output->buf[!active_index]->addr;
+ vfe_buf_update_wm_on_next(vfe, output);
+ }
+
+ if (active_index)
+ for (i = 0; i < output->wm_num; i++)
+ vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], new_addr[i]);
+ else
+ for (i = 0; i < output->wm_num; i++)
+ vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], new_addr[i]);
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ if (output->state == VFE_OUTPUT_STOPPING)
+ output->last_buffer = ready_buf;
+ else
+ vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+ return;
+
+out_unlock:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_queue_buffer - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_queue_buffer(struct camss_video *vid, struct camss_buffer *buf)
+{
+ struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ unsigned long flags;
+
+ output = &line->output;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ vfe_buf_update_wm_on_new(vfe, output, buf, line);
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
+
+int vfe_word_per_line(u32 format, u32 width)
+{
+ int val = 0;
+
+ switch (format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ val = CALC_WORD(width, 1, 8);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ val = CALC_WORD(width, 2, 8);
+ break;
+ }
+
+ return val;
+}
+
+const struct vfe_isr_ops vfe_isr_ops_gen1 = {
+ .reset_ack = vfe_isr_reset_ack,
+ .halt_ack = vfe_isr_halt_ack,
+ .reg_update = vfe_isr_reg_update,
+ .sof = vfe_isr_sof,
+ .comp_done = vfe_isr_comp_done,
+ .wm_done = vfe_isr_wm_done,
+};
+
+const struct camss_video_ops vfe_video_ops_gen1 = {
+ .queue_buffer = vfe_queue_buffer,
+ .flush_buffers = vfe_flush_buffers,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.h b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h
new file mode 100644
index 000000000000..6d5f9656562c
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-vfe.h
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2018 Linaro Ltd.
+ */
+#ifndef QC_MSM_CAMSS_VFE_GEN1_H
+#define QC_MSM_CAMSS_VFE_GEN1_H
+
+#include "camss-vfe.h"
+
+enum vfe_line_id;
+struct vfe_device;
+struct vfe_line;
+struct vfe_output;
+
+struct vfe_hw_ops_gen1 {
+ void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm, enum vfe_line_id id);
+ void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm, enum vfe_line_id id);
+ void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable);
+ void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm);
+ int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev);
+ void (*enable_irq_common)(struct vfe_device *vfe);
+ void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm, enum vfe_line_id line_id,
+ u8 enable);
+ void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp, enum vfe_line_id line_id,
+ u8 enable);
+ u16 (*get_ub_size)(u8 vfe_id);
+ void (*halt_clear)(struct vfe_device *vfe);
+ void (*halt_request)(struct vfe_device *vfe);
+ void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable);
+ void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable);
+ void (*set_clamp_cfg)(struct vfe_device *vfe);
+ void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_ds)(struct vfe_device *vfe);
+ void (*set_module_cfg)(struct vfe_device *vfe, u8 enable);
+ void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id, u8 cid);
+ void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line, u8 enable);
+ void (*set_qos)(struct vfe_device *vfe);
+ void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output, u8 enable);
+ void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable);
+ void (*wm_line_based)(struct vfe_device *vfe, u32 wm, struct v4l2_pix_format_mplane *pix,
+ u8 plane, u32 enable);
+ void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset, u16 depth);
+ void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm);
+ void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per);
+ void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm, u32 pattern);
+ void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
+ void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
+ int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm);
+ void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable);
+};
+
+/*
+ * vfe_calc_interp_reso - Calculate interpolation mode
+ * @input: Input resolution
+ * @output: Output resolution
+ *
+ * Return interpolation mode
+ */
+static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
+{
+ if (input / output >= 16)
+ return 0;
+
+ if (input / output >= 8)
+ return 1;
+
+ if (input / output >= 4)
+ return 2;
+
+ return 3;
+}
+
+/*
+ * vfe_gen1_disable - Disable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_gen1_disable(struct vfe_line *line);
+
+/*
+ * vfe_gen1_enable - Enable VFE module
+ * @line: VFE line
+ *
+ * Return 0 on success
+ */
+int vfe_gen1_enable(struct vfe_line *line);
+
+/*
+ * vfe_gen1_enable - Halt VFE module
+ * @vfe: VFE device
+ *
+ * Return 0 on success
+ */
+int vfe_gen1_halt(struct vfe_device *vfe);
+
+/*
+ * vfe_word_per_line - Calculate number of words per frame width
+ * @format: V4L2 format
+ * @width: Frame width
+ *
+ * Return number of words per frame width
+ */
+int vfe_word_per_line(u32 format, u32 width);
+
+extern const struct vfe_isr_ops vfe_isr_ops_gen1;
+extern const struct camss_video_ops vfe_video_ops_gen1;
+
+#endif /* QC_MSM_CAMSS_VFE_GEN1_H */
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index fae2b513b2f9..15695fd466c4 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -26,22 +26,8 @@
#define MSM_VFE_NAME "msm_vfe"
-#define vfe_line_array(ptr_line) \
- ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)]))
-
-#define to_vfe(ptr_line) \
- container_of(vfe_line_array(ptr_line), struct vfe_device, line)
-
/* VFE reset timeout */
#define VFE_RESET_TIMEOUT_MS 50
-/* VFE halt timeout */
-#define VFE_HALT_TIMEOUT_MS 100
-/* Max number of frame drop updates per frame */
-#define VFE_FRAME_DROP_UPDATES 2
-/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */
-#define VFE_FRAME_DROP_VAL 30
-
-#define VFE_NEXT_SOF_MS 500
#define SCALER_RATIO_MAX 16
@@ -110,6 +96,32 @@ static const struct vfe_format formats_pix_8x96[] = {
{ MEDIA_BUS_FMT_YVYU8_2X8, 8 },
};
+static const struct vfe_format formats_rdi_845[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
+ { MEDIA_BUS_FMT_Y10_1X10, 10 },
+ { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
+};
+
/*
* vfe_get_bpp - map media bus format to bits per pixel
* @formats: supported media bus formats array
@@ -206,7 +218,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
return sink_code;
}
else if (vfe->camss->version == CAMSS_8x96 ||
- vfe->camss->version == CAMSS_660)
+ vfe->camss->version == CAMSS_660 ||
+ vfe->camss->version == CAMSS_845)
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
{
@@ -270,13 +283,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
return 0;
}
-/*
- * vfe_reset - Trigger reset on VFE module and wait to complete
- * @vfe: VFE device
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_reset(struct vfe_device *vfe)
+int vfe_reset(struct vfe_device *vfe)
{
unsigned long time;
@@ -294,35 +301,11 @@ static int vfe_reset(struct vfe_device *vfe)
return 0;
}
-/*
- * vfe_halt - Trigger halt on VFE module and wait to complete
- * @vfe: VFE device
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_halt(struct vfe_device *vfe)
-{
- unsigned long time;
-
- reinit_completion(&vfe->halt_complete);
-
- vfe->ops->halt_request(vfe);
-
- time = wait_for_completion_timeout(&vfe->halt_complete,
- msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
- if (!time) {
- dev_err(vfe->camss->dev, "VFE halt timeout\n");
- return -EIO;
- }
-
- return 0;
-}
-
static void vfe_init_outputs(struct vfe_device *vfe)
{
int i;
- for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
+ for (i = 0; i < vfe->line_num; i++) {
struct vfe_output *output = &vfe->line[i].output;
output->state = VFE_OUTPUT_OFF;
@@ -340,71 +323,7 @@ static void vfe_reset_output_maps(struct vfe_device *vfe)
vfe->wm_output_map[i] = VFE_LINE_NONE;
}
-static void vfe_output_init_addrs(struct vfe_device *vfe,
- struct vfe_output *output, u8 sync)
-{
- u32 ping_addr;
- u32 pong_addr;
- unsigned int i;
-
- output->active_buf = 0;
-
- for (i = 0; i < output->wm_num; i++) {
- if (output->buf[0])
- ping_addr = output->buf[0]->addr[i];
- else
- ping_addr = 0;
-
- if (output->buf[1])
- pong_addr = output->buf[1]->addr[i];
- else
- pong_addr = ping_addr;
-
- vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
- vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
- if (sync)
- vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
- }
-}
-
-static void vfe_output_update_ping_addr(struct vfe_device *vfe,
- struct vfe_output *output, u8 sync)
-{
- u32 addr;
- unsigned int i;
-
- for (i = 0; i < output->wm_num; i++) {
- if (output->buf[0])
- addr = output->buf[0]->addr[i];
- else
- addr = 0;
-
- vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
- if (sync)
- vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
- }
-}
-
-static void vfe_output_update_pong_addr(struct vfe_device *vfe,
- struct vfe_output *output, u8 sync)
-{
- u32 addr;
- unsigned int i;
-
- for (i = 0; i < output->wm_num; i++) {
- if (output->buf[1])
- addr = output->buf[1]->addr[i];
- else
- addr = 0;
-
- vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
- if (sync)
- vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
- }
-
-}
-
-static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
+int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
{
int ret = -EBUSY;
int i;
@@ -420,7 +339,7 @@ static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
return ret;
}
-static int vfe_release_wm(struct vfe_device *vfe, u8 wm)
+int vfe_release_wm(struct vfe_device *vfe, u8 wm)
{
if (wm >= ARRAY_SIZE(vfe->wm_output_map))
return -EINVAL;
@@ -430,29 +349,7 @@ static int vfe_release_wm(struct vfe_device *vfe, u8 wm)
return 0;
}
-static void vfe_output_frame_drop(struct vfe_device *vfe,
- struct vfe_output *output,
- u32 drop_pattern)
-{
- u8 drop_period;
- unsigned int i;
-
- /* We need to toggle update period to be valid on next frame */
- output->drop_update_idx++;
- output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
- drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
-
- for (i = 0; i < output->wm_num; i++) {
- vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i],
- drop_period);
- vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i],
- drop_pattern);
- }
- vfe->ops->reg_update(vfe,
- container_of(output, struct vfe_line, output)->id);
-}
-
-static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
+struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
{
struct camss_buffer *buffer = NULL;
@@ -466,13 +363,8 @@ static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
return buffer;
}
-/*
- * vfe_buf_add_pending - Add output buffer to list of pending
- * @output: VFE output
- * @buffer: Video buffer
- */
-static void vfe_buf_add_pending(struct vfe_output *output,
- struct camss_buffer *buffer)
+void vfe_buf_add_pending(struct vfe_output *output,
+ struct camss_buffer *buffer)
{
INIT_LIST_HEAD(&buffer->queue);
list_add_tail(&buffer->queue, &output->pending_bufs);
@@ -495,149 +387,7 @@ static void vfe_buf_flush_pending(struct vfe_output *output,
}
}
-static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
- struct vfe_output *output)
-{
- switch (output->state) {
- case VFE_OUTPUT_CONTINUOUS:
- vfe_output_frame_drop(vfe, output, 3);
- break;
- case VFE_OUTPUT_SINGLE:
- default:
- dev_err_ratelimited(vfe->camss->dev,
- "Next buf in wrong state! %d\n",
- output->state);
- break;
- }
-}
-
-static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
- struct vfe_output *output)
-{
- switch (output->state) {
- case VFE_OUTPUT_CONTINUOUS:
- output->state = VFE_OUTPUT_SINGLE;
- vfe_output_frame_drop(vfe, output, 1);
- break;
- case VFE_OUTPUT_SINGLE:
- output->state = VFE_OUTPUT_STOPPING;
- vfe_output_frame_drop(vfe, output, 0);
- break;
- default:
- dev_err_ratelimited(vfe->camss->dev,
- "Last buff in wrong state! %d\n",
- output->state);
- break;
- }
-}
-
-static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
- struct vfe_output *output,
- struct camss_buffer *new_buf)
-{
- int inactive_idx;
-
- switch (output->state) {
- case VFE_OUTPUT_SINGLE:
- inactive_idx = !output->active_buf;
-
- if (!output->buf[inactive_idx]) {
- output->buf[inactive_idx] = new_buf;
-
- if (inactive_idx)
- vfe_output_update_pong_addr(vfe, output, 0);
- else
- vfe_output_update_ping_addr(vfe, output, 0);
-
- vfe_output_frame_drop(vfe, output, 3);
- output->state = VFE_OUTPUT_CONTINUOUS;
- } else {
- vfe_buf_add_pending(output, new_buf);
- dev_err_ratelimited(vfe->camss->dev,
- "Inactive buffer is busy\n");
- }
- break;
-
- case VFE_OUTPUT_IDLE:
- if (!output->buf[0]) {
- output->buf[0] = new_buf;
-
- vfe_output_init_addrs(vfe, output, 1);
-
- vfe_output_frame_drop(vfe, output, 1);
- output->state = VFE_OUTPUT_SINGLE;
- } else {
- vfe_buf_add_pending(output, new_buf);
- dev_err_ratelimited(vfe->camss->dev,
- "Output idle with buffer set!\n");
- }
- break;
-
- case VFE_OUTPUT_CONTINUOUS:
- default:
- vfe_buf_add_pending(output, new_buf);
- break;
- }
-}
-
-static int vfe_get_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- struct v4l2_format *f = &line->video_out.active_fmt;
- unsigned long flags;
- int i;
- int wm_idx;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- output = &line->output;
- if (output->state != VFE_OUTPUT_OFF) {
- dev_err(vfe->camss->dev, "Output is running\n");
- goto error;
- }
- output->state = VFE_OUTPUT_RESERVED;
-
- output->active_buf = 0;
-
- switch (f->fmt.pix_mp.pixelformat) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- output->wm_num = 2;
- break;
- default:
- output->wm_num = 1;
- break;
- }
-
- for (i = 0; i < output->wm_num; i++) {
- wm_idx = vfe_reserve_wm(vfe, line->id);
- if (wm_idx < 0) {
- dev_err(vfe->camss->dev, "Can not reserve wm\n");
- goto error_get_wm;
- }
- output->wm_idx[i] = wm_idx;
- }
-
- output->drop_update_idx = 0;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-
-error_get_wm:
- for (i--; i >= 0; i--)
- vfe_release_wm(vfe, output->wm_idx[i]);
- output->state = VFE_OUTPUT_OFF;
-error:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return -EINVAL;
-}
-
-static int vfe_put_output(struct vfe_line *line)
+int vfe_put_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
@@ -655,454 +405,27 @@ static int vfe_put_output(struct vfe_line *line)
return 0;
}
-static int vfe_enable_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output = &line->output;
- const struct vfe_hw_ops *ops = vfe->ops;
- struct media_entity *sensor;
- unsigned long flags;
- unsigned int frame_skip = 0;
- unsigned int i;
- u16 ub_size;
-
- ub_size = ops->get_ub_size(vfe->id);
- if (!ub_size)
- return -EINVAL;
-
- sensor = camss_find_sensor(&line->subdev.entity);
- if (sensor) {
- struct v4l2_subdev *subdev =
- media_entity_to_v4l2_subdev(sensor);
-
- v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
- /* Max frame skip is 29 frames */
- if (frame_skip > VFE_FRAME_DROP_VAL - 1)
- frame_skip = VFE_FRAME_DROP_VAL - 1;
- }
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- ops->reg_update_clear(vfe, line->id);
-
- if (output->state != VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
- output->state);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- return -EINVAL;
- }
- output->state = VFE_OUTPUT_IDLE;
-
- output->buf[0] = vfe_buf_get_pending(output);
- output->buf[1] = vfe_buf_get_pending(output);
-
- if (!output->buf[0] && output->buf[1]) {
- output->buf[0] = output->buf[1];
- output->buf[1] = NULL;
- }
-
- if (output->buf[0])
- output->state = VFE_OUTPUT_SINGLE;
-
- if (output->buf[1])
- output->state = VFE_OUTPUT_CONTINUOUS;
-
- switch (output->state) {
- case VFE_OUTPUT_SINGLE:
- vfe_output_frame_drop(vfe, output, 1 << frame_skip);
- break;
- case VFE_OUTPUT_CONTINUOUS:
- vfe_output_frame_drop(vfe, output, 3 << frame_skip);
- break;
- default:
- vfe_output_frame_drop(vfe, output, 0);
- break;
- }
-
- output->sequence = 0;
- output->wait_sof = 0;
- output->wait_reg_update = 0;
- reinit_completion(&output->sof);
- reinit_completion(&output->reg_update);
-
- vfe_output_init_addrs(vfe, output, 0);
-
- if (line->id != VFE_LINE_PIX) {
- ops->set_cgc_override(vfe, output->wm_idx[0], 1);
- ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
- ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
- ops->wm_set_subsample(vfe, output->wm_idx[0]);
- ops->set_rdi_cid(vfe, line->id, 0);
- ops->wm_set_ub_cfg(vfe, output->wm_idx[0],
- (ub_size + 1) * output->wm_idx[0], ub_size);
- ops->wm_frame_based(vfe, output->wm_idx[0], 1);
- ops->wm_enable(vfe, output->wm_idx[0], 1);
- ops->bus_reload_wm(vfe, output->wm_idx[0]);
- } else {
- ub_size /= output->wm_num;
- for (i = 0; i < output->wm_num; i++) {
- ops->set_cgc_override(vfe, output->wm_idx[i], 1);
- ops->wm_set_subsample(vfe, output->wm_idx[i]);
- ops->wm_set_ub_cfg(vfe, output->wm_idx[i],
- (ub_size + 1) * output->wm_idx[i],
- ub_size);
- ops->wm_line_based(vfe, output->wm_idx[i],
- &line->video_out.active_fmt.fmt.pix_mp,
- i, 1);
- ops->wm_enable(vfe, output->wm_idx[i], 1);
- ops->bus_reload_wm(vfe, output->wm_idx[i]);
- }
- ops->enable_irq_pix_line(vfe, 0, line->id, 1);
- ops->set_module_cfg(vfe, 1);
- ops->set_camif_cfg(vfe, line);
- ops->set_realign_cfg(vfe, line, 1);
- ops->set_xbar_cfg(vfe, output, 1);
- ops->set_demux_cfg(vfe, line);
- ops->set_scale_cfg(vfe, line);
- ops->set_crop_cfg(vfe, line);
- ops->set_clamp_cfg(vfe);
- ops->set_camif_cmd(vfe, 1);
- }
-
- ops->reg_update(vfe, line->id);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
-static int vfe_disable_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output = &line->output;
- const struct vfe_hw_ops *ops = vfe->ops;
- unsigned long flags;
- unsigned long time;
- unsigned int i;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- output->wait_sof = 1;
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- time = wait_for_completion_timeout(&output->sof,
- msecs_to_jiffies(VFE_NEXT_SOF_MS));
- if (!time)
- dev_err(vfe->camss->dev, "VFE sof timeout\n");
-
- spin_lock_irqsave(&vfe->output_lock, flags);
- for (i = 0; i < output->wm_num; i++)
- ops->wm_enable(vfe, output->wm_idx[i], 0);
-
- ops->reg_update(vfe, line->id);
- output->wait_reg_update = 1;
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- time = wait_for_completion_timeout(&output->reg_update,
- msecs_to_jiffies(VFE_NEXT_SOF_MS));
- if (!time)
- dev_err(vfe->camss->dev, "VFE reg update timeout\n");
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (line->id != VFE_LINE_PIX) {
- ops->wm_frame_based(vfe, output->wm_idx[0], 0);
- ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0],
- line->id);
- ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
- ops->set_cgc_override(vfe, output->wm_idx[0], 0);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- } else {
- for (i = 0; i < output->wm_num; i++) {
- ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
- ops->set_cgc_override(vfe, output->wm_idx[i], 0);
- }
-
- ops->enable_irq_pix_line(vfe, 0, line->id, 0);
- ops->set_module_cfg(vfe, 0);
- ops->set_realign_cfg(vfe, line, 0);
- ops->set_xbar_cfg(vfe, output, 0);
-
- ops->set_camif_cmd(vfe, 0);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- ops->camif_wait_for_stop(vfe, vfe->camss->dev);
- }
-
- return 0;
-}
-
-/*
- * vfe_enable - Enable streaming on VFE line
- * @line: VFE line
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_enable(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- int ret;
-
- mutex_lock(&vfe->stream_lock);
-
- if (!vfe->stream_count) {
- vfe->ops->enable_irq_common(vfe);
-
- vfe->ops->bus_enable_wr_if(vfe, 1);
-
- vfe->ops->set_qos(vfe);
-
- vfe->ops->set_ds(vfe);
- }
-
- vfe->stream_count++;
-
- mutex_unlock(&vfe->stream_lock);
-
- ret = vfe_get_output(line);
- if (ret < 0)
- goto error_get_output;
-
- ret = vfe_enable_output(line);
- if (ret < 0)
- goto error_enable_output;
-
- vfe->was_streaming = 1;
-
- return 0;
-
-
-error_enable_output:
- vfe_put_output(line);
-
-error_get_output:
- mutex_lock(&vfe->stream_lock);
-
- if (vfe->stream_count == 1)
- vfe->ops->bus_enable_wr_if(vfe, 0);
-
- vfe->stream_count--;
-
- mutex_unlock(&vfe->stream_lock);
-
- return ret;
-}
-
-/*
- * vfe_disable - Disable streaming on VFE line
- * @line: VFE line
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_disable(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
-
- vfe_disable_output(line);
-
- vfe_put_output(line);
-
- mutex_lock(&vfe->stream_lock);
-
- if (vfe->stream_count == 1)
- vfe->ops->bus_enable_wr_if(vfe, 0);
-
- vfe->stream_count--;
-
- mutex_unlock(&vfe->stream_lock);
-
- return 0;
-}
-
-/*
- * vfe_isr_sof - Process start of frame interrupt
- * @vfe: VFE Device
- * @line_id: VFE line
- */
-static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
-{
- struct vfe_output *output;
- unsigned long flags;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
- output = &vfe->line[line_id].output;
- if (output->wait_sof) {
- output->wait_sof = 0;
- complete(&output->sof);
- }
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-}
-
-/*
- * vfe_isr_reg_update - Process reg update interrupt
- * @vfe: VFE Device
- * @line_id: VFE line
- */
-static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
-{
- struct vfe_output *output;
- unsigned long flags;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
- vfe->ops->reg_update_clear(vfe, line_id);
-
- output = &vfe->line[line_id].output;
-
- if (output->wait_reg_update) {
- output->wait_reg_update = 0;
- complete(&output->reg_update);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- return;
- }
-
- if (output->state == VFE_OUTPUT_STOPPING) {
- /* Release last buffer when hw is idle */
- if (output->last_buffer) {
- vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
- VB2_BUF_STATE_DONE);
- output->last_buffer = NULL;
- }
- output->state = VFE_OUTPUT_IDLE;
-
- /* Buffers received in stopping state are queued in */
- /* dma pending queue, start next capture here */
-
- output->buf[0] = vfe_buf_get_pending(output);
- output->buf[1] = vfe_buf_get_pending(output);
-
- if (!output->buf[0] && output->buf[1]) {
- output->buf[0] = output->buf[1];
- output->buf[1] = NULL;
- }
-
- if (output->buf[0])
- output->state = VFE_OUTPUT_SINGLE;
-
- if (output->buf[1])
- output->state = VFE_OUTPUT_CONTINUOUS;
-
- switch (output->state) {
- case VFE_OUTPUT_SINGLE:
- vfe_output_frame_drop(vfe, output, 2);
- break;
- case VFE_OUTPUT_CONTINUOUS:
- vfe_output_frame_drop(vfe, output, 3);
- break;
- default:
- vfe_output_frame_drop(vfe, output, 0);
- break;
- }
-
- vfe_output_init_addrs(vfe, output, 1);
- }
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-}
-
-/*
- * vfe_isr_wm_done - Process write master done interrupt
- * @vfe: VFE Device
- * @wm: Write master id
- */
-static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
-{
- struct camss_buffer *ready_buf;
- struct vfe_output *output;
- dma_addr_t *new_addr;
- unsigned long flags;
- u32 active_index;
- u64 ts = ktime_get_ns();
- unsigned int i;
-
- active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm);
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
- dev_err_ratelimited(vfe->camss->dev,
- "Received wm done for unmapped index\n");
- goto out_unlock;
- }
- output = &vfe->line[vfe->wm_output_map[wm]].output;
-
- if (output->active_buf == active_index) {
- dev_err_ratelimited(vfe->camss->dev,
- "Active buffer mismatch!\n");
- goto out_unlock;
- }
- output->active_buf = active_index;
-
- ready_buf = output->buf[!active_index];
- if (!ready_buf) {
- dev_err_ratelimited(vfe->camss->dev,
- "Missing ready buf %d %d!\n",
- !active_index, output->state);
- goto out_unlock;
- }
-
- ready_buf->vb.vb2_buf.timestamp = ts;
- ready_buf->vb.sequence = output->sequence++;
-
- /* Get next buffer */
- output->buf[!active_index] = vfe_buf_get_pending(output);
- if (!output->buf[!active_index]) {
- /* No next buffer - set same address */
- new_addr = ready_buf->addr;
- vfe_buf_update_wm_on_last(vfe, output);
- } else {
- new_addr = output->buf[!active_index]->addr;
- vfe_buf_update_wm_on_next(vfe, output);
- }
-
- if (active_index)
- for (i = 0; i < output->wm_num; i++)
- vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i],
- new_addr[i]);
- else
- for (i = 0; i < output->wm_num; i++)
- vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i],
- new_addr[i]);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- if (output->state == VFE_OUTPUT_STOPPING)
- output->last_buffer = ready_buf;
- else
- vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
-
- return;
-
-out_unlock:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-}
-
-/*
- * vfe_isr_wm_done - Process composite image done interrupt
+/**
+ * vfe_isr_comp_done() - Process composite image done interrupt
* @vfe: VFE Device
* @comp: Composite image id
*/
-static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
+void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
- vfe_isr_wm_done(vfe, i);
+ vfe->isr_ops.wm_done(vfe, i);
break;
}
}
-static inline void vfe_isr_reset_ack(struct vfe_device *vfe)
+void vfe_isr_reset_ack(struct vfe_device *vfe)
{
complete(&vfe->reset_complete);
}
-static inline void vfe_isr_halt_ack(struct vfe_device *vfe)
-{
- complete(&vfe->halt_complete);
- vfe->ops->halt_clear(vfe);
-}
-
/*
* vfe_set_clock_rates - Calculate and set clock rates on VFE module
* @vfe: VFE device
@@ -1112,11 +435,11 @@ static inline void vfe_isr_halt_ack(struct vfe_device *vfe)
static int vfe_set_clock_rates(struct vfe_device *vfe)
{
struct device *dev = vfe->camss->dev;
- u32 pixel_clock[MSM_VFE_LINE_NUM];
+ u64 pixel_clock[VFE_LINE_NUM_MAX];
int i, j;
int ret;
- for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
&pixel_clock[i]);
if (ret)
@@ -1127,11 +450,12 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
struct camss_clock *clock = &vfe->clock[i];
if (!strcmp(clock->name, "vfe0") ||
- !strcmp(clock->name, "vfe1")) {
+ !strcmp(clock->name, "vfe1") ||
+ !strcmp(clock->name, "vfe_lite")) {
u64 min_rate = 0;
long rate;
- for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
+ for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
u32 tmp;
u8 bpp;
@@ -1194,11 +518,11 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
*/
static int vfe_check_clock_rates(struct vfe_device *vfe)
{
- u32 pixel_clock[MSM_VFE_LINE_NUM];
+ u64 pixel_clock[VFE_LINE_NUM_MAX];
int i, j;
int ret;
- for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
&pixel_clock[i]);
if (ret)
@@ -1213,7 +537,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
u64 min_rate = 0;
unsigned long rate;
- for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
+ for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
u32 tmp;
u8 bpp;
@@ -1256,7 +580,7 @@ static int vfe_get(struct vfe_device *vfe)
mutex_lock(&vfe->power_lock);
if (vfe->power_count == 0) {
- ret = camss_pm_domain_on(vfe->camss, vfe->id);
+ ret = vfe->ops->pm_domain_on(vfe);
if (ret < 0)
goto error_pm_domain;
@@ -1296,7 +620,7 @@ error_reset:
error_pm_runtime_get:
pm_runtime_put_sync(vfe->camss->dev);
- camss_pm_domain_off(vfe->camss, vfe->id);
+ vfe->ops->pm_domain_off(vfe);
error_pm_domain:
mutex_unlock(&vfe->power_lock);
@@ -1318,11 +642,11 @@ static void vfe_put(struct vfe_device *vfe)
} else if (vfe->power_count == 1) {
if (vfe->was_streaming) {
vfe->was_streaming = 0;
- vfe_halt(vfe);
+ vfe->ops->vfe_halt(vfe);
}
camss_disable_clocks(vfe->nclocks, vfe->clock);
pm_runtime_put_sync(vfe->camss->dev);
- camss_pm_domain_off(vfe->camss, vfe->id);
+ vfe->ops->pm_domain_off(vfe);
}
vfe->power_count--;
@@ -1332,35 +656,6 @@ exit:
}
/*
- * vfe_queue_buffer - Add empty buffer
- * @vid: Video device structure
- * @buf: Buffer to be enqueued
- *
- * Add an empty buffer - depending on the current number of buffers it will be
- * put in pending buffer queue or directly given to the hardware to be filled.
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_queue_buffer(struct camss_video *vid,
- struct camss_buffer *buf)
-{
- struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- output = &line->output;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- vfe_buf_update_wm_on_new(vfe, output, buf);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
-/*
* vfe_flush_buffers - Return all vb2 buffers
* @vid: Video device structure
* @state: vb2 buffer state of the returned buffers
@@ -1370,8 +665,8 @@ static int vfe_queue_buffer(struct camss_video *vid,
*
* Return 0 on success or a negative error code otherwise
*/
-static int vfe_flush_buffers(struct camss_video *vid,
- enum vb2_buffer_state state)
+int vfe_flush_buffers(struct camss_video *vid,
+ enum vb2_buffer_state state)
{
struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
struct vfe_device *vfe = to_vfe(line);
@@ -1442,12 +737,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (enable) {
- ret = vfe_enable(line);
+ ret = vfe->ops->vfe_enable(line);
if (ret < 0)
dev_err(vfe->camss->dev,
"Failed to enable vfe outputs\n");
} else {
- ret = vfe_disable(line);
+ ret = vfe->ops->vfe_disable(line);
if (ret < 0)
dev_err(vfe->camss->dev,
"Failed to disable vfe outputs\n");
@@ -1985,13 +1280,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
int i, j;
int ret;
- vfe->isr_ops.reset_ack = vfe_isr_reset_ack;
- vfe->isr_ops.halt_ack = vfe_isr_halt_ack;
- vfe->isr_ops.reg_update = vfe_isr_reg_update;
- vfe->isr_ops.sof = vfe_isr_sof;
- vfe->isr_ops.comp_done = vfe_isr_comp_done;
- vfe->isr_ops.wm_done = vfe_isr_wm_done;
-
switch (camss->version) {
case CAMSS_8x16:
vfe->ops = &vfe_ops_4_1;
@@ -2002,9 +1290,14 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
case CAMSS_660:
vfe->ops = &vfe_ops_4_8;
break;
+
+ case CAMSS_845:
+ vfe->ops = &vfe_ops_170;
+ break;
default:
return -EINVAL;
}
+ vfe->ops->subdev_init(dev, vfe);
/* Memory */
@@ -2086,7 +1379,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
vfe->id = id;
vfe->reg_update = 0;
- for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
struct vfe_line *l = &vfe->line[i];
l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
@@ -2112,6 +1405,9 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
l->formats = formats_rdi_8x96;
l->nformats = ARRAY_SIZE(formats_rdi_8x96);
}
+ } else if (camss->version == CAMSS_845) {
+ l->formats = formats_rdi_845;
+ l->nformats = ARRAY_SIZE(formats_rdi_845);
} else {
return -EINVAL;
}
@@ -2209,11 +1505,6 @@ static const struct media_entity_operations vfe_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
-static const struct camss_video_ops camss_vfe_video_ops = {
- .queue_buffer = vfe_queue_buffer,
- .flush_buffers = vfe_flush_buffers,
-};
-
/*
* msm_vfe_register_entities - Register subdev node for VFE module
* @vfe: VFE device
@@ -2236,7 +1527,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
int ret;
int i;
- for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
+ for (i = 0; i < vfe->line_num; i++) {
char name[32];
sd = &vfe->line[i].subdev;
@@ -2279,7 +1570,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
goto error_reg_subdev;
}
- video_out->ops = &camss_vfe_video_ops;
+ video_out->ops = &vfe->video_ops;
video_out->bpl_alignment = 8;
video_out->line_based = 0;
if (i == VFE_LINE_PIX) {
@@ -2343,7 +1634,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe)
mutex_destroy(&vfe->power_lock);
mutex_destroy(&vfe->stream_lock);
- for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
+ for (i = 0; i < vfe->line_num; i++) {
struct v4l2_subdev *sd = &vfe->line[i].subdev;
struct camss_video *video_out = &vfe->line[i].video_out;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 5bce6736e4bb..844b9275031d 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -17,22 +17,34 @@
#include <media/v4l2-subdev.h>
#include "camss-video.h"
+#include "camss-vfe-gen1.h"
#define MSM_VFE_PAD_SINK 0
#define MSM_VFE_PAD_SRC 1
#define MSM_VFE_PADS_NUM 2
-#define MSM_VFE_LINE_NUM 4
#define MSM_VFE_IMAGE_MASTERS_NUM 7
#define MSM_VFE_COMPOSITE_IRQ_NUM 4
+/* VFE halt timeout */
+#define VFE_HALT_TIMEOUT_MS 100
+/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */
+#define VFE_FRAME_DROP_VAL 30
+
+#define vfe_line_array(ptr_line) \
+ ((const struct vfe_line (*)[]) &(ptr_line)[-(ptr_line)->id])
+
+#define to_vfe(ptr_line) \
+ container_of(vfe_line_array(ptr_line), struct vfe_device, line)
+
enum vfe_output_state {
VFE_OUTPUT_OFF,
VFE_OUTPUT_RESERVED,
VFE_OUTPUT_SINGLE,
VFE_OUTPUT_CONTINUOUS,
VFE_OUTPUT_IDLE,
- VFE_OUTPUT_STOPPING
+ VFE_OUTPUT_STOPPING,
+ VFE_OUTPUT_ON,
};
enum vfe_line_id {
@@ -40,23 +52,34 @@ enum vfe_line_id {
VFE_LINE_RDI0 = 0,
VFE_LINE_RDI1 = 1,
VFE_LINE_RDI2 = 2,
- VFE_LINE_PIX = 3
+ VFE_LINE_NUM_GEN2 = 3,
+ VFE_LINE_PIX = 3,
+ VFE_LINE_NUM_GEN1 = 4,
+ VFE_LINE_NUM_MAX = 4
};
struct vfe_output {
u8 wm_num;
u8 wm_idx[3];
- int active_buf;
struct camss_buffer *buf[2];
struct camss_buffer *last_buffer;
struct list_head pending_bufs;
unsigned int drop_update_idx;
+ union {
+ struct {
+ int active_buf;
+ int wait_sof;
+ } gen1;
+ struct {
+ int active_num;
+ } gen2;
+ };
enum vfe_output_state state;
unsigned int sequence;
- int wait_sof;
+
int wait_reg_update;
struct completion sof;
struct completion reg_update;
@@ -78,59 +101,21 @@ struct vfe_line {
struct vfe_device;
struct vfe_hw_ops {
- void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
- u16 (*get_ub_size)(u8 vfe_id);
+ void (*enable_irq_common)(struct vfe_device *vfe);
void (*global_reset)(struct vfe_device *vfe);
- void (*halt_request)(struct vfe_device *vfe);
- void (*halt_clear)(struct vfe_device *vfe);
- void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable);
- void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable);
- void (*wm_line_based)(struct vfe_device *vfe, u32 wm,
- struct v4l2_pix_format_mplane *pix,
- u8 plane, u32 enable);
- void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per);
- void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm,
- u32 pattern);
- void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset,
- u16 depth);
- void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm);
- void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
- void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
- int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm);
- void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable);
- void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm,
- enum vfe_line_id id);
- void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm);
- void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm,
- enum vfe_line_id id);
- void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output,
- u8 enable);
- void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id,
- u8 cid);
- void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line,
- u8 enable);
+ void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
+ irqreturn_t (*isr)(int irq, void *dev);
+ void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1);
+ void (*pm_domain_off)(struct vfe_device *vfe);
+ int (*pm_domain_on)(struct vfe_device *vfe);
void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id);
void (*reg_update_clear)(struct vfe_device *vfe,
enum vfe_line_id line_id);
- void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm,
- enum vfe_line_id line_id, u8 enable);
- void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp,
- enum vfe_line_id line_id, u8 enable);
- void (*enable_irq_common)(struct vfe_device *vfe);
- void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line);
- void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line);
- void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line);
- void (*set_clamp_cfg)(struct vfe_device *vfe);
- void (*set_qos)(struct vfe_device *vfe);
- void (*set_ds)(struct vfe_device *vfe);
- void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable);
- void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line);
- void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable);
- void (*set_module_cfg)(struct vfe_device *vfe, u8 enable);
- int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev);
- void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1);
+ void (*subdev_init)(struct device *dev, struct vfe_device *vfe);
+ int (*vfe_disable)(struct vfe_line *line);
+ int (*vfe_enable)(struct vfe_line *line);
+ int (*vfe_halt)(struct vfe_device *vfe);
void (*violation_read)(struct vfe_device *vfe);
- irqreturn_t (*isr)(int irq, void *dev);
};
struct vfe_isr_ops {
@@ -158,11 +143,14 @@ struct vfe_device {
int stream_count;
spinlock_t output_lock;
enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
- struct vfe_line line[MSM_VFE_LINE_NUM];
+ struct vfe_line line[VFE_LINE_NUM_MAX];
+ u8 line_num;
u32 reg_update;
u8 was_streaming;
const struct vfe_hw_ops *ops;
+ const struct vfe_hw_ops_gen1 *ops_gen1;
struct vfe_isr_ops isr_ops;
+ struct camss_video_ops video_ops;
};
struct resources;
@@ -178,8 +166,40 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe);
void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
+/*
+ * vfe_buf_add_pending - Add output buffer to list of pending
+ * @output: VFE output
+ * @buffer: Video buffer
+ */
+void vfe_buf_add_pending(struct vfe_output *output, struct camss_buffer *buffer);
+
+struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output);
+
+int vfe_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state);
+
+/*
+ * vfe_isr_comp_done - Process composite image done interrupt
+ * @vfe: VFE Device
+ * @comp: Composite image id
+ */
+void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp);
+
+void vfe_isr_reset_ack(struct vfe_device *vfe);
+int vfe_put_output(struct vfe_line *line);
+int vfe_release_wm(struct vfe_device *vfe, u8 wm);
+int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id);
+
+/*
+ * vfe_reset - Trigger reset on VFE module and wait to complete
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_reset(struct vfe_device *vfe);
+
extern const struct vfe_hw_ops vfe_ops_4_1;
extern const struct vfe_hw_ops vfe_ops_4_7;
extern const struct vfe_hw_ops vfe_ops_4_8;
+extern const struct vfe_hw_ops vfe_ops_170;
#endif /* QC_MSM_CAMSS_VFE_H */
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 97cea7c4d769..f282275af626 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -133,6 +133,55 @@ static const struct camss_format_info formats_rdi_8x96[] = {
{ { 1, 1 } }, { { 1, 1 } }, { 16 } },
};
+static const struct camss_format_info formats_rdi_845[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+};
+
static const struct camss_format_info formats_pix_8x16[] = {
{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
{ { 1, 1 } }, { { 2, 3 } }, { 8 } },
@@ -960,6 +1009,9 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
video->formats = formats_rdi_8x96;
video->nformats = ARRAY_SIZE(formats_rdi_8x96);
}
+ } else if (video->camss->version == CAMSS_845) {
+ video->formats = formats_rdi_845;
+ video->nformats = ARRAY_SIZE(formats_rdi_845);
} else {
ret = -EINVAL;
goto error_video_register;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 7c0f669f8aa6..ef100d5f7763 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -465,6 +465,203 @@ static const struct resources vfe_res_660[] = {
}
};
+static const struct resources csiphy_res_845[] = {
+ /* CSIPHY0 */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
+ "cpas_ahb", "cphy_rx_src", "csiphy0",
+ "csiphy0_timer_src", "csiphy0_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" }
+ },
+
+ /* CSIPHY1 */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
+ "cpas_ahb", "cphy_rx_src", "csiphy1",
+ "csiphy1_timer_src", "csiphy1_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" }
+ },
+
+ /* CSIPHY2 */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
+ "cpas_ahb", "cphy_rx_src", "csiphy2",
+ "csiphy2_timer_src", "csiphy2_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" }
+ },
+
+ /* CSIPHY3 */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
+ "cpas_ahb", "cphy_rx_src", "csiphy3",
+ "csiphy3_timer_src", "csiphy3_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" }
+ }
+};
+
+static const struct resources csid_res_845[] = {
+ /* CSID0 */
+ {
+ .regulator = { "vdda-csi0" },
+ .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
+ "soc_ahb", "vfe0", "vfe0_src",
+ "vfe0_cphy_rx", "csi0",
+ "csi0_src" },
+ .clock_rate = { { 0 },
+ { 384000000 },
+ { 80000000 },
+ { 0 },
+ { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 320000000 },
+ { 0 },
+ { 19200000, 75000000, 384000000, 538666667 },
+ { 384000000 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" }
+ },
+
+ /* CSID1 */
+ {
+ .regulator = { "vdda-csi1" },
+ .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
+ "soc_ahb", "vfe1", "vfe1_src",
+ "vfe1_cphy_rx", "csi1",
+ "csi1_src" },
+ .clock_rate = { { 0 },
+ { 384000000 },
+ { 80000000 },
+ { 0 },
+ { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 320000000 },
+ { 0 },
+ { 19200000, 75000000, 384000000, 538666667 },
+ { 384000000 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" }
+ },
+
+ /* CSID2 */
+ {
+ .regulator = { "vdda-csi2" },
+ .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
+ "soc_ahb", "vfe_lite", "vfe_lite_src",
+ "vfe_lite_cphy_rx", "csi2",
+ "csi2_src" },
+ .clock_rate = { { 0 },
+ { 384000000 },
+ { 80000000 },
+ { 0 },
+ { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 320000000 },
+ { 0 },
+ { 19200000, 75000000, 384000000, 538666667 },
+ { 384000000 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" }
+ }
+};
+
+static const struct resources vfe_res_845[] = {
+ /* VFE0 */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src",
+ "soc_ahb", "vfe0", "vfe0_axi",
+ "vfe0_src", "csi0",
+ "csi0_src"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 80000000 },
+ { 0 },
+ { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 0 },
+ { 320000000 },
+ { 19200000, 75000000, 384000000, 538666667 },
+ { 384000000 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" }
+ },
+
+ /* VFE1 */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src",
+ "soc_ahb", "vfe1", "vfe1_axi",
+ "vfe1_src", "csi1",
+ "csi1_src"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 80000000 },
+ { 0 },
+ { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 0 },
+ { 320000000 },
+ { 19200000, 75000000, 384000000, 538666667 },
+ { 384000000 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" }
+ },
+
+ /* VFE-lite */
+ {
+ .regulator = { NULL },
+ .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src",
+ "soc_ahb", "vfe_lite",
+ "vfe_lite_src", "csi2",
+ "csi2_src"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 80000000 },
+ { 0 },
+ { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 320000000 },
+ { 19200000, 75000000, 384000000, 538666667 },
+ { 384000000 } },
+ .reg = { "vfe_lite" },
+ .interrupt = { "vfe_lite" }
+ }
+};
+
/*
* camss_add_clock_margin - Add margin to clock frequency rate
* @rate: Clock frequency rate
@@ -548,6 +745,29 @@ struct media_entity *camss_find_sensor(struct media_entity *entity)
}
}
+/**
+ * camss_get_link_freq - Get link frequency from sensor
+ * @entity: Media entity in the current pipeline
+ * @bpp: Number of bits per pixel for the current format
+ * @lanes: Number of lanes in the link to the sensor
+ *
+ * Return link frequency on success or a negative error code otherwise
+ */
+s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
+ unsigned int lanes)
+{
+ struct media_entity *sensor;
+ struct v4l2_subdev *subdev;
+
+ sensor = camss_find_sensor(entity);
+ if (!sensor)
+ return -ENODEV;
+
+ subdev = media_entity_to_v4l2_subdev(sensor);
+
+ return v4l2_get_link_freq(subdev->ctrl_handler, bpp, 2 * lanes);
+}
+
/*
* camss_get_pixel_clock - Get pixel clock rate from sensor
* @entity: Media entity in the current pipeline
@@ -555,7 +775,7 @@ struct media_entity *camss_find_sensor(struct media_entity *entity)
*
* Return 0 on success or a negative error code otherwise
*/
-int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
+int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock)
{
struct media_entity *sensor;
struct v4l2_subdev *subdev;
@@ -579,24 +799,24 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
int camss_pm_domain_on(struct camss *camss, int id)
{
- if (camss->version == CAMSS_8x96 ||
- camss->version == CAMSS_660) {
- camss->genpd_link[id] = device_link_add(camss->dev,
- camss->genpd[id], DL_FLAG_STATELESS |
- DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+ int ret = 0;
+
+ if (id < camss->vfe_num) {
+ struct vfe_device *vfe = &camss->vfe[id];
- if (!camss->genpd_link[id])
- return -EINVAL;
+ ret = vfe->ops->pm_domain_on(vfe);
}
- return 0;
+ return ret;
}
void camss_pm_domain_off(struct camss *camss, int id)
{
- if (camss->version == CAMSS_8x96 ||
- camss->version == CAMSS_660)
- device_link_del(camss->genpd_link[id]);
+ if (id < camss->vfe_num) {
+ struct vfe_device *vfe = &camss->vfe[id];
+
+ vfe->ops->pm_domain_off(vfe);
+ }
}
/*
@@ -719,6 +939,12 @@ static int camss_init_subdevices(struct camss *camss)
csid_res = csid_res_660;
ispif_res = &ispif_res_660;
vfe_res = vfe_res_660;
+ } else if (camss->version == CAMSS_845) {
+ csiphy_res = csiphy_res_845;
+ csid_res = csid_res_845;
+ /* Titan VFEs don't have an ISPIF */
+ ispif_res = NULL;
+ vfe_res = vfe_res_845;
} else {
return -EINVAL;
}
@@ -745,10 +971,10 @@ static int camss_init_subdevices(struct camss *camss)
}
}
- ret = msm_ispif_subdev_init(&camss->ispif, ispif_res);
+ ret = msm_ispif_subdev_init(camss, ispif_res);
if (ret < 0) {
dev_err(camss->dev, "Failed to init ispif sub-device: %d\n",
- ret);
+ ret);
return ret;
}
@@ -798,10 +1024,11 @@ static int camss_register_entities(struct camss *camss)
}
}
- ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev);
+ ret = msm_ispif_register_entities(camss->ispif,
+ &camss->v4l2_dev);
if (ret < 0) {
dev_err(camss->dev, "Failed to register ispif entities: %d\n",
- ret);
+ ret);
goto err_reg_ispif;
}
@@ -835,43 +1062,68 @@ static int camss_register_entities(struct camss *camss)
}
}
- for (i = 0; i < camss->csid_num; i++) {
- for (j = 0; j < camss->ispif.line_num; j++) {
- ret = media_create_pad_link(
- &camss->csid[i].subdev.entity,
- MSM_CSID_PAD_SRC,
- &camss->ispif.line[j].subdev.entity,
- MSM_ISPIF_PAD_SINK,
- 0);
- if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- camss->csid[i].subdev.entity.name,
- camss->ispif.line[j].subdev.entity.name,
- ret);
- goto err_link;
- }
- }
- }
-
- for (i = 0; i < camss->ispif.line_num; i++)
- for (k = 0; k < camss->vfe_num; k++)
- for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) {
+ if (camss->ispif) {
+ for (i = 0; i < camss->csid_num; i++) {
+ for (j = 0; j < camss->ispif->line_num; j++) {
ret = media_create_pad_link(
- &camss->ispif.line[i].subdev.entity,
- MSM_ISPIF_PAD_SRC,
- &camss->vfe[k].line[j].subdev.entity,
- MSM_VFE_PAD_SINK,
+ &camss->csid[i].subdev.entity,
+ MSM_CSID_PAD_SRC,
+ &camss->ispif->line[j].subdev.entity,
+ MSM_ISPIF_PAD_SINK,
0);
if (ret < 0) {
dev_err(camss->dev,
"Failed to link %s->%s entities: %d\n",
- camss->ispif.line[i].subdev.entity.name,
- camss->vfe[k].line[j].subdev.entity.name,
+ camss->csid[i].subdev.entity.name,
+ camss->ispif->line[j].subdev.entity.name,
ret);
goto err_link;
}
}
+ }
+
+ for (i = 0; i < camss->ispif->line_num; i++)
+ for (k = 0; k < camss->vfe_num; k++)
+ for (j = 0; j < camss->vfe[k].line_num; j++) {
+ struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev;
+ struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev;
+
+ ret = media_create_pad_link(&ispif->entity,
+ MSM_ISPIF_PAD_SRC,
+ &vfe->entity,
+ MSM_VFE_PAD_SINK,
+ 0);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to link %s->%s entities: %d\n",
+ ispif->entity.name,
+ vfe->entity.name,
+ ret);
+ goto err_link;
+ }
+ }
+ } else {
+ for (i = 0; i < camss->csid_num; i++)
+ for (k = 0; k < camss->vfe_num; k++)
+ for (j = 0; j < camss->vfe[k].line_num; j++) {
+ struct v4l2_subdev *csid = &camss->csid[i].subdev;
+ struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev;
+
+ ret = media_create_pad_link(&csid->entity,
+ MSM_CSID_PAD_SRC,
+ &vfe->entity,
+ MSM_VFE_PAD_SINK,
+ 0);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to link %s->%s entities: %d\n",
+ csid->entity.name,
+ vfe->entity.name,
+ ret);
+ goto err_link;
+ }
+ }
+ }
return 0;
@@ -881,8 +1133,8 @@ err_reg_vfe:
for (i--; i >= 0; i--)
msm_vfe_unregister_entities(&camss->vfe[i]);
- msm_ispif_unregister_entities(&camss->ispif);
err_reg_ispif:
+ msm_ispif_unregister_entities(camss->ispif);
i = camss->csid_num;
err_reg_csid:
@@ -913,7 +1165,7 @@ static void camss_unregister_entities(struct camss *camss)
for (i = 0; i < camss->csid_num; i++)
msm_csid_unregister_entity(&camss->csid[i]);
- msm_ispif_unregister_entities(&camss->ispif);
+ msm_ispif_unregister_entities(camss->ispif);
for (i = 0; i < camss->vfe_num; i++)
msm_vfe_unregister_entities(&camss->vfe[i]);
@@ -988,6 +1240,49 @@ static const struct media_device_ops camss_media_ops = {
.link_notify = v4l2_pipeline_link_notify,
};
+static int camss_configure_pd(struct camss *camss)
+{
+ int nbr_pm_domains = 0;
+ int last_pm_domain = 0;
+ int i;
+ int ret;
+
+ if (camss->version == CAMSS_8x96 ||
+ camss->version == CAMSS_660)
+ nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
+ else if (camss->version == CAMSS_845)
+ nbr_pm_domains = PM_DOMAIN_GEN2_COUNT;
+
+ for (i = 0; i < nbr_pm_domains; i++) {
+ camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i);
+ if (IS_ERR(camss->genpd[i])) {
+ ret = PTR_ERR(camss->genpd[i]);
+ goto fail_pm;
+ }
+
+ camss->genpd_link[i] = device_link_add(camss->dev, camss->genpd[i],
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!camss->genpd_link[i]) {
+ dev_pm_domain_detach(camss->genpd[i], true);
+ ret = -EINVAL;
+ goto fail_pm;
+ }
+
+ last_pm_domain = i;
+ }
+
+ return 0;
+
+fail_pm:
+ for (i = 0; i < last_pm_domain; i++) {
+ device_link_del(camss->genpd_link[i]);
+ dev_pm_domain_detach(camss->genpd[i], true);
+ }
+
+ return ret;
+}
+
/*
* camss_probe - Probe CAMSS platform device
* @pdev: Pointer to CAMSS platform device
@@ -1025,6 +1320,12 @@ static int camss_probe(struct platform_device *pdev)
camss->csiphy_num = 3;
camss->csid_num = 4;
camss->vfe_num = 2;
+ } else if (of_device_is_compatible(dev->of_node,
+ "qcom,sdm845-camss")) {
+ camss->version = CAMSS_845;
+ camss->csiphy_num = 4;
+ camss->csid_num = 3;
+ camss->vfe_num = 3;
} else {
ret = -EINVAL;
goto err_free;
@@ -1044,6 +1345,15 @@ static int camss_probe(struct platform_device *pdev)
goto err_free;
}
+ if (camss->version == CAMSS_8x16 ||
+ camss->version == CAMSS_8x96) {
+ camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL);
+ if (!camss->ispif) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ }
+
camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe),
GFP_KERNEL);
if (!camss->vfe) {
@@ -1111,20 +1421,10 @@ static int camss_probe(struct platform_device *pdev)
}
}
- if (camss->version == CAMSS_8x96 ||
- camss->version == CAMSS_660) {
- camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id(
- camss->dev, PM_DOMAIN_VFE0);
- if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0]))
- return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]);
-
- camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id(
- camss->dev, PM_DOMAIN_VFE1);
- if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) {
- dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0],
- true);
- return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]);
- }
+ ret = camss_configure_pd(camss);
+ if (ret < 0) {
+ dev_err(dev, "Failed to configure power domains: %d\n", ret);
+ return ret;
}
pm_runtime_enable(dev);
@@ -1145,6 +1445,9 @@ err_free:
void camss_delete(struct camss *camss)
{
+ int nbr_pm_domains = 0;
+ int i;
+
v4l2_device_unregister(&camss->v4l2_dev);
media_device_unregister(&camss->media_dev);
media_device_cleanup(&camss->media_dev);
@@ -1152,9 +1455,14 @@ void camss_delete(struct camss *camss)
pm_runtime_disable(camss->dev);
if (camss->version == CAMSS_8x96 ||
- camss->version == CAMSS_660) {
- dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true);
- dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true);
+ camss->version == CAMSS_660)
+ nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
+ else if (camss->version == CAMSS_845)
+ nbr_pm_domains = PM_DOMAIN_GEN2_COUNT;
+
+ for (i = 0; i < nbr_pm_domains; i++) {
+ device_link_del(camss->genpd_link[i]);
+ dev_pm_domain_detach(camss->genpd[i], true);
}
kfree(camss);
@@ -1184,6 +1492,7 @@ static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8916-camss" },
{ .compatible = "qcom,msm8996-camss" },
{ .compatible = "qcom,sdm660-camss" },
+ { .compatible = "qcom,sdm845-camss" },
{ }
};
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 3a0484683cd6..dc8b4154f92b 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -57,15 +57,18 @@ struct resources_ispif {
};
enum pm_domain {
- PM_DOMAIN_VFE0,
- PM_DOMAIN_VFE1,
- PM_DOMAIN_COUNT
+ PM_DOMAIN_VFE0 = 0,
+ PM_DOMAIN_VFE1 = 1,
+ PM_DOMAIN_GEN1_COUNT = 2, /* CAMSS series of ISPs */
+ PM_DOMAIN_VFELITE = 2, /* VFELITE / TOP GDSC */
+ PM_DOMAIN_GEN2_COUNT = 3, /* Titan series of ISPs */
};
enum camss_version {
CAMSS_8x16,
CAMSS_8x96,
CAMSS_660,
+ CAMSS_845,
};
struct camss {
@@ -78,12 +81,12 @@ struct camss {
struct csiphy_device *csiphy;
int csid_num;
struct csid_device *csid;
- struct ispif_device ispif;
+ struct ispif_device *ispif;
int vfe_num;
struct vfe_device *vfe;
atomic_t ref_count;
- struct device *genpd[PM_DOMAIN_COUNT];
- struct device_link *genpd_link[PM_DOMAIN_COUNT];
+ struct device *genpd[PM_DOMAIN_GEN2_COUNT];
+ struct device_link *genpd_link[PM_DOMAIN_GEN2_COUNT];
};
struct camss_camera_interface {
@@ -108,7 +111,9 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
struct device *dev);
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
struct media_entity *camss_find_sensor(struct media_entity *entity);
-int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock);
+s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
+ unsigned int lanes);
+int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock);
int camss_pm_domain_on(struct camss *camss, int id);
void camss_pm_domain_off(struct camss *camss, int id);
void camss_delete(struct camss *camss);
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index f9896c121fd8..54bac7ec14c5 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -5,6 +5,7 @@
*/
#include <linux/init.h>
#include <linux/interconnect.h>
+#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/devcoredump.h>
@@ -22,6 +23,7 @@
#include "core.h"
#include "firmware.h"
#include "pm_helpers.h"
+#include "hfi_venus_io.h"
static void venus_coredump(struct venus_core *core)
{
@@ -206,6 +208,27 @@ err:
return ret;
}
+static void venus_assign_register_offsets(struct venus_core *core)
+{
+ if (IS_V6(core)) {
+ core->vbif_base = core->base + VBIF_BASE;
+ core->cpu_base = core->base + CPU_BASE_V6;
+ core->cpu_cs_base = core->base + CPU_CS_BASE_V6;
+ core->cpu_ic_base = core->base + CPU_IC_BASE_V6;
+ core->wrapper_base = core->base + WRAPPER_BASE_V6;
+ core->wrapper_tz_base = core->base + WRAPPER_TZ_BASE_V6;
+ core->aon_base = core->base + AON_BASE_V6;
+ } else {
+ core->vbif_base = core->base + VBIF_BASE;
+ core->cpu_base = core->base + CPU_BASE;
+ core->cpu_cs_base = core->base + CPU_CS_BASE;
+ core->cpu_ic_base = core->base + CPU_IC_BASE;
+ core->wrapper_base = core->base + WRAPPER_BASE;
+ core->wrapper_tz_base = NULL;
+ core->aon_base = NULL;
+ }
+}
+
static int venus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -218,18 +241,17 @@ static int venus_probe(struct platform_device *pdev)
return -ENOMEM;
core->dev = dev;
- platform_set_drvdata(pdev, core);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
core->base = devm_ioremap_resource(dev, r);
if (IS_ERR(core->base))
return PTR_ERR(core->base);
- core->video_path = of_icc_get(dev, "video-mem");
+ core->video_path = devm_of_icc_get(dev, "video-mem");
if (IS_ERR(core->video_path))
return PTR_ERR(core->video_path);
- core->cpucfg_path = of_icc_get(dev, "cpu-cfg");
+ core->cpucfg_path = devm_of_icc_get(dev, "cpu-cfg");
if (IS_ERR(core->cpucfg_path))
return PTR_ERR(core->cpucfg_path);
@@ -248,7 +270,7 @@ static int venus_probe(struct platform_device *pdev)
return -ENODEV;
if (core->pm_ops->core_get) {
- ret = core->pm_ops->core_get(dev);
+ ret = core->pm_ops->core_get(core);
if (ret)
return ret;
}
@@ -273,6 +295,14 @@ static int venus_probe(struct platform_device *pdev)
if (ret)
goto err_core_put;
+ venus_assign_register_offsets(core);
+
+ ret = v4l2_device_register(dev, &core->v4l2_dev);
+ if (ret)
+ goto err_core_deinit;
+
+ platform_set_drvdata(pdev, core);
+
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
@@ -307,10 +337,6 @@ static int venus_probe(struct platform_device *pdev)
if (ret)
goto err_venus_shutdown;
- ret = v4l2_device_register(dev, &core->v4l2_dev);
- if (ret)
- goto err_core_deinit;
-
ret = pm_runtime_put_sync(dev);
if (ret) {
pm_runtime_get_noresume(dev);
@@ -323,8 +349,6 @@ static int venus_probe(struct platform_device *pdev)
err_dev_unregister:
v4l2_device_unregister(&core->v4l2_dev);
-err_core_deinit:
- hfi_core_deinit(core, false);
err_venus_shutdown:
venus_shutdown(core);
err_runtime_disable:
@@ -332,9 +356,11 @@ err_runtime_disable:
pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
hfi_destroy(core);
+err_core_deinit:
+ hfi_core_deinit(core, false);
err_core_put:
if (core->pm_ops->core_put)
- core->pm_ops->core_put(dev);
+ core->pm_ops->core_put(core);
return ret;
}
@@ -360,14 +386,12 @@ static int venus_remove(struct platform_device *pdev)
pm_runtime_disable(dev);
if (pm_ops->core_put)
- pm_ops->core_put(dev);
+ pm_ops->core_put(core);
- hfi_destroy(core);
+ v4l2_device_unregister(&core->v4l2_dev);
- icc_put(core->video_path);
- icc_put(core->cpucfg_path);
+ hfi_destroy(core);
- v4l2_device_unregister(&core->v4l2_dev);
mutex_destroy(&core->pm_lock);
mutex_destroy(&core->lock);
venus_dbgfs_deinit(core);
@@ -396,7 +420,7 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev)
return ret;
if (pm_ops->core_power) {
- ret = pm_ops->core_power(dev, POWER_OFF);
+ ret = pm_ops->core_power(core, POWER_OFF);
if (ret)
return ret;
}
@@ -414,7 +438,7 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev)
err_video_path:
icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0);
err_cpucfg_path:
- pm_ops->core_power(dev, POWER_ON);
+ pm_ops->core_power(core, POWER_ON);
return ret;
}
@@ -434,7 +458,7 @@ static __maybe_unused int venus_runtime_resume(struct device *dev)
return ret;
if (pm_ops->core_power) {
- ret = pm_ops->core_power(dev, POWER_ON);
+ ret = pm_ops->core_power(core, POWER_ON);
if (ret)
return ret;
}
@@ -625,12 +649,66 @@ static const struct venus_resources sc7180_res = {
.fwname = "qcom/venus-5.4/venus.mdt",
};
+static const struct freq_tbl sm8250_freq_table[] = {
+ { 0, 444000000 },
+ { 0, 366000000 },
+ { 0, 338000000 },
+ { 0, 240000000 },
+};
+
+static const struct bw_tbl sm8250_bw_table_enc[] = {
+ { 1944000, 1954000, 0, 3711000, 0 }, /* 3840x2160@60 */
+ { 972000, 996000, 0, 1905000, 0 }, /* 3840x2160@30 */
+ { 489600, 645000, 0, 977000, 0 }, /* 1920x1080@60 */
+ { 244800, 332000, 0, 498000, 0 }, /* 1920x1080@30 */
+};
+
+static const struct bw_tbl sm8250_bw_table_dec[] = {
+ { 2073600, 2403000, 0, 4113000, 0 }, /* 4096x2160@60 */
+ { 1036800, 1224000, 0, 2079000, 0 }, /* 4096x2160@30 */
+ { 489600, 812000, 0, 998000, 0 }, /* 1920x1080@60 */
+ { 244800, 416000, 0, 509000, 0 }, /* 1920x1080@30 */
+};
+
+static const struct reg_val sm8250_reg_preset[] = {
+ { 0xb0088, 0 },
+};
+
+static const struct venus_resources sm8250_res = {
+ .freq_tbl = sm8250_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(sm8250_freq_table),
+ .reg_tbl = sm8250_reg_preset,
+ .reg_tbl_size = ARRAY_SIZE(sm8250_reg_preset),
+ .bw_tbl_enc = sm8250_bw_table_enc,
+ .bw_tbl_enc_size = ARRAY_SIZE(sm8250_bw_table_enc),
+ .bw_tbl_dec = sm8250_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
+ .clks = {"core", "iface"},
+ .clks_num = 2,
+ .resets = { "bus", "core" },
+ .resets_num = 2,
+ .vcodec0_clks = { "vcodec0_core" },
+ .vcodec_clks_num = 1,
+ .vcodec_pmdomains = { "venus", "vcodec0" },
+ .vcodec_pmdomains_num = 2,
+ .opp_pmdomain = (const char *[]) { "mx", NULL },
+ .vcodec_num = 1,
+ .max_load = 7833600,
+ .hfi_version = HFI_VERSION_6XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu-1.0/venus.mdt",
+};
+
static const struct of_device_id venus_dt_match[] = {
{ .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
{ .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
{ .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
{ .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, },
{ .compatible = "qcom,sc7180-venus", .data = &sc7180_res, },
+ { .compatible = "qcom,sm8250-venus", .data = &sm8250_res, },
{ }
};
MODULE_DEVICE_TABLE(of, venus_dt_match);
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index a252ed32cc14..745f226a523f 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -24,6 +24,7 @@
#define VIDC_CLKS_NUM_MAX 4
#define VIDC_VCODEC_CLKS_NUM_MAX 2
#define VIDC_PMDOMAINS_NUM_MAX 3
+#define VIDC_RESETS_NUM_MAX 2
extern int venus_fw_debug;
@@ -64,6 +65,8 @@ struct venus_resources {
unsigned int vcodec_pmdomains_num;
const char **opp_pmdomain;
unsigned int vcodec_num;
+ const char * const resets[VIDC_RESETS_NUM_MAX];
+ unsigned int resets_num;
enum hfi_version hfi_version;
u32 max_load;
unsigned int vmem_id;
@@ -87,11 +90,25 @@ struct venus_format {
* struct venus_core - holds core parameters valid for all instances
*
* @base: IO memory base address
+ * @vbif_base: IO memory vbif base address
+ * @cpu_base: IO memory cpu base address
+ * @cpu_cs_base: IO memory cpu_cs base address
+ * @cpu_ic_base: IO memory cpu_ic base address
+ * @wrapper_base: IO memory wrapper base address
+ * @wrapper_tz_base: IO memory wrapper TZ base address
+ * @aon_base: AON base address
* @irq: Venus irq
* @clks: an array of struct clk pointers
* @vcodec0_clks: an array of vcodec0 struct clk pointers
* @vcodec1_clks: an array of vcodec1 struct clk pointers
+ * @video_path: an interconnect handle to video to/from memory path
+ * @cpucfg_path: an interconnect handle to cpu configuration path
+ * @opp_table: an device OPP table handle
+ * @has_opp_table: does OPP table exist
* @pmdomains: an array of pmdomains struct device pointers
+ * @opp_dl_venus: an device-link for device OPP
+ * @opp_pmdomain: an OPP power-domain
+ * @resets: an array of reset signals
* @vdev_dec: a reference to video device structure for decoder instances
* @vdev_enc: a reference to video device structure for encoder instances
* @v4l2_dev: a holder for v4l2 device structure
@@ -100,6 +117,7 @@ struct venus_format {
* @dev_dec: convenience struct device pointer for decoder device
* @dev_enc: convenience struct device pointer for encoder device
* @use_tz: a flag that suggests presence of trustzone
+ * @fw: structure of firmware parameters
* @lock: a lock for this strucure
* @instances: a list_head of all instances
* @insts_count: num of instances
@@ -108,6 +126,7 @@ struct venus_format {
* @error: an error returned during last HFI sync operations
* @sys_error: an error flag that signal system error event
* @core_ops: the core operations
+ * @pm_ops: a pointer to pm operations
* @pm_lock: a lock for PM operations
* @enc_codecs: encoders supported by this core
* @dec_codecs: decoders supported by this core
@@ -115,10 +134,21 @@ struct venus_format {
* @priv: a private filed for HFI operations
* @ops: the core HFI operations
* @work: a delayed work for handling system fatal error
+ * @caps: an array of supported HFI capabilities
+ * @codecs_count: platform codecs count
+ * @core0_usage_count: usage counter for core0
+ * @core1_usage_count: usage counter for core1
* @root: debugfs root directory
*/
struct venus_core {
void __iomem *base;
+ void __iomem *vbif_base;
+ void __iomem *cpu_base;
+ void __iomem *cpu_cs_base;
+ void __iomem *cpu_ic_base;
+ void __iomem *wrapper_base;
+ void __iomem *wrapper_tz_base;
+ void __iomem *aon_base;
int irq;
struct clk *clks[VIDC_CLKS_NUM_MAX];
struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
@@ -130,6 +160,7 @@ struct venus_core {
struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX];
struct device_link *opp_dl_venus;
struct device *opp_pmdomain;
+ struct reset_control *resets[VIDC_RESETS_NUM_MAX];
struct video_device *vdev_dec;
struct video_device *vdev_enc;
struct v4l2_device v4l2_dev;
@@ -172,6 +203,9 @@ struct vdec_controls {
u32 post_loop_deb_mode;
u32 profile;
u32 level;
+ u32 display_delay;
+ u32 display_delay_enable;
+ u64 conceal_color;
};
struct venc_controls {
@@ -222,6 +256,7 @@ struct venc_controls {
u32 multi_slice_max_mb;
u32 header_mode;
+ bool aud_enable;
struct {
u32 h264;
@@ -238,6 +273,9 @@ struct venc_controls {
} level;
u32 base_priority_id;
+ u32 ltr_count;
+ struct v4l2_ctrl_hdr10_cll_info cll;
+ struct v4l2_ctrl_hdr10_mastering_display mastering;
};
struct venus_buffer {
@@ -284,10 +322,11 @@ struct venus_ts_metadata {
* @list: used for attach an instance to the core
* @lock: instance lock
* @core: a reference to the core struct
+ * @clk_data: clock data per core ID
* @dpbbufs: a list of decoded picture buffers
* @internalbufs: a list of internal bufferes
* @registeredbufs: a list of registered capture bufferes
- * @delayed_process a list of delayed buffers
+ * @delayed_process: a list of delayed buffers
* @delayed_process_work: a work_struct for process delayed buffers
* @ctrl_handler: v4l control handler
* @controls: a union of decoder and encoder control parameters
@@ -296,22 +335,26 @@ struct venus_ts_metadata {
* @streamon_out: stream on flag for output queue
* @width: current capture width
* @height: current capture height
+ * @crop: current crop rectangle
* @out_width: current output width
* @out_height: current output height
* @colorspace: current color space
+ * @ycbcr_enc: current YCbCr encoding
* @quantization: current quantization
* @xfer_func: current xfer function
* @codec_state: current codec API state (see DEC/ENC_STATE_)
* @reconf_wait: wait queue for resolution change event
* @subscriptions: used to hold current events subscriptions
* @buf_count: used to count number of buffers (reqbuf(0))
+ * @tss: timestamp metadata
+ * @payloads: cache plane payload to use it for clock/BW scaling
* @fps: holds current FPS
* @timeperframe: holds current time per frame structure
* @fmt_out: a reference to output format structure
* @fmt_cap: a reference to capture format structure
* @num_input_bufs: holds number of input buffers
* @num_output_bufs: holds number of output buffers
- * @input_buf_size holds input buffer size
+ * @input_buf_size: holds input buffer size
* @output_buf_size: holds output buffer size
* @output2_buf_size: holds secondary decoder output buffer size
* @dpb_buftype: decoded picture buffer type
@@ -332,7 +375,11 @@ struct venus_ts_metadata {
* @priv: a private for HFI operations callbacks
* @session_type: the type of the session (decoder or encoder)
* @hprop: a union used as a holder by get property
+ * @core_acquired: the Core has been acquired
+ * @bit_depth: current bitstream bit-depth
+ * @pic_struct: bitstream progressive vs interlaced
* @next_buf_last: a flag to mark next queued capture buffer as last
+ * @drain_active: Drain sequence is in progress
*/
struct venus_inst {
struct list_head list;
@@ -403,6 +450,7 @@ struct venus_inst {
#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
#define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX)
#define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX)
+#define IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX)
#define ctrl_to_inst(ctrl) \
container_of((ctrl)->handler, struct venus_inst, ctrl_handler)
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 89defc21ea81..227bd3b3f84c 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -27,19 +27,19 @@
static void venus_reset_cpu(struct venus_core *core)
{
u32 fw_size = core->fw.mapped_mem_size;
- void __iomem *base = core->base;
+ void __iomem *wrapper_base = core->wrapper_base;
- writel(0, base + WRAPPER_FW_START_ADDR);
- writel(fw_size, base + WRAPPER_FW_END_ADDR);
- writel(0, base + WRAPPER_CPA_START_ADDR);
- writel(fw_size, base + WRAPPER_CPA_END_ADDR);
- writel(fw_size, base + WRAPPER_NONPIX_START_ADDR);
- writel(fw_size, base + WRAPPER_NONPIX_END_ADDR);
- writel(0x0, base + WRAPPER_CPU_CGC_DIS);
- writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG);
+ writel(0, wrapper_base + WRAPPER_FW_START_ADDR);
+ writel(fw_size, wrapper_base + WRAPPER_FW_END_ADDR);
+ writel(0, wrapper_base + WRAPPER_CPA_START_ADDR);
+ writel(fw_size, wrapper_base + WRAPPER_CPA_END_ADDR);
+ writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR);
+ writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR);
+ writel(0x0, wrapper_base + WRAPPER_CPU_CGC_DIS);
+ writel(0x0, wrapper_base + WRAPPER_CPU_CLOCK_CONFIG);
/* Bring ARM9 out of reset */
- writel(0, base + WRAPPER_A9SS_SW_RESET);
+ writel(0, wrapper_base + WRAPPER_A9SS_SW_RESET);
}
int venus_set_hw_state(struct venus_core *core, bool resume)
@@ -53,10 +53,12 @@ int venus_set_hw_state(struct venus_core *core, bool resume)
return ret;
}
- if (resume)
+ if (resume) {
venus_reset_cpu(core);
- else
- writel(1, core->base + WRAPPER_A9SS_SW_RESET);
+ } else {
+ if (!IS_V6(core))
+ writel(1, core->wrapper_base + WRAPPER_A9SS_SW_RESET);
+ }
return 0;
}
@@ -159,12 +161,12 @@ static int venus_shutdown_no_tz(struct venus_core *core)
size_t unmapped;
u32 reg;
struct device *dev = core->fw.dev;
- void __iomem *base = core->base;
+ void __iomem *wrapper_base = core->wrapper_base;
/* Assert the reset to ARM9 */
- reg = readl_relaxed(base + WRAPPER_A9SS_SW_RESET);
+ reg = readl_relaxed(wrapper_base + WRAPPER_A9SS_SW_RESET);
reg |= WRAPPER_A9SS_SW_RESET_BIT;
- writel_relaxed(reg, base + WRAPPER_A9SS_SW_RESET);
+ writel_relaxed(reg, wrapper_base + WRAPPER_A9SS_SW_RESET);
/* Make sure reset is asserted before the mapping is removed */
mb();
@@ -187,6 +189,7 @@ int venus_boot(struct venus_core *core)
{
struct device *dev = core->dev;
const struct venus_resources *res = core->res;
+ const char *fwpath = NULL;
phys_addr_t mem_phys;
size_t mem_size;
int ret;
@@ -195,7 +198,12 @@ int venus_boot(struct venus_core *core)
(core->use_tz && !qcom_scm_is_available()))
return -EPROBE_DEFER;
- ret = venus_load_fw(core, core->res->fwname, &mem_phys, &mem_size);
+ ret = of_property_read_string_index(dev->of_node, "firmware-name", 0,
+ &fwpath);
+ if (ret)
+ fwpath = core->res->fwname;
+
+ ret = venus_load_fw(core, fwpath, &mem_phys, &mem_size);
if (ret) {
dev_err(dev, "fail to load video firmware\n");
return -EINVAL;
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 76ece2ff8d39..b813d6dba481 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -18,6 +18,9 @@
#include "hfi_platform.h"
#include "hfi_parser.h"
+#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4))
+#define NUM_MBS_4K (((4096 + 15) >> 4) * ((2304 + 15) >> 4))
+
struct intbuf {
struct list_head list;
u32 type;
@@ -279,13 +282,24 @@ static const unsigned int intbuf_types_4xx[] = {
HFI_BUFFER_INTERNAL_PERSIST_1,
};
+static const unsigned int intbuf_types_6xx[] = {
+ HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_6XX),
+ HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_6XX),
+ HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_6XX),
+ HFI_BUFFER_INTERNAL_PERSIST,
+ HFI_BUFFER_INTERNAL_PERSIST_1,
+};
+
int venus_helper_intbufs_alloc(struct venus_inst *inst)
{
const unsigned int *intbuf;
size_t arr_sz, i;
int ret;
- if (IS_V4(inst->core)) {
+ if (IS_V6(inst->core)) {
+ arr_sz = ARRAY_SIZE(intbuf_types_6xx);
+ intbuf = intbuf_types_6xx;
+ } else if (IS_V4(inst->core)) {
arr_sz = ARRAY_SIZE(intbuf_types_4xx);
intbuf = intbuf_types_4xx;
} else {
@@ -488,7 +502,7 @@ static bool is_dynamic_bufmode(struct venus_inst *inst)
* v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports
* dynamic buffer mode by default for HFI_BUFFER_OUTPUT/OUTPUT2.
*/
- if (IS_V4(core))
+ if (IS_V4(core) || IS_V6(core))
return true;
caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type);
@@ -1079,20 +1093,67 @@ int venus_helper_set_output_resolution(struct venus_inst *inst,
}
EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution);
-int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode)
+static u32 venus_helper_get_work_mode(struct venus_inst *inst)
+{
+ u32 mode;
+ u32 num_mbs;
+
+ mode = VIDC_WORK_MODE_2;
+ if (inst->session_type == VIDC_SESSION_TYPE_DEC) {
+ num_mbs = (ALIGN(inst->height, 16) * ALIGN(inst->width, 16)) / 256;
+ if (inst->hfi_codec == HFI_VIDEO_CODEC_MPEG2 ||
+ inst->pic_struct != HFI_INTERLACE_FRAME_PROGRESSIVE ||
+ num_mbs <= NUM_MBS_720P)
+ mode = VIDC_WORK_MODE_1;
+ } else {
+ num_mbs = (ALIGN(inst->out_height, 16) * ALIGN(inst->out_width, 16)) / 256;
+ if (inst->hfi_codec == HFI_VIDEO_CODEC_VP8 &&
+ num_mbs <= NUM_MBS_4K)
+ mode = VIDC_WORK_MODE_1;
+ }
+
+ return mode;
+}
+
+int venus_helper_set_work_mode(struct venus_inst *inst)
{
const u32 ptype = HFI_PROPERTY_PARAM_WORK_MODE;
struct hfi_video_work_mode wm;
+ u32 mode;
- if (!IS_V4(inst->core))
+ if (!IS_V4(inst->core) && !IS_V6(inst->core))
return 0;
+ mode = venus_helper_get_work_mode(inst);
wm.video_work_mode = mode;
-
return hfi_session_set_property(inst, ptype, &wm);
}
EXPORT_SYMBOL_GPL(venus_helper_set_work_mode);
+int venus_helper_set_format_constraints(struct venus_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO;
+ struct hfi_uncompressed_plane_actual_constraints_info pconstraint;
+
+ if (!IS_V6(inst->core))
+ return 0;
+
+ pconstraint.buffer_type = HFI_BUFFER_OUTPUT2;
+ pconstraint.num_planes = 2;
+ pconstraint.plane_format[0].stride_multiples = 128;
+ pconstraint.plane_format[0].max_stride = 8192;
+ pconstraint.plane_format[0].min_plane_buffer_height_multiple = 32;
+ pconstraint.plane_format[0].buffer_alignment = 256;
+
+ pconstraint.plane_format[1].stride_multiples = 128;
+ pconstraint.plane_format[1].max_stride = 8192;
+ pconstraint.plane_format[1].min_plane_buffer_height_multiple = 16;
+ pconstraint.plane_format[1].buffer_alignment = 256;
+
+ return hfi_session_set_property(inst, ptype, &pconstraint);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_format_constraints);
+
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
unsigned int output_bufs,
unsigned int output2_bufs)
diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h
index 351093845499..e6269b4be3af 100644
--- a/drivers/media/platform/qcom/venus/helpers.h
+++ b/drivers/media/platform/qcom/venus/helpers.h
@@ -32,7 +32,8 @@ int venus_helper_set_input_resolution(struct venus_inst *inst,
int venus_helper_set_output_resolution(struct venus_inst *inst,
unsigned int width, unsigned int height,
u32 buftype);
-int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode);
+int venus_helper_set_work_mode(struct venus_inst *inst);
+int venus_helper_set_format_constraints(struct venus_inst *inst);
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
unsigned int output_bufs,
unsigned int output2_bufs);
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
index 4f7565834469..11a8347e5f5c 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.c
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -254,7 +254,7 @@ int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt,
int pkt_session_etb_decoder(struct hfi_session_empty_buffer_compressed_pkt *pkt,
void *cookie, struct hfi_frame_data *in_frame)
{
- if (!cookie || !in_frame->device_addr)
+ if (!cookie)
return -EINVAL;
pkt->shdr.hdr.size = sizeof(*pkt);
@@ -760,7 +760,9 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt,
struct hfi_conceal_color *color = prop_data;
u32 *in = pdata;
- color->conceal_color = *in;
+ color->conceal_color = *in & 0xff;
+ color->conceal_color |= ((*in >> 10) & 0xff) << 8;
+ color->conceal_color |= ((*in >> 20) & 0xff) << 16;
pkt->shdr.hdr.size += sizeof(u32) + sizeof(*color);
break;
}
@@ -1039,6 +1041,18 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt,
pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hierp);
break;
}
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: {
+ struct hfi_uncompressed_plane_actual_info *in = pdata;
+ struct hfi_uncompressed_plane_actual_info *info = prop_data;
+
+ info->buffer_type = in->buffer_type;
+ info->num_planes = in->num_planes;
+ info->plane_format[0] = in->plane_format[0];
+ if (in->num_planes > 1)
+ info->plane_format[1] = in->plane_format[1];
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ break;
+ }
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
@@ -1205,18 +1219,14 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt,
pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu);
break;
}
- case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: {
- struct hfi_uncompressed_plane_actual_info *in = pdata;
- struct hfi_uncompressed_plane_actual_info *info = prop_data;
+ case HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI: {
+ struct hfi_hdr10_pq_sei *in = pdata, *hdr10 = prop_data;
- info->buffer_type = in->buffer_type;
- info->num_planes = in->num_planes;
- info->plane_format[0] = in->plane_format[0];
- if (in->num_planes > 1)
- info->plane_format[1] = in->plane_format[1];
- pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ memcpy(hdr10, in, sizeof(*hdr10));
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hdr10);
break;
}
+
case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE:
case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE:
@@ -1249,13 +1259,38 @@ pkt_session_set_property_6xx(struct hfi_session_set_property_pkt *pkt,
pkt->data[0] = ptype;
switch (ptype) {
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: {
+ struct hfi_uncompressed_plane_actual_constraints_info *in = pdata;
+ struct hfi_uncompressed_plane_actual_constraints_info *info = prop_data;
+
+ info->buffer_type = in->buffer_type;
+ info->num_planes = in->num_planes;
+ info->plane_format[0] = in->plane_format[0];
+ if (in->num_planes > 1)
+ info->plane_format[1] = in->plane_format[1];
+
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ break;
+ }
case HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY: {
struct hfi_heic_frame_quality *in = pdata, *cq = prop_data;
cq->frame_quality = in->frame_quality;
pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cq);
break;
- } default:
+ }
+ case HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR: {
+ struct hfi_conceal_color_v4 *color = prop_data;
+ u32 *in = pdata;
+
+ color->conceal_color_8bit = *in & 0xff;
+ color->conceal_color_8bit |= ((*in >> 10) & 0xff) << 8;
+ color->conceal_color_8bit |= ((*in >> 20) & 0xff) << 16;
+ color->conceal_color_10bit = *in;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*color);
+ break;
+ }
+ default:
return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata);
}
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index 6b524c7cde5f..63cd347a62da 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -395,11 +395,14 @@
#define HFI_BUFFER_INTERNAL_PERSIST 0x4
#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5
#define HFI_BUFFER_INTERNAL_SCRATCH(ver) \
- (((ver) == HFI_VERSION_4XX) ? 0x6 : 0x1000001)
+ (((ver) == HFI_VERSION_4XX || \
+ (ver) == HFI_VERSION_6XX) ? 0x6 : 0x1000001)
#define HFI_BUFFER_INTERNAL_SCRATCH_1(ver) \
- (((ver) == HFI_VERSION_4XX) ? 0x7 : 0x1000005)
+ (((ver) == HFI_VERSION_4XX || \
+ (ver) == HFI_VERSION_6XX) ? 0x7 : 0x1000005)
#define HFI_BUFFER_INTERNAL_SCRATCH_2(ver) \
- (((ver) == HFI_VERSION_4XX) ? 0x8 : 0x1000006)
+ (((ver) == HFI_VERSION_4XX || \
+ (ver) == HFI_VERSION_6XX) ? 0x8 : 0x1000006)
#define HFI_BUFFER_EXTRADATA_INPUT(ver) \
(((ver) == HFI_VERSION_4XX) ? 0xc : 0x1000002)
#define HFI_BUFFER_EXTRADATA_OUTPUT(ver) \
@@ -513,6 +516,7 @@
#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE 0x2005029
#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER 0x200502c
#define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE 0x200502f
+#define HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI 0x2005036
/*
* HFI_PROPERTY_CONFIG_VENC_COMMON_START
@@ -685,10 +689,20 @@ struct hfi_vc1e_perf_cfg_type {
u32 search_range_y_subsampled[3];
};
+/*
+ * 0 - 7bit -> Luma (def: 16)
+ * 8 - 15bit -> Chroma (def: 128)
+ * format is valid up to v4
+ */
struct hfi_conceal_color {
u32 conceal_color;
};
+struct hfi_conceal_color_v4 {
+ u32 conceal_color_8bit;
+ u32 conceal_color_10bit;
+};
+
struct hfi_intra_period {
u32 pframes;
u32 bframes;
@@ -809,6 +823,25 @@ struct hfi_ltr_mark {
u32 mark_frame;
};
+struct hfi_mastering_display_colour_sei_payload {
+ u32 display_primaries_x[3];
+ u32 display_primaries_y[3];
+ u32 white_point_x;
+ u32 white_point_y;
+ u32 max_display_mastering_luminance;
+ u32 min_display_mastering_luminance;
+};
+
+struct hfi_content_light_level_sei_payload {
+ u32 max_content_light;
+ u32 max_pic_average_light;
+};
+
+struct hfi_hdr10_pq_sei {
+ struct hfi_mastering_display_colour_sei_payload mastering;
+ struct hfi_content_light_level_sei_payload cll;
+};
+
struct hfi_framesize {
u32 buffer_type;
u32 width;
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index 06a1908ca225..a2d436d407b2 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -6,6 +6,7 @@
#include <linux/hash.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/soc/qcom/smem.h>
#include <media/videobuf2-v4l2.h>
#include "core.h"
@@ -14,6 +15,10 @@
#include "hfi_msgs.h"
#include "hfi_parser.h"
+#define SMEM_IMG_VER_TBL 469
+#define VER_STR_SZ 128
+#define SMEM_IMG_OFFSET_VENUS (14 * 128)
+
static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
@@ -239,15 +244,26 @@ static void
sys_get_prop_image_version(struct device *dev,
struct hfi_msg_sys_property_info_pkt *pkt)
{
+ u8 *smem_tbl_ptr;
+ u8 *img_ver;
int req_bytes;
+ size_t smem_blk_sz;
req_bytes = pkt->hdr.size - sizeof(*pkt);
- if (req_bytes < 128 || !pkt->data[1] || pkt->num_properties > 1)
+ if (req_bytes < VER_STR_SZ || !pkt->data[1] || pkt->num_properties > 1)
/* bad packet */
return;
- dev_dbg(dev, VDBGL "F/W version: %s\n", (u8 *)&pkt->data[1]);
+ img_ver = (u8 *)&pkt->data[1];
+
+ dev_dbg(dev, VDBGL "F/W version: %s\n", img_ver);
+
+ smem_tbl_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY,
+ SMEM_IMG_VER_TBL, &smem_blk_sz);
+ if (smem_tbl_ptr && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ)
+ memcpy(smem_tbl_ptr + SMEM_IMG_OFFSET_VENUS,
+ img_ver, VER_STR_SZ);
}
static void hfi_sys_property_info(struct venus_core *core,
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index 7263c0c32695..5b8389b98299 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -235,13 +235,13 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst)
u32 enc_codecs, dec_codecs, count = 0;
unsigned int entries;
- if (inst)
- return 0;
-
plat = hfi_platform_get(core->res->hfi_version);
if (!plat)
return -EINVAL;
+ if (inst)
+ return 0;
+
if (plat->codecs)
plat->codecs(&enc_codecs, &dec_codecs, &count);
@@ -277,8 +277,10 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
parser_init(inst, &codecs, &domain);
- core->codecs_count = 0;
- memset(core->caps, 0, sizeof(core->caps));
+ if (core->res->hfi_version > HFI_VERSION_1XX) {
+ core->codecs_count = 0;
+ memset(core->caps, 0, sizeof(core->caps));
+ }
while (words_count) {
data = word + 1;
diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
index d43d1a53e72d..479178b0600d 100644
--- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
+++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
@@ -40,7 +40,8 @@
#define MAX_TILE_COLUMNS 32 /* 8K/256 */
-#define NUM_HW_PIC_BUF 10
+#define VPP_CMD_MAX_SIZE BIT(20)
+#define NUM_HW_PIC_BUF 32
#define BIN_BUFFER_THRESHOLD (1280 * 736)
#define H264D_MAX_SLICE 1800
/* sizeof(h264d_buftab_t) aligned to 256 */
@@ -90,6 +91,7 @@
#define SIZE_SLIST_BUF_H264 512
#define LCU_MAX_SIZE_PELS 64
#define LCU_MIN_SIZE_PELS 16
+#define SIZE_SEI_USERDATA 4096
#define H265D_MAX_SLICE 600
#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T
@@ -199,7 +201,7 @@ static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height)
#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN 2
#define VP8_NUM_FRAME_INFO_BUF (5 + 1)
-#define VP9_NUM_FRAME_INFO_BUF (8 + 2 + 1 + 8)
+#define VP9_NUM_FRAME_INFO_BUF 32
#define VP8_NUM_PROBABILITY_TABLE_BUF VP8_NUM_FRAME_INFO_BUF
#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4)
#define VP8_PROB_TABLE_SIZE 3840
@@ -211,7 +213,7 @@ static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height)
#define QMATRIX_SIZE (sizeof(u32) * 128 + 256)
#define MP2D_QPDUMP_SIZE 115200
-#define HFI_IRIS2_ENC_PERSIST_SIZE 102400
+#define HFI_IRIS2_ENC_PERSIST_SIZE 204800
#define HFI_MAX_COL_FRAME 6
#define HFI_VENUS_VENC_TRE_WB_BUFF_SIZE (65 << 4) /* in Bytes */
#define HFI_VENUS_VENC_DB_LINE_BUFF_PER_MB 512
@@ -467,7 +469,7 @@ static u32 hfi_iris2_h264d_comv_size(u32 width, u32 height,
{
u32 frame_width_in_mbs = ((width + 15) >> 4);
u32 frame_height_in_mbs = ((height + 15) >> 4);
- u32 col_mv_aligned_width = (frame_width_in_mbs << 6);
+ u32 col_mv_aligned_width = (frame_width_in_mbs << 7);
u32 col_zero_aligned_width = (frame_width_in_mbs << 2);
u32 col_zero_size = 0, size_colloc = 0, comv_size = 0;
@@ -500,9 +502,14 @@ static u32 size_h264d_bse_cmd_buf(u32 height)
static u32 size_h264d_vpp_cmd_buf(u32 height)
{
u32 aligned_height = ALIGN(height, 32);
+ u32 size;
- return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4),
+ size = min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4),
H264D_MAX_SLICE) * SIZE_H264D_VPP_CMD_PER_BUF;
+ if (size > VPP_CMD_MAX_SIZE)
+ size = VPP_CMD_MAX_SIZE;
+
+ return size;
}
static u32 hfi_iris2_h264d_non_comv_size(u32 width, u32 height,
@@ -559,8 +566,11 @@ static u32 size_h265d_vpp_cmd_buf(u32 width, u32 height)
size = min_t(u32, size, H265D_MAX_SLICE + 1);
size = ALIGN(size, 4);
size = 2 * size * SIZE_H265D_VPP_CMD_PER_BUF;
+ size = ALIGN(size, HFI_DMA_ALIGNMENT);
+ if (size > VPP_CMD_MAX_SIZE)
+ size = VPP_CMD_MAX_SIZE;
- return ALIGN(size, HFI_DMA_ALIGNMENT);
+ return size;
}
static u32 hfi_iris2_h265d_comv_size(u32 width, u32 height,
@@ -1004,8 +1014,8 @@ static u32 enc_persist_size(void)
static u32 h264d_persist1_size(void)
{
- return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264),
- HFI_DMA_ALIGNMENT);
+ return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264
+ + NUM_HW_PIC_BUF * SIZE_SEI_USERDATA), HFI_DMA_ALIGNMENT);
}
static u32 h265d_persist1_size(void)
@@ -1159,7 +1169,7 @@ static int output_buffer_count(u32 session_type, u32 codec)
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_HEVC:
default:
- output_min_count = 8;
+ output_min_count = 18;
break;
}
} else {
@@ -1233,7 +1243,7 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype,
} else if (buftype == HFI_BUFFER_INTERNAL_PERSIST_1) {
bufreq->size = dec_ops->persist1();
} else {
- return -EINVAL;
+ bufreq->size = 0;
}
return 0;
@@ -1301,7 +1311,7 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype,
} else if (buftype == HFI_BUFFER_INTERNAL_PERSIST) {
bufreq->size = enc_ops->persist();
} else {
- return -EINVAL;
+ bufreq->size = 0;
}
return 0;
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
index 2278be13cb90..dd1a03911b6c 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
@@ -9,15 +9,15 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_H264,
.domain = VIDC_SESSION_TYPE_DEC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 5760, 1},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 5760, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1},
/* ((5760 * 2880) / 256) */
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 36, 64800, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 200000000, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1},
.caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 36, 1958400, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1},
.caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.num_caps = 9,
.pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52},
@@ -35,15 +35,15 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_HEVC,
.domain = VIDC_SESSION_TYPE_DEC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
- .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
- .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
.num_caps = 10,
.pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
@@ -61,15 +61,15 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP8,
.domain = VIDC_SESSION_TYPE_DEC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
- .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
- .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 100000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 4423680, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 120, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
.num_caps = 10,
.pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0},
@@ -86,15 +86,15 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP9,
.domain = VIDC_SESSION_TYPE_DEC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
- .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
- .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1},
.num_caps = 10,
.pl[0] = {HFI_VP9_PROFILE_P0, 200},
@@ -112,15 +112,15 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_MPEG2,
.domain = VIDC_SESSION_TYPE_DEC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 1920, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 1920, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 8160, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1},
- .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1},
- .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1},
+ .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1},
+ .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1},
.caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1},
.num_caps = 10,
.pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14},
@@ -135,21 +135,21 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_H264,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
.caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
- .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1},
- .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1},
+ .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 6, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1},
.caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1},
.caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
- .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
+ .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 6, 1},
.caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1},
.caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1},
.caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1},
@@ -172,24 +172,24 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_HEVC,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
.caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
.caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1},
- .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1},
+ .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1},
.caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1},
.caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
.caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
- .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1},
- .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1},
- .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1},
+ .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1},
+ .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1},
+ .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1},
.caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
.caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
.caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
@@ -209,20 +209,20 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP8,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
- .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
- .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16},
+ .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1},
+ .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
.caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1},
- .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1},
- .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1},
- .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1},
+ .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 4423680, 1},
+ .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 120, 1},
+ .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1},
.caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1},
.caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1},
.caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1},
.caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1},
- .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1},
+ .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 0, 1},
.caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1},
.caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1},
.caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index 50e03f8fc278..ce98c523b3c6 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -345,16 +345,6 @@ static void venus_free(struct venus_hfi_device *hdev, struct mem_desc *mem)
dma_free_attrs(dev, mem->size, mem->kva, mem->da, mem->attrs);
}
-static void venus_writel(struct venus_hfi_device *hdev, u32 reg, u32 value)
-{
- writel(value, hdev->core->base + reg);
-}
-
-static u32 venus_readl(struct venus_hfi_device *hdev, u32 reg)
-{
- return readl(hdev->core->base + reg);
-}
-
static void venus_set_registers(struct venus_hfi_device *hdev)
{
const struct venus_resources *res = hdev->core->res;
@@ -363,12 +353,20 @@ static void venus_set_registers(struct venus_hfi_device *hdev)
unsigned int i;
for (i = 0; i < count; i++)
- venus_writel(hdev, tbl[i].reg, tbl[i].value);
+ writel(tbl[i].value, hdev->core->base + tbl[i].reg);
}
static void venus_soft_int(struct venus_hfi_device *hdev)
{
- venus_writel(hdev, CPU_IC_SOFTINT, BIT(CPU_IC_SOFTINT_H2A_SHIFT));
+ void __iomem *cpu_ic_base = hdev->core->cpu_ic_base;
+ u32 clear_bit;
+
+ if (IS_V6(hdev->core))
+ clear_bit = BIT(CPU_IC_SOFTINT_H2A_SHIFT_V6);
+ else
+ clear_bit = BIT(CPU_IC_SOFTINT_H2A_SHIFT);
+
+ writel(clear_bit, cpu_ic_base + CPU_IC_SOFTINT);
}
static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev,
@@ -450,16 +448,25 @@ static int venus_boot_core(struct venus_hfi_device *hdev)
{
struct device *dev = hdev->core->dev;
static const unsigned int max_tries = 100;
- u32 ctrl_status = 0;
+ u32 ctrl_status = 0, mask_val;
unsigned int count = 0;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
+ void __iomem *wrapper_base = hdev->core->wrapper_base;
int ret = 0;
- venus_writel(hdev, VIDC_CTRL_INIT, BIT(VIDC_CTRL_INIT_CTRL_SHIFT));
- venus_writel(hdev, WRAPPER_INTR_MASK, WRAPPER_INTR_MASK_A2HVCODEC_MASK);
- venus_writel(hdev, CPU_CS_SCIACMDARG3, 1);
+ writel(BIT(VIDC_CTRL_INIT_CTRL_SHIFT), cpu_cs_base + VIDC_CTRL_INIT);
+ if (IS_V6(hdev->core)) {
+ mask_val = readl(wrapper_base + WRAPPER_INTR_MASK);
+ mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BASK_V6 |
+ WRAPPER_INTR_MASK_A2HCPU_MASK);
+ } else {
+ mask_val = WRAPPER_INTR_MASK_A2HVCODEC_MASK;
+ }
+ writel(mask_val, wrapper_base + WRAPPER_INTR_MASK);
+ writel(1, cpu_cs_base + CPU_CS_SCIACMDARG3);
while (!ctrl_status && count < max_tries) {
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0);
if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) {
dev_err(dev, "invalid setting for UC_REGION\n");
ret = -EINVAL;
@@ -473,15 +480,22 @@ static int venus_boot_core(struct venus_hfi_device *hdev)
if (count >= max_tries)
ret = -ETIMEDOUT;
+ if (IS_V6(hdev->core)) {
+ writel(0x1, cpu_cs_base + CPU_CS_H2XSOFTINTEN_V6);
+ writel(0x0, cpu_cs_base + CPU_CS_X2RPMH_V6);
+ }
+
return ret;
}
static u32 venus_hwversion(struct venus_hfi_device *hdev)
{
struct device *dev = hdev->core->dev;
- u32 ver = venus_readl(hdev, WRAPPER_HW_VERSION);
+ void __iomem *wrapper_base = hdev->core->wrapper_base;
+ u32 ver;
u32 major, minor, step;
+ ver = readl(wrapper_base + WRAPPER_HW_VERSION);
major = ver & WRAPPER_HW_VERSION_MAJOR_VERSION_MASK;
major = major >> WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT;
minor = ver & WRAPPER_HW_VERSION_MINOR_VERSION_MASK;
@@ -496,6 +510,7 @@ static u32 venus_hwversion(struct venus_hfi_device *hdev)
static int venus_run(struct venus_hfi_device *hdev)
{
struct device *dev = hdev->core->dev;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
int ret;
/*
@@ -504,12 +519,12 @@ static int venus_run(struct venus_hfi_device *hdev)
*/
venus_set_registers(hdev);
- venus_writel(hdev, UC_REGION_ADDR, hdev->ifaceq_table.da);
- venus_writel(hdev, UC_REGION_SIZE, SHARED_QSIZE);
- venus_writel(hdev, CPU_CS_SCIACMDARG2, hdev->ifaceq_table.da);
- venus_writel(hdev, CPU_CS_SCIACMDARG1, 0x01);
+ writel(hdev->ifaceq_table.da, cpu_cs_base + UC_REGION_ADDR);
+ writel(SHARED_QSIZE, cpu_cs_base + UC_REGION_SIZE);
+ writel(hdev->ifaceq_table.da, cpu_cs_base + CPU_CS_SCIACMDARG2);
+ writel(0x01, cpu_cs_base + CPU_CS_SCIACMDARG1);
if (hdev->sfr.da)
- venus_writel(hdev, SFR_ADDR, hdev->sfr.da);
+ writel(hdev->sfr.da, cpu_cs_base + SFR_ADDR);
ret = venus_boot_core(hdev);
if (ret) {
@@ -524,17 +539,50 @@ static int venus_run(struct venus_hfi_device *hdev)
static int venus_halt_axi(struct venus_hfi_device *hdev)
{
- void __iomem *base = hdev->core->base;
+ void __iomem *wrapper_base = hdev->core->wrapper_base;
+ void __iomem *vbif_base = hdev->core->vbif_base;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
+ void __iomem *aon_base = hdev->core->aon_base;
struct device *dev = hdev->core->dev;
u32 val;
+ u32 mask_val;
int ret;
+ if (IS_V6(hdev->core)) {
+ writel(0x3, cpu_cs_base + CPU_CS_X2RPMH_V6);
+
+ writel(0x1, aon_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+ ret = readl_poll_timeout(aon_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ val,
+ val & BIT(0),
+ POLL_INTERVAL_US,
+ VBIF_AXI_HALT_ACK_TIMEOUT_US);
+ if (ret)
+ return -ETIMEDOUT;
+
+ mask_val = (BIT(2) | BIT(1) | BIT(0));
+ writel(mask_val, wrapper_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_V6);
+
+ writel(0x00, wrapper_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_V6);
+ ret = readl_poll_timeout(wrapper_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS_V6,
+ val,
+ val == 0,
+ POLL_INTERVAL_US,
+ VBIF_AXI_HALT_ACK_TIMEOUT_US);
+
+ if (ret) {
+ dev_err(dev, "DBLP Release: lpi_status %x\n", val);
+ return -ETIMEDOUT;
+ }
+ return 0;
+ }
+
if (IS_V4(hdev->core)) {
- val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT);
+ val = readl(wrapper_base + WRAPPER_CPU_AXI_HALT);
val |= WRAPPER_CPU_AXI_HALT_HALT;
- venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val);
+ writel(val, wrapper_base + WRAPPER_CPU_AXI_HALT);
- ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS,
+ ret = readl_poll_timeout(wrapper_base + WRAPPER_CPU_AXI_HALT_STATUS,
val,
val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE,
POLL_INTERVAL_US,
@@ -548,12 +596,12 @@ static int venus_halt_axi(struct venus_hfi_device *hdev)
}
/* Halt AXI and AXI IMEM VBIF Access */
- val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0);
+ val = readl(vbif_base + VBIF_AXI_HALT_CTRL0);
val |= VBIF_AXI_HALT_CTRL0_HALT_REQ;
- venus_writel(hdev, VBIF_AXI_HALT_CTRL0, val);
+ writel(val, vbif_base + VBIF_AXI_HALT_CTRL0);
/* Request for AXI bus port halt */
- ret = readl_poll_timeout(base + VBIF_AXI_HALT_CTRL1, val,
+ ret = readl_poll_timeout(vbif_base + VBIF_AXI_HALT_CTRL1, val,
val & VBIF_AXI_HALT_CTRL1_HALT_ACK,
POLL_INTERVAL_US,
VBIF_AXI_HALT_ACK_TIMEOUT_US);
@@ -881,7 +929,7 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev)
* enable it explicitly in order to make suspend functional by checking
* WFI (wait-for-interrupt) bit.
*/
- if (IS_V4(hdev->core))
+ if (IS_V4(hdev->core) || IS_V6(hdev->core))
venus_sys_idle_indicator = true;
ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator);
@@ -1046,19 +1094,30 @@ static irqreturn_t venus_isr(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
u32 status;
+ void __iomem *cpu_cs_base;
+ void __iomem *wrapper_base;
if (!hdev)
return IRQ_NONE;
- status = venus_readl(hdev, WRAPPER_INTR_STATUS);
-
- if (status & WRAPPER_INTR_STATUS_A2H_MASK ||
- status & WRAPPER_INTR_STATUS_A2HWD_MASK ||
- status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
- hdev->irq_status = status;
+ cpu_cs_base = hdev->core->cpu_cs_base;
+ wrapper_base = hdev->core->wrapper_base;
- venus_writel(hdev, CPU_CS_A2HSOFTINTCLR, 1);
- venus_writel(hdev, WRAPPER_INTR_CLEAR, status);
+ status = readl(wrapper_base + WRAPPER_INTR_STATUS);
+ if (IS_V6(core)) {
+ if (status & WRAPPER_INTR_STATUS_A2H_MASK ||
+ status & WRAPPER_INTR_STATUS_A2HWD_MASK_V6 ||
+ status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
+ hdev->irq_status = status;
+ } else {
+ if (status & WRAPPER_INTR_STATUS_A2H_MASK ||
+ status & WRAPPER_INTR_STATUS_A2HWD_MASK ||
+ status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
+ hdev->irq_status = status;
+ }
+ writel(1, cpu_cs_base + CPU_CS_A2HSOFTINTCLR);
+ if (!IS_V6(core))
+ writel(status, wrapper_base + WRAPPER_INTR_CLEAR);
return IRQ_WAKE_THREAD;
}
@@ -1391,6 +1450,7 @@ static int venus_suspend_1xx(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
struct device *dev = core->dev;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
u32 ctrl_status;
int ret;
@@ -1425,7 +1485,7 @@ static int venus_suspend_1xx(struct venus_core *core)
return -EINVAL;
}
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0);
if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) {
mutex_unlock(&hdev->lock);
return -EINVAL;
@@ -1446,10 +1506,16 @@ static int venus_suspend_1xx(struct venus_core *core)
static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev)
{
+ void __iomem *wrapper_base = hdev->core->wrapper_base;
+ void __iomem *wrapper_tz_base = hdev->core->wrapper_tz_base;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
u32 ctrl_status, cpu_status;
- cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if (IS_V6(hdev->core))
+ cpu_status = readl(wrapper_tz_base + WRAPPER_TZ_CPU_STATUS_V6);
+ else
+ cpu_status = readl(wrapper_base + WRAPPER_CPU_STATUS);
+ ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0);
if (cpu_status & WRAPPER_CPU_STATUS_WFI &&
ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
@@ -1460,10 +1526,16 @@ static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev)
static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev)
{
+ void __iomem *wrapper_base = hdev->core->wrapper_base;
+ void __iomem *wrapper_tz_base = hdev->core->wrapper_tz_base;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
u32 ctrl_status, cpu_status;
- cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ if (IS_V6(hdev->core))
+ cpu_status = readl(wrapper_tz_base + WRAPPER_TZ_CPU_STATUS_V6);
+ else
+ cpu_status = readl(wrapper_base + WRAPPER_CPU_STATUS);
+ ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0);
if (cpu_status & WRAPPER_CPU_STATUS_WFI &&
ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)
@@ -1476,6 +1548,7 @@ static int venus_suspend_3xx(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
struct device *dev = core->dev;
+ void __iomem *cpu_cs_base = hdev->core->cpu_cs_base;
u32 ctrl_status;
bool val;
int ret;
@@ -1492,7 +1565,7 @@ static int venus_suspend_3xx(struct venus_core *core)
return -EINVAL;
}
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+ ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0);
if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)
goto power_off;
@@ -1539,7 +1612,7 @@ power_off:
static int venus_suspend(struct venus_core *core)
{
- if (IS_V3(core) || IS_V4(core))
+ if (IS_V3(core) || IS_V4(core) || IS_V6(core))
return venus_suspend_3xx(core);
return venus_suspend_1xx(core);
@@ -1580,10 +1653,10 @@ void venus_hfi_destroy(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
+ core->priv = NULL;
venus_interface_queues_release(hdev);
mutex_destroy(&hdev->lock);
kfree(hdev);
- core->priv = NULL;
core->ops = NULL;
}
diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h
index 3b52f98478db..300c6e47e72f 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus_io.h
+++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h
@@ -8,27 +8,31 @@
#define VBIF_BASE 0x80000
-#define VBIF_AXI_HALT_CTRL0 (VBIF_BASE + 0x208)
-#define VBIF_AXI_HALT_CTRL1 (VBIF_BASE + 0x20c)
+#define VBIF_AXI_HALT_CTRL0 0x208
+#define VBIF_AXI_HALT_CTRL1 0x20c
#define VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0)
#define VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0)
#define VBIF_AXI_HALT_ACK_TIMEOUT_US 500000
#define CPU_BASE 0xc0000
+
#define CPU_CS_BASE (CPU_BASE + 0x12000)
#define CPU_IC_BASE (CPU_BASE + 0x1f000)
+#define CPU_BASE_V6 0xa0000
+#define CPU_CS_BASE_V6 CPU_BASE_V6
+#define CPU_IC_BASE_V6 (CPU_BASE_V6 + 0x138)
-#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE + 0x1c)
+#define CPU_CS_A2HSOFTINTCLR 0x1c
-#define VIDC_CTRL_INIT (CPU_CS_BASE + 0x48)
+#define VIDC_CTRL_INIT 0x48
#define VIDC_CTRL_INIT_RESERVED_BITS31_1_MASK 0xfffffffe
#define VIDC_CTRL_INIT_RESERVED_BITS31_1_SHIFT 1
#define VIDC_CTRL_INIT_CTRL_MASK 0x1
#define VIDC_CTRL_INIT_CTRL_SHIFT 0
/* HFI control status */
-#define CPU_CS_SCIACMDARG0 (CPU_CS_BASE + 0x4c)
+#define CPU_CS_SCIACMDARG0 0x4c
#define CPU_CS_SCIACMDARG0_MASK 0xff
#define CPU_CS_SCIACMDARG0_SHIFT 0x0
#define CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK 0xfe
@@ -39,42 +43,56 @@
#define CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK BIT(30)
/* HFI queue table info */
-#define CPU_CS_SCIACMDARG1 (CPU_CS_BASE + 0x50)
+#define CPU_CS_SCIACMDARG1 0x50
/* HFI queue table address */
-#define CPU_CS_SCIACMDARG2 (CPU_CS_BASE + 0x54)
+#define CPU_CS_SCIACMDARG2 0x54
/* Venus cpu */
-#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE + 0x58)
-
-#define SFR_ADDR (CPU_CS_BASE + 0x5c)
-#define MMAP_ADDR (CPU_CS_BASE + 0x60)
-#define UC_REGION_ADDR (CPU_CS_BASE + 0x64)
-#define UC_REGION_SIZE (CPU_CS_BASE + 0x68)
-
-#define CPU_IC_SOFTINT (CPU_IC_BASE + 0x18)
+#define CPU_CS_SCIACMDARG3 0x58
+
+#define SFR_ADDR 0x5c
+#define MMAP_ADDR 0x60
+#define UC_REGION_ADDR 0x64
+#define UC_REGION_SIZE 0x68
+
+#define CPU_CS_H2XSOFTINTEN_V6 0x148
+
+#define CPU_CS_X2RPMH_V6 0x168
+#define CPU_CS_X2RPMH_MASK0_BMSK_V6 0x1
+#define CPU_CS_X2RPMH_MASK0_SHFT_V6 0x0
+#define CPU_CS_X2RPMH_MASK1_BMSK_V6 0x2
+#define CPU_CS_X2RPMH_MASK1_SHFT_V6 0x1
+#define CPU_CS_X2RPMH_SWOVERRIDE_BMSK_V6 0x4
+#define CPU_CS_X2RPMH_SWOVERRIDE_SHFT_V6 0x3
+
+/* Relative to CPU_IC_BASE */
+#define CPU_IC_SOFTINT 0x18
+#define CPU_IC_SOFTINT_V6 0x150
#define CPU_IC_SOFTINT_H2A_MASK 0x8000
#define CPU_IC_SOFTINT_H2A_SHIFT 0xf
+#define CPU_IC_SOFTINT_H2A_SHIFT_V6 0x0
/* Venus wrapper */
+#define WRAPPER_BASE_V6 0x000b0000
#define WRAPPER_BASE 0x000e0000
-#define WRAPPER_HW_VERSION (WRAPPER_BASE + 0x00)
+#define WRAPPER_HW_VERSION 0x00
#define WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000
#define WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28
#define WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xfff0000
#define WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16
#define WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xffff
-#define WRAPPER_CLOCK_CONFIG (WRAPPER_BASE + 0x04)
+#define WRAPPER_CLOCK_CONFIG 0x04
-#define WRAPPER_INTR_STATUS (WRAPPER_BASE + 0x0c)
+#define WRAPPER_INTR_STATUS 0x0c
#define WRAPPER_INTR_STATUS_A2HWD_MASK 0x10
#define WRAPPER_INTR_STATUS_A2HWD_SHIFT 0x4
#define WRAPPER_INTR_STATUS_A2H_MASK 0x4
#define WRAPPER_INTR_STATUS_A2H_SHIFT 0x2
-#define WRAPPER_INTR_MASK (WRAPPER_BASE + 0x10)
+#define WRAPPER_INTR_MASK 0x10
#define WRAPPER_INTR_MASK_A2HWD_BASK 0x10
#define WRAPPER_INTR_MASK_A2HWD_SHIFT 0x4
#define WRAPPER_INTR_MASK_A2HVCODEC_MASK 0x8
@@ -82,41 +100,59 @@
#define WRAPPER_INTR_MASK_A2HCPU_MASK 0x4
#define WRAPPER_INTR_MASK_A2HCPU_SHIFT 0x2
-#define WRAPPER_INTR_CLEAR (WRAPPER_BASE + 0x14)
+#define WRAPPER_INTR_STATUS_A2HWD_MASK_V6 0x8
+#define WRAPPER_INTR_MASK_A2HWD_BASK_V6 0x8
+
+#define WRAPPER_INTR_CLEAR 0x14
#define WRAPPER_INTR_CLEAR_A2HWD_MASK 0x10
#define WRAPPER_INTR_CLEAR_A2HWD_SHIFT 0x4
#define WRAPPER_INTR_CLEAR_A2H_MASK 0x4
#define WRAPPER_INTR_CLEAR_A2H_SHIFT 0x2
-#define WRAPPER_POWER_STATUS (WRAPPER_BASE + 0x44)
-#define WRAPPER_VDEC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x48)
-#define WRAPPER_VENC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x4c)
-#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET (WRAPPER_BASE + 0x64)
+#define WRAPPER_POWER_STATUS 0x44
+#define WRAPPER_VDEC_VCODEC_POWER_CONTROL 0x48
+#define WRAPPER_VENC_VCODEC_POWER_CONTROL 0x4c
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_V6 0x54
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS_V6 0x58
+#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET 0x64
-#define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000)
-#define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008)
+#define WRAPPER_CPU_CLOCK_CONFIG 0x2000
+#define WRAPPER_CPU_AXI_HALT 0x2008
#define WRAPPER_CPU_AXI_HALT_HALT BIT(16)
-#define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c)
+#define WRAPPER_CPU_AXI_HALT_STATUS 0x200c
#define WRAPPER_CPU_AXI_HALT_STATUS_IDLE BIT(24)
-#define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010)
-#define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014)
+#define WRAPPER_CPU_CGC_DIS 0x2010
+#define WRAPPER_CPU_STATUS 0x2014
#define WRAPPER_CPU_STATUS_WFI BIT(0)
-#define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000)
-#define WRAPPER_CPA_START_ADDR (WRAPPER_BASE + 0x1020)
-#define WRAPPER_CPA_END_ADDR (WRAPPER_BASE + 0x1024)
-#define WRAPPER_FW_START_ADDR (WRAPPER_BASE + 0x1028)
-#define WRAPPER_FW_END_ADDR (WRAPPER_BASE + 0x102C)
-#define WRAPPER_NONPIX_START_ADDR (WRAPPER_BASE + 0x1030)
-#define WRAPPER_NONPIX_END_ADDR (WRAPPER_BASE + 0x1034)
-#define WRAPPER_A9SS_SW_RESET (WRAPPER_BASE + 0x3000)
+#define WRAPPER_SW_RESET 0x3000
+#define WRAPPER_CPA_START_ADDR 0x1020
+#define WRAPPER_CPA_END_ADDR 0x1024
+#define WRAPPER_FW_START_ADDR 0x1028
+#define WRAPPER_FW_END_ADDR 0x102C
+#define WRAPPER_NONPIX_START_ADDR 0x1030
+#define WRAPPER_NONPIX_END_ADDR 0x1034
+#define WRAPPER_A9SS_SW_RESET 0x3000
#define WRAPPER_A9SS_SW_RESET_BIT BIT(4)
/* Venus 4xx */
-#define WRAPPER_VCODEC0_MMCC_POWER_STATUS (WRAPPER_BASE + 0x90)
-#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x94)
+#define WRAPPER_VCODEC0_MMCC_POWER_STATUS 0x90
+#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL 0x94
+
+#define WRAPPER_VCODEC1_MMCC_POWER_STATUS 0x110
+#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL 0x114
+
+/* Venus 6xx */
+#define WRAPPER_CORE_POWER_STATUS_V6 0x80
+#define WRAPPER_CORE_POWER_CONTROL_V6 0x84
+
+/* Wrapper TZ 6xx */
+#define WRAPPER_TZ_BASE_V6 0x000c0000
+#define WRAPPER_TZ_CPU_STATUS_V6 0x10
-#define WRAPPER_VCODEC1_MMCC_POWER_STATUS (WRAPPER_BASE + 0x110)
-#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x114)
+/* Venus AON */
+#define AON_BASE_V6 0x000e0000
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL 0x00
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS 0x04
#endif
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index 43c4e3d9e281..c7e1ebec47ee 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -11,6 +11,7 @@
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <linux/types.h>
#include <media/v4l2-mem2mem.h>
@@ -40,10 +41,24 @@ static int core_clks_get(struct venus_core *core)
static int core_clks_enable(struct venus_core *core)
{
const struct venus_resources *res = core->res;
+ const struct freq_tbl *freq_tbl = core->res->freq_tbl;
+ unsigned int freq_tbl_size = core->res->freq_tbl_size;
+ unsigned long freq;
unsigned int i;
int ret;
+ if (!freq_tbl)
+ return -EINVAL;
+
+ freq = freq_tbl[freq_tbl_size - 1].freq;
+
for (i = 0; i < res->clks_num; i++) {
+ if (IS_V6(core)) {
+ ret = clk_set_rate(core->clks[i], freq);
+ if (ret)
+ goto err;
+ }
+
ret = clk_prepare_enable(core->clks[i]);
if (ret)
goto err;
@@ -186,7 +201,7 @@ static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak)
return;
for (i = 0; i < num_rows; i++) {
- if (mbs > bw_tbl[i].mbs_per_sec)
+ if (i != 0 && mbs > bw_tbl[i].mbs_per_sec)
break;
if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) {
@@ -277,16 +292,28 @@ set_freq:
return 0;
}
-static int core_get_v1(struct device *dev)
+static int core_get_v1(struct venus_core *core)
{
- struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
+
+ ret = core_clks_get(core);
+ if (ret)
+ return ret;
+
+ core->opp_table = dev_pm_opp_set_clkname(core->dev, "core");
+ if (IS_ERR(core->opp_table))
+ return PTR_ERR(core->opp_table);
- return core_clks_get(core);
+ return 0;
}
-static int core_power_v1(struct device *dev, int on)
+static void core_put_v1(struct venus_core *core)
+{
+ dev_pm_opp_put_clkname(core->opp_table);
+}
+
+static int core_power_v1(struct venus_core *core, int on)
{
- struct venus_core *core = dev_get_drvdata(dev);
int ret = 0;
if (on == POWER_ON)
@@ -299,6 +326,7 @@ static int core_power_v1(struct device *dev, int on)
static const struct venus_pm_ops pm_ops_v1 = {
.core_get = core_get_v1,
+ .core_put = core_put_v1,
.core_power = core_power_v1,
.load_scale = load_scale_v1,
};
@@ -309,9 +337,9 @@ vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable)
void __iomem *ctrl;
if (session_type == VIDC_SESSION_TYPE_DEC)
- ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL;
+ ctrl = core->wrapper_base + WRAPPER_VDEC_VCODEC_POWER_CONTROL;
else
- ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL;
+ ctrl = core->wrapper_base + WRAPPER_VENC_VCODEC_POWER_CONTROL;
if (enable)
writel(0, ctrl);
@@ -371,6 +399,7 @@ static int venc_power_v3(struct device *dev, int on)
static const struct venus_pm_ops pm_ops_v3 = {
.core_get = core_get_v1,
+ .core_put = core_put_v1,
.core_power = core_power_v1,
.vdec_get = vdec_get_v3,
.vdec_power = vdec_power_v3,
@@ -385,12 +414,15 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable)
u32 val;
int ret;
- if (coreid == VIDC_CORE_ID_1) {
- ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
- stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
+ if (IS_V6(core)) {
+ ctrl = core->wrapper_base + WRAPPER_CORE_POWER_CONTROL_V6;
+ stat = core->wrapper_base + WRAPPER_CORE_POWER_STATUS_V6;
+ } else if (coreid == VIDC_CORE_ID_1) {
+ ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
+ stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
} else {
- ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL;
- stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS;
+ ctrl = core->wrapper_base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL;
+ stat = core->wrapper_base + WRAPPER_VCODEC1_MMCC_POWER_STATUS;
}
if (enable) {
@@ -753,12 +785,12 @@ static int venc_power_v4(struct device *dev, int on)
return ret;
}
-static int vcodec_domains_get(struct device *dev)
+static int vcodec_domains_get(struct venus_core *core)
{
int ret;
struct opp_table *opp_table;
struct device **opp_virt_dev;
- struct venus_core *core = dev_get_drvdata(dev);
+ struct device *dev = core->dev;
const struct venus_resources *res = core->res;
struct device *pd;
unsigned int i;
@@ -809,9 +841,8 @@ opp_attach_err:
return ret;
}
-static void vcodec_domains_put(struct device *dev)
+static void vcodec_domains_put(struct venus_core *core)
{
- struct venus_core *core = dev_get_drvdata(dev);
const struct venus_resources *res = core->res;
unsigned int i;
@@ -834,9 +865,55 @@ skip_pmdomains:
dev_pm_opp_detach_genpd(core->opp_table);
}
-static int core_get_v4(struct device *dev)
+static int core_resets_reset(struct venus_core *core)
{
- struct venus_core *core = dev_get_drvdata(dev);
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+ int ret;
+
+ if (!res->resets_num)
+ return 0;
+
+ for (i = 0; i < res->resets_num; i++) {
+ ret = reset_control_assert(core->resets[i]);
+ if (ret)
+ goto err;
+
+ usleep_range(150, 250);
+ ret = reset_control_deassert(core->resets[i]);
+ if (ret)
+ goto err;
+ }
+
+err:
+ return ret;
+}
+
+static int core_resets_get(struct venus_core *core)
+{
+ struct device *dev = core->dev;
+ const struct venus_resources *res = core->res;
+ unsigned int i;
+ int ret;
+
+ if (!res->resets_num)
+ return 0;
+
+ for (i = 0; i < res->resets_num; i++) {
+ core->resets[i] =
+ devm_reset_control_get_exclusive(dev, res->resets[i]);
+ if (IS_ERR(core->resets[i])) {
+ ret = PTR_ERR(core->resets[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int core_get_v4(struct venus_core *core)
+{
+ struct device *dev = core->dev;
const struct venus_resources *res = core->res;
int ret;
@@ -857,6 +934,10 @@ static int core_get_v4(struct device *dev)
if (ret)
return ret;
+ ret = core_resets_get(core);
+ if (ret)
+ return ret;
+
if (legacy_binding)
return 0;
@@ -875,7 +956,7 @@ static int core_get_v4(struct device *dev)
}
}
- ret = vcodec_domains_get(dev);
+ ret = vcodec_domains_get(core);
if (ret) {
if (core->has_opp_table)
dev_pm_opp_of_remove_table(dev);
@@ -886,14 +967,14 @@ static int core_get_v4(struct device *dev)
return 0;
}
-static void core_put_v4(struct device *dev)
+static void core_put_v4(struct venus_core *core)
{
- struct venus_core *core = dev_get_drvdata(dev);
+ struct device *dev = core->dev;
if (legacy_binding)
return;
- vcodec_domains_put(dev);
+ vcodec_domains_put(core);
if (core->has_opp_table)
dev_pm_opp_of_remove_table(dev);
@@ -901,9 +982,9 @@ static void core_put_v4(struct device *dev)
}
-static int core_power_v4(struct device *dev, int on)
+static int core_power_v4(struct venus_core *core, int on)
{
- struct venus_core *core = dev_get_drvdata(dev);
+ struct device *dev = core->dev;
struct device *pmctrl = core->pmdomains[0];
int ret = 0;
@@ -916,6 +997,13 @@ static int core_power_v4(struct device *dev, int on)
}
}
+ ret = core_resets_reset(core);
+ if (ret) {
+ if (pmctrl)
+ pm_runtime_put_sync(pmctrl);
+ return ret;
+ }
+
ret = core_clks_enable(core);
if (ret < 0 && pmctrl)
pm_runtime_put_sync(pmctrl);
@@ -926,6 +1014,8 @@ static int core_power_v4(struct device *dev, int on)
core_clks_disable(core);
+ ret = core_resets_reset(core);
+
if (pmctrl)
pm_runtime_put_sync(pmctrl);
}
@@ -993,7 +1083,7 @@ static int load_scale_v4(struct venus_inst *inst)
freq = max(freq_core1, freq_core2);
- if (freq >= table[0].freq) {
+ if (freq > table[0].freq) {
freq = table[0].freq;
dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n",
freq, table[0].freq);
@@ -1049,6 +1139,7 @@ const struct venus_pm_ops *venus_pm_get(enum hfi_version version)
case HFI_VERSION_3XX:
return &pm_ops_v3;
case HFI_VERSION_4XX:
+ case HFI_VERSION_6XX:
return &pm_ops_v4;
}
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h
index aa2f6afa2354..a492c50c5543 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.h
+++ b/drivers/media/platform/qcom/venus/pm_helpers.h
@@ -4,14 +4,15 @@
#define __VENUS_PM_HELPERS_H__
struct device;
+struct venus_core;
#define POWER_ON 1
#define POWER_OFF 0
struct venus_pm_ops {
- int (*core_get)(struct device *dev);
- void (*core_put)(struct device *dev);
- int (*core_power)(struct device *dev, int on);
+ int (*core_get)(struct venus_core *core);
+ void (*core_put)(struct venus_core *core);
+ int (*core_power)(struct venus_core *core, int on);
int (*vdec_get)(struct device *dev);
void (*vdec_put)(struct device *dev);
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index e4dc97f00fc3..ddb7cd39424e 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -515,7 +515,10 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
fdata.buffer_type = HFI_BUFFER_INPUT;
fdata.flags |= HFI_BUFFERFLAG_EOS;
- fdata.device_addr = 0xdeadb000;
+ if (IS_V6(inst->core))
+ fdata.device_addr = 0;
+ else
+ fdata.device_addr = 0xdeadb000;
ret = hfi_session_process_buf(inst, &fdata);
@@ -620,7 +623,7 @@ static int vdec_set_properties(struct venus_inst *inst)
{
struct vdec_controls *ctr = &inst->controls.dec;
struct hfi_enable en = { .enable = 1 };
- u32 ptype;
+ u32 ptype, decode_order, conceal;
int ret;
if (ctr->post_loop_deb_mode) {
@@ -630,6 +633,23 @@ static int vdec_set_properties(struct venus_inst *inst)
return ret;
}
+ if (ctr->display_delay_enable && ctr->display_delay == 0) {
+ ptype = HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER;
+ decode_order = HFI_OUTPUT_ORDER_DECODE;
+ ret = hfi_session_set_property(inst, ptype, &decode_order);
+ if (ret)
+ return ret;
+ }
+
+ ptype = HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR;
+ conceal = ctr->conceal_color & 0xffff;
+ conceal |= ((ctr->conceal_color >> 16) & 0xffff) << 10;
+ conceal |= ((ctr->conceal_color >> 32) & 0xffff) << 20;
+
+ ret = hfi_session_set_property(inst, ptype, &conceal);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -647,7 +667,7 @@ static int vdec_output_conf(struct venus_inst *inst)
u32 ptype;
int ret;
- ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2);
+ ret = venus_helper_set_work_mode(inst);
if (ret)
return ret;
@@ -662,8 +682,8 @@ static int vdec_output_conf(struct venus_inst *inst)
if (width > 1920 && height > ALIGN(1080, 32))
ubwc = true;
- /* For Venus v4 UBWC format is mandatory */
- if (IS_V4(core))
+ /* For Venus v4/v6 UBWC format is mandatory */
+ if (IS_V4(core) || IS_V6(core))
ubwc = true;
ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt,
@@ -698,6 +718,10 @@ static int vdec_output_conf(struct venus_inst *inst)
if (ret)
return ret;
+ ret = venus_helper_set_format_constraints(inst);
+ if (ret)
+ return ret;
+
if (inst->dpb_fmt) {
ret = venus_helper_set_multistream(inst, false, true);
if (ret)
@@ -714,7 +738,7 @@ static int vdec_output_conf(struct venus_inst *inst)
return ret;
}
- if (IS_V3(core) || IS_V4(core)) {
+ if (IS_V3(core) || IS_V4(core) || IS_V6(core)) {
ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq);
if (ret)
return ret;
diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c
index 974110b75b93..fbe12a608b21 100644
--- a/drivers/media/platform/qcom/venus/vdec_ctrls.c
+++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c
@@ -30,6 +30,15 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_VP9_LEVEL:
ctr->level = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY:
+ ctr->display_delay = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE:
+ ctr->display_delay_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR:
+ ctr->conceal_color = *ctrl->p_new.p_s64;
+ break;
default:
return -EINVAL;
}
@@ -89,7 +98,7 @@ int vdec_ctrl_init(struct venus_inst *inst)
struct v4l2_ctrl *ctrl;
int ret;
- ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 9);
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 12);
if (ret)
return ret;
@@ -158,6 +167,18 @@ int vdec_ctrl_init(struct venus_inst *inst)
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY,
+ 0, 16383, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE,
+ 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR, 0,
+ 0xffffffffffffLL, 1, 0x8000800010LL);
+
ret = inst->ctrl_handler.error;
if (ret) {
v4l2_ctrl_handler_free(&inst->ctrl_handler);
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 6976ed553647..4a7291f934b6 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -546,11 +546,12 @@ static int venc_set_properties(struct venus_inst *inst)
struct hfi_quantization quant;
struct hfi_quantization_range quant_range;
struct hfi_enable en;
+ struct hfi_ltr_mode ltr_mode;
u32 ptype, rate_control, bitrate;
u32 profile, level;
int ret;
- ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2);
+ ret = venus_helper_set_work_mode(inst);
if (ret)
return ret;
@@ -612,6 +613,35 @@ static int venc_set_properties(struct venus_inst *inst)
return ret;
}
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ struct hfi_hdr10_pq_sei hdr10;
+ unsigned int c;
+
+ ptype = HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI;
+
+ for (c = 0; c < 3; c++) {
+ hdr10.mastering.display_primaries_x[c] =
+ ctr->mastering.display_primaries_x[c];
+ hdr10.mastering.display_primaries_y[c] =
+ ctr->mastering.display_primaries_y[c];
+ }
+
+ hdr10.mastering.white_point_x = ctr->mastering.white_point_x;
+ hdr10.mastering.white_point_y = ctr->mastering.white_point_y;
+ hdr10.mastering.max_display_mastering_luminance =
+ ctr->mastering.max_display_mastering_luminance;
+ hdr10.mastering.min_display_mastering_luminance =
+ ctr->mastering.min_display_mastering_luminance;
+
+ hdr10.cll.max_content_light = ctr->cll.max_content_light_level;
+ hdr10.cll.max_pic_average_light =
+ ctr->cll.max_pic_average_light_level;
+
+ ret = hfi_session_set_property(inst, ptype, &hdr10);
+ if (ret)
+ return ret;
+ }
+
if (ctr->num_b_frames) {
u32 max_num_b_frames = NUM_B_FRAMES_MAX;
@@ -722,6 +752,14 @@ static int venc_set_properties(struct venus_inst *inst)
if (ret)
return ret;
+ ptype = HFI_PROPERTY_PARAM_VENC_LTRMODE;
+ ltr_mode.ltr_count = ctr->ltr_count;
+ ltr_mode.ltr_mode = HFI_LTR_MODE_MANUAL;
+ ltr_mode.trust_mode = 1;
+ ret = hfi_session_set_property(inst, ptype, &ltr_mode);
+ if (ret)
+ return ret;
+
switch (inst->hfi_codec) {
case HFI_VIDEO_CODEC_H264:
profile = ctr->profile.h264;
@@ -754,6 +792,20 @@ static int venc_set_properties(struct venus_inst *inst)
if (ret)
return ret;
+ if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
+ inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ struct hfi_enable en = {};
+
+ ptype = HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL;
+
+ if (ctr->aud_enable)
+ en.enable = 1;
+
+ ret = hfi_session_set_property(inst, ptype, &en);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index a52b80055173..637c92f6c5be 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -20,6 +20,7 @@
#define INTRA_REFRESH_MBS_MAX 300
#define AT_SLICE_BOUNDARY \
V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+#define MAX_LTR_FRAME_COUNT 4
static int venc_calc_bpframes(u32 gop_size, u32 conseq_b, u32 *bf, u32 *pf)
{
@@ -72,6 +73,8 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
struct venc_controls *ctr = &inst->controls.enc;
struct hfi_enable en = { .enable = 1 };
struct hfi_bitrate brate;
+ struct hfi_ltr_use ltr_use;
+ struct hfi_ltr_mark ltr_mark;
u32 bframes;
u32 ptype;
int ret;
@@ -276,6 +279,46 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID:
ctr->base_priority_id = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_AU_DELIMITER:
+ ctr->aud_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_LTR_COUNT:
+ ctr->ltr_count = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX:
+ mutex_lock(&inst->lock);
+ if (inst->streamon_out && inst->streamon_cap) {
+ ptype = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME;
+ ltr_mark.mark_frame = ctrl->val;
+ ret = hfi_session_set_property(inst, ptype, &ltr_mark);
+ if (ret) {
+ mutex_unlock(&inst->lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&inst->lock);
+ break;
+ case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES:
+ mutex_lock(&inst->lock);
+ if (inst->streamon_out && inst->streamon_cap) {
+ ptype = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME;
+ ltr_use.ref_ltr = ctrl->val;
+ ltr_use.use_constrnt = true;
+ ltr_use.frames = 0;
+ ret = hfi_session_set_property(inst, ptype, &ltr_use);
+ if (ret) {
+ mutex_unlock(&inst->lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&inst->lock);
+ break;
+ case V4L2_CID_COLORIMETRY_HDR10_CLL_INFO:
+ ctr->cll = *ctrl->p_new.p_hdr10_cll;
+ break;
+ case V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY:
+ ctr->mastering = *ctrl->p_new.p_hdr10_mastering;
+ break;
default:
return -EINVAL;
}
@@ -291,7 +334,7 @@ int venc_ctrl_init(struct venus_inst *inst)
{
int ret;
- ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 51);
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 57);
if (ret)
return ret;
@@ -359,7 +402,7 @@ int venc_ctrl_init(struct venus_inst *inst)
V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
~((1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME)),
- V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE);
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
@@ -498,6 +541,29 @@ int venc_ctrl_init(struct venus_inst *inst)
V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID, 0,
6, 1, 0);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_AU_DELIMITER, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES, 0,
+ ((1 << MAX_LTR_FRAME_COUNT) - 1), 0, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_LTR_COUNT, 0,
+ MAX_LTR_FRAME_COUNT, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX, 0,
+ (MAX_LTR_FRAME_COUNT - 1), 1, 0);
+
+ v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
+ v4l2_ctrl_ptr_create(NULL));
+
+ v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
+ v4l2_ctrl_ptr_create(NULL));
+
ret = inst->ctrl_handler.error;
if (ret)
goto err;
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 0ee9d402f5ac..b263ead4db2b 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -49,11 +49,12 @@ enum rvin_csi_id {
};
/**
- * STOPPED - No operation in progress
- * STARTING - Capture starting up
- * RUNNING - Operation in progress have buffers
- * STOPPING - Stopping operation
- * SUSPENDED - Capture is suspended
+ * enum rvin_dma_state - DMA states
+ * @STOPPED: No operation in progress
+ * @STARTING: Capture starting up
+ * @RUNNING: Operation in progress have buffers
+ * @STOPPING: Stopping operation
+ * @SUSPENDED: Capture is suspended
*/
enum rvin_dma_state {
STOPPED = 0,
@@ -70,9 +71,9 @@ enum rvin_dma_state {
* to capture SEQ_TB/BT it's needed to capture to the same vb2
* buffer twice so the type of buffer needs to be kept.
*
- * FULL - One capture fills the whole vb2 buffer
- * HALF_TOP - One capture fills the top half of the vb2 buffer
- * HALF_BOTTOM - One capture fills the bottom half of the vb2 buffer
+ * @FULL: One capture fills the whole vb2 buffer
+ * @HALF_TOP: One capture fills the top half of the vb2 buffer
+ * @HALF_BOTTOM: One capture fills the bottom half of the vb2 buffer
*/
enum rvin_buffer_type {
FULL,
@@ -191,7 +192,7 @@ struct rvin_info {
* @state: keeps track of operation state
*
* @is_csi: flag to mark the VIN as using a CSI-2 subdevice
- * @chsel Cached value of the current CSI-2 channel selection
+ * @chsel: Cached value of the current CSI-2 channel selection
*
* @mbus_code: media bus format code
* @format: active V4L2 pixel format
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index a60c302ef267..b6beddd988d0 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -889,219 +889,177 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
module_cfg_update = new_params->module_cfg_update;
module_ens = new_params->module_ens;
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_DPCC) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)) {
- /*update dpc config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)
- rkisp1_dpcc_config(params,
- &new_params->others.dpcc_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_DPCC) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_DPCC_MODE,
- RKISP1_CIF_ISP_DPCC_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_DPCC_MODE,
- RKISP1_CIF_ISP_DPCC_ENA);
- }
+ /* update dpc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)
+ rkisp1_dpcc_config(params,
+ &new_params->others.dpcc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_DPCC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_BLS) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS)) {
- /* update bls config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS)
- rkisp1_bls_config(params,
- &new_params->others.bls_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_BLS) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_BLS)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_BLS_CTRL,
- RKISP1_CIF_ISP_BLS_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_BLS_CTRL,
- RKISP1_CIF_ISP_BLS_ENA);
- }
- }
+ /* update bls config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS)
+ rkisp1_bls_config(params,
+ &new_params->others.bls_config);
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_SDG) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG)) {
- /* update sdg config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG)
- rkisp1_sdg_config(params,
- &new_params->others.sdg_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_SDG) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_SDG)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_CTRL,
- RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_CTRL,
- RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
- }
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_BLS) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_BLS)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_LSC) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)) {
- /* update lsc config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)
- rkisp1_lsc_config(params,
- &new_params->others.lsc_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_LSC) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_LSC)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_LSC_CTRL,
- RKISP1_CIF_ISP_LSC_CTRL_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_LSC_CTRL,
- RKISP1_CIF_ISP_LSC_CTRL_ENA);
- }
- }
+ /* update sdg config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG)
+ rkisp1_sdg_config(params,
+ &new_params->others.sdg_config);
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)) {
- /* update awb gains */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
- rkisp1_awb_gain_config(params,
- &new_params->others.awb_gain_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_CTRL,
- RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_CTRL,
- RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
- }
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_SDG) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_SDG)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_BDM) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM)) {
- /* update bdm config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM)
- rkisp1_bdm_config(params,
- &new_params->others.bdm_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_BDM) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_BDM)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_DEMOSAIC,
- RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_DEMOSAIC,
- RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
- }
+ /* update lsc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)
+ rkisp1_lsc_config(params,
+ &new_params->others.lsc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_LSC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_LSC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_FLT) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT)) {
- /* update filter config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT)
- rkisp1_flt_config(params,
- &new_params->others.flt_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_FLT) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_FLT)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_FILT_MODE,
- RKISP1_CIF_ISP_FLT_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_FILT_MODE,
- RKISP1_CIF_ISP_FLT_ENA);
- }
+ /* update awb gains */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
+ rkisp1_awb_gain_config(params, &new_params->others.awb_gain_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_AWB_GAIN)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_CTK) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK)) {
- /* update ctk config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK)
- rkisp1_ctk_config(params,
- &new_params->others.ctk_config);
+ /* update bdm config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM)
+ rkisp1_bdm_config(params,
+ &new_params->others.bdm_config);
- if (module_en_update & RKISP1_CIF_ISP_MODULE_CTK)
- rkisp1_ctk_enable(params,
- !!(module_ens & RKISP1_CIF_ISP_MODULE_CTK));
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_BDM) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_BDM)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_GOC) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC)) {
- /* update goc config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC)
- rkisp1_goc_config(params,
- &new_params->others.goc_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_GOC) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_GOC)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_CTRL,
- RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_CTRL,
- RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
- }
+ /* update filter config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT)
+ rkisp1_flt_config(params,
+ &new_params->others.flt_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_FLT) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_FLT)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_CPROC) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC)) {
- /* update cproc config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC) {
- rkisp1_cproc_config(params,
- &new_params->others.cproc_config);
- }
+ /* update ctk config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK)
+ rkisp1_ctk_config(params,
+ &new_params->others.ctk_config);
- if (module_en_update & RKISP1_CIF_ISP_MODULE_CPROC) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_CPROC)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_C_PROC_CTRL,
- RKISP1_CIF_C_PROC_CTR_ENABLE);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_C_PROC_CTRL,
- RKISP1_CIF_C_PROC_CTR_ENABLE);
- }
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_CTK)
+ rkisp1_ctk_enable(params, !!(module_ens & RKISP1_CIF_ISP_MODULE_CTK));
+
+ /* update goc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC)
+ rkisp1_goc_config(params,
+ &new_params->others.goc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_GOC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_GOC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_IE) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_IE)) {
- /* update ie config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_IE)
- rkisp1_ie_config(params,
- &new_params->others.ie_config);
+ /* update cproc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC)
+ rkisp1_cproc_config(params,
+ &new_params->others.cproc_config);
- if (module_en_update & RKISP1_CIF_ISP_MODULE_IE)
- rkisp1_ie_enable(params,
- !!(module_ens & RKISP1_CIF_ISP_MODULE_IE));
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_CPROC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_CPROC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_DPF) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF)) {
- /* update dpf config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF)
- rkisp1_dpf_config(params,
- &new_params->others.dpf_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_DPF) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_DPF)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_DPF_MODE,
- RKISP1_CIF_ISP_DPF_MODE_EN);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_DPF_MODE,
- RKISP1_CIF_ISP_DPF_MODE_EN);
- }
+ /* update ie config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_IE)
+ rkisp1_ie_config(params, &new_params->others.ie_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_IE)
+ rkisp1_ie_enable(params, !!(module_ens & RKISP1_CIF_ISP_MODULE_IE));
+
+ /* update dpf config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF)
+ rkisp1_dpf_config(params, &new_params->others.dpf_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_DPF) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_DPF)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
}
if ((module_en_update & RKISP1_CIF_ISP_MODULE_DPF_STRENGTH) ||
@@ -1121,68 +1079,55 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params,
module_cfg_update = new_params->module_cfg_update;
module_ens = new_params->module_ens;
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_AWB) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB)) {
- /* update awb config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB)
- rkisp1_awb_meas_config(params,
- &new_params->meas.awb_meas_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB)
- rkisp1_awb_meas_enable(params,
- &new_params->meas.awb_meas_config,
- !!(module_ens & RKISP1_CIF_ISP_MODULE_AWB));
- }
-
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_AFC) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC)) {
- /* update afc config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC)
- rkisp1_afm_config(params,
- &new_params->meas.afc_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_AFC) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_AFC)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_AFM_CTRL,
- RKISP1_CIF_ISP_AFM_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_AFM_CTRL,
- RKISP1_CIF_ISP_AFM_ENA);
- }
- }
-
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_HST) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_HST)) {
- /* update hst config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_HST)
- rkisp1_hst_config(params,
- &new_params->meas.hst_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_HST)
- rkisp1_hst_enable(params,
- &new_params->meas.hst_config,
- !!(module_ens & RKISP1_CIF_ISP_MODULE_HST));
+ /* update awb config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB)
+ rkisp1_awb_meas_config(params, &new_params->meas.awb_meas_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB)
+ rkisp1_awb_meas_enable(params,
+ &new_params->meas.awb_meas_config,
+ !!(module_ens & RKISP1_CIF_ISP_MODULE_AWB));
+
+ /* update afc config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC)
+ rkisp1_afm_config(params,
+ &new_params->meas.afc_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AFC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_AFC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
}
- if ((module_en_update & RKISP1_CIF_ISP_MODULE_AEC) ||
- (module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC)) {
- /* update aec config */
- if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC)
- rkisp1_aec_config(params,
- &new_params->meas.aec_config);
-
- if (module_en_update & RKISP1_CIF_ISP_MODULE_AEC) {
- if (module_ens & RKISP1_CIF_ISP_MODULE_AEC)
- rkisp1_param_set_bits(params,
- RKISP1_CIF_ISP_EXP_CTRL,
- RKISP1_CIF_ISP_EXP_ENA);
- else
- rkisp1_param_clear_bits(params,
- RKISP1_CIF_ISP_EXP_CTRL,
- RKISP1_CIF_ISP_EXP_ENA);
- }
+ /* update hst config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_HST)
+ rkisp1_hst_config(params,
+ &new_params->meas.hst_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_HST)
+ rkisp1_hst_enable(params,
+ &new_params->meas.hst_config,
+ !!(module_ens & RKISP1_CIF_ISP_MODULE_HST));
+
+ /* update aec config */
+ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC)
+ rkisp1_aec_config(params,
+ &new_params->meas.aec_config);
+
+ if (module_en_update & RKISP1_CIF_ISP_MODULE_AEC) {
+ if (module_ens & RKISP1_CIF_ISP_MODULE_AEC)
+ rkisp1_param_set_bits(params,
+ RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+ else
+ rkisp1_param_clear_bits(params,
+ RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
}
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
index 813670ed9577..79deed8adcea 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
@@ -520,14 +520,15 @@ static void rkisp1_rsz_set_src_fmt(struct rkisp1_resizer *rsz,
struct v4l2_mbus_framefmt *format,
unsigned int which)
{
- const struct rkisp1_isp_mbus_info *mbus_info;
- struct v4l2_mbus_framefmt *src_fmt;
+ const struct rkisp1_isp_mbus_info *sink_mbus_info;
+ struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
+ sink_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, which);
src_fmt = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SRC, which);
- mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code);
+ sink_mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code);
/* for YUV formats, userspace can change the mbus code on the src pad if it is supported */
- if (mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
+ if (sink_mbus_info->pixel_enc == V4L2_PIXEL_ENC_YUV &&
rkisp1_rsz_get_yuv_mbus_info(format->code))
src_fmt->code = format->code;
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h
index f937e638490f..f3442e251bc9 100644
--- a/drivers/media/platform/s3c-camif/camif-core.h
+++ b/drivers/media/platform/s3c-camif/camif-core.h
@@ -144,8 +144,10 @@ struct camif_pix_limits {
/**
* struct s3c_camif_variant - CAMIF variant structure
* @vp_pix_limits: pixel limits for the codec and preview paths
- * @camif_pix_limits: pixel limits for the camera input interface
+ * @pix_limits: pixel limits for the camera input interface
* @ip_revision: the CAMIF IP revision: 0x20 for s3c244x, 0x32 for s3c6410
+ * @has_img_effect: supports image effects
+ * @vp_offset: register offset
*/
struct s3c_camif_variant {
struct vp_pix_limits vp_pix_limits[2];
@@ -183,9 +185,10 @@ struct camif_dev;
* @irq: interrupt number for this data path
* @camif: pointer to the camif structure
* @pad: media pad for the video node
- * @vdev video device
+ * @vdev: video device
* @ctrl_handler: video node controls handler
* @owner: file handle that own the streaming
+ * @vb_queue: vb2 buffer queue
* @pending_buf_q: pending (empty) buffers queue head
* @active_buf_q: active (being written) buffers queue head
* @active_buffers: counter of buffer set up at the DMA engine
@@ -202,6 +205,7 @@ struct camif_dev;
* @rotation: current image rotation value
* @hflip: apply horizontal flip if set
* @vflip: apply vertical flip if set
+ * @offset: register offset
*/
struct camif_vp {
wait_queue_head_t irq_queue;
@@ -248,7 +252,13 @@ struct camif_vp {
* @sensor: image sensor data structure
* @m_pipeline: video entity pipeline description
* @ctrl_handler: v4l2 control handler (owned by @subdev)
- * @test_pattern: test pattern controls
+ * @ctrl_test_pattern: V4L2_CID_TEST_PATTERN control
+ * @ctrl_colorfx: V4L2_CID_COLORFX control
+ * @ctrl_colorfx_cbcr: V4L2_CID_COLORFX_CBCR control
+ * @test_pattern: test pattern
+ * @colorfx: color effect
+ * @colorfx_cb: Cb value for V4L2_COLORFX_SET_CBCR
+ * @colorfx_cr: Cr value for V4L2_COLORFX_SET_CBCR
* @vp: video path (DMA) description (codec/preview)
* @variant: variant information for this device
* @dev: pointer to the CAMIF device struct
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h
index 4407fe775afa..a77d93c098ce 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h
@@ -111,12 +111,12 @@ enum s5p_jpeg_ctx_state {
* @m2m_dev: v4l2 mem2mem device data
* @regs: JPEG IP registers mapping
* @irq: JPEG IP irq
+ * @irq_ret: JPEG IP irq result value
* @clocks: JPEG IP clock(s)
* @dev: JPEG IP struct device
* @variant: driver variant to be used
- * @irq_status interrupt flags set during single encode/decode
- operation
-
+ * @irq_status: interrupt flags set during single encode/decode
+ * operation
*/
struct s5p_jpeg {
struct mutex lock;
@@ -149,12 +149,14 @@ struct s5p_jpeg_variant {
};
/**
- * struct jpeg_fmt - driver's internal color format data
+ * struct s5p_jpeg_fmt - driver's internal color format data
* @fourcc: the fourcc code, 0 if not applicable
* @depth: number of bits per pixel
* @colplanes: number of color planes (1 for packed formats)
+ * @memplanes: number of memory planes (1 for packed formats)
* @h_align: horizontal alignment order (align to 2^h_align)
* @v_align: vertical alignment order (align to 2^v_align)
+ * @subsampling:subsampling of a raw format or a JPEG
* @flags: flags describing format applicability
*/
struct s5p_jpeg_fmt {
@@ -169,7 +171,7 @@ struct s5p_jpeg_fmt {
};
/**
- * s5p_jpeg_marker - collection of markers from jpeg header
+ * struct s5p_jpeg_marker - collection of markers from jpeg header
* @marker: markers' positions relative to the buffer beginning
* @len: markers' payload lengths (without length field)
* @n: number of markers in collection
@@ -181,7 +183,7 @@ struct s5p_jpeg_marker {
};
/**
- * s5p_jpeg_q_data - parameters of one queue
+ * struct s5p_jpeg_q_data - parameters of one queue
* @fmt: driver-specific format of this queue
* @w: image width
* @h: image height
@@ -205,7 +207,7 @@ struct s5p_jpeg_q_data {
};
/**
- * s5p_jpeg_ctx - the device context data
+ * struct s5p_jpeg_ctx - the device context data
* @jpeg: JPEG IP device for this context
* @mode: compression (encode) operation or decompression (decode)
* @compr_quality: destination image quality in compression (encode) mode
@@ -239,7 +241,7 @@ struct s5p_jpeg_ctx {
};
/**
- * s5p_jpeg_buffer - description of memory containing input JPEG data
+ * struct s5p_jpeg_buffer - description of memory containing input JPEG data
* @size: buffer size
* @curr: current position in the buffer
* @data: pointer to the data
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 96d1ecd1521b..c3ef4f6a42d2 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -102,7 +102,7 @@
#define mfc_write(dev, data, offset) writel((data), dev->regs_base + \
(offset))
-/**
+/*
* enum s5p_mfc_fmt_type - type of the pixelformat
*/
enum s5p_mfc_fmt_type {
@@ -111,7 +111,7 @@ enum s5p_mfc_fmt_type {
MFC_FMT_RAW,
};
-/**
+/*
* enum s5p_mfc_inst_type - The type of an MFC instance.
*/
enum s5p_mfc_inst_type {
@@ -120,7 +120,7 @@ enum s5p_mfc_inst_type {
MFCINST_ENCODER,
};
-/**
+/*
* enum s5p_mfc_inst_state - The state of an MFC instance.
*/
enum s5p_mfc_inst_state {
@@ -142,7 +142,7 @@ enum s5p_mfc_inst_state {
MFCINST_RES_CHANGE_END,
};
-/**
+/*
* enum s5p_mfc_queue_state - The state of buffer queue.
*/
enum s5p_mfc_queue_state {
@@ -152,7 +152,7 @@ enum s5p_mfc_queue_state {
QUEUE_BUFS_MMAPED,
};
-/**
+/*
* enum s5p_mfc_decode_arg - type of frame decoding
*/
enum s5p_mfc_decode_arg {
@@ -171,7 +171,7 @@ enum s5p_mfc_fw_ver {
struct s5p_mfc_ctx;
-/**
+/*
* struct s5p_mfc_buf - MFC buffer
*/
struct s5p_mfc_buf {
@@ -187,7 +187,7 @@ struct s5p_mfc_buf {
int flags;
};
-/**
+/*
* struct s5p_mfc_pm - power management data structure
*/
struct s5p_mfc_pm {
@@ -257,7 +257,7 @@ struct s5p_mfc_priv_buf {
* @vfd_dec: video device for decoding
* @vfd_enc: video device for encoding
* @plat_dev: platform device
- * @mem_dev[]: child devices of the memory banks
+ * @mem_dev: child devices of the memory banks
* @regs_base: base address of the MFC hw registers
* @irq: irq resource
* @dec_ctrl_handler: control framework handler for decoding
@@ -273,14 +273,18 @@ struct s5p_mfc_priv_buf {
* @int_type: type of last interrupt
* @int_err: error number for last interrupt
* @queue: waitqueue for waiting for completion of device commands
- * @fw_size: size of firmware
- * @fw_virt_addr: virtual firmware address
- * @dma_base[]: address of the beginning of memory banks
+ * @fw_buf: the firmware buffer data structure
+ * @mem_size: size of the firmware operation memory
+ * @mem_base: base DMA address of the firmware operation memory
+ * @mem_bitmap: bitmap for managing MFC internal buffer allocations
+ * @mem_virt: virtual address of the firmware operation memory
+ * @dma_base: address of the beginning of memory banks
* @hw_lock: used for hardware locking
* @ctx: array of driver contexts
* @curr_ctx: number of the currently running context
* @ctx_work_bits: used to mark which contexts are waiting for hardware
* @watchdog_cnt: counter for the watchdog
+ * @watchdog_timer: timer for the watchdog
* @watchdog_workqueue: workqueue for the watchdog
* @watchdog_work: worker for the watchdog
* @enter_suspend: flag set when entering suspend
@@ -290,9 +294,9 @@ struct s5p_mfc_priv_buf {
* @mfc_cmds: cmd structure holding HW commands function pointers
* @mfc_regs: structure holding MFC registers
* @fw_ver: loaded firmware sub-version
- * @fw_get_done flag set when request_firmware() is complete and
+ * @fw_get_done: flag set when request_firmware() is complete and
* copied into fw_buf
- * risc_on: flag indicates RISC is on or off
+ * @risc_on: flag indicates RISC is on or off
*
*/
struct s5p_mfc_dev {
@@ -342,7 +346,7 @@ struct s5p_mfc_dev {
bool risc_on; /* indicates if RISC is on or off */
};
-/**
+/*
* struct s5p_mfc_h264_enc_params - encoding parameters for h264
*/
struct s5p_mfc_h264_enc_params {
@@ -391,7 +395,7 @@ struct s5p_mfc_h264_enc_params {
u32 aso_slice_order[8];
};
-/**
+/*
* struct s5p_mfc_mpeg4_enc_params - encoding parameters for h263 and mpeg4
*/
struct s5p_mfc_mpeg4_enc_params {
@@ -410,7 +414,7 @@ struct s5p_mfc_mpeg4_enc_params {
int level;
};
-/**
+/*
* struct s5p_mfc_vp8_enc_params - encoding parameters for vp8
*/
struct s5p_mfc_vp8_enc_params {
@@ -479,7 +483,7 @@ struct s5p_mfc_hevc_enc_params {
u8 prepend_sps_pps_to_idr;
};
-/**
+/*
* struct s5p_mfc_enc_params - general encoding parameters
*/
struct s5p_mfc_enc_params {
@@ -521,7 +525,7 @@ struct s5p_mfc_enc_params {
};
-/**
+/*
* struct s5p_mfc_codec_ops - codec ops, used by encoding
*/
struct s5p_mfc_codec_ops {
@@ -579,7 +583,9 @@ struct s5p_mfc_codec_ops {
* @capture_state: state of the capture buffers queue
* @output_state: state of the output buffers queue
* @src_bufs: information on allocated source buffers
+ * @src_bufs_cnt: number of allocated source buffers
* @dst_bufs: information on allocated destination buffers
+ * @dst_bufs_cnt: number of allocated destination buffers
* @sequence: counter for the sequence number for v4l2
* @dec_dst_flag: flags for buffers queued in the hardware
* @dec_src_buf_size: size of the buffer for source buffers in decoding
@@ -591,7 +597,7 @@ struct s5p_mfc_codec_ops {
* @after_packed_pb: flag used to track buffer when stream is in
* Packed PB format
* @sei_fp_parse: enable/disable parsing of frame packing SEI information
- * @dpb_count: count of the DPB buffers required by MFC hw
+ * @pb_count: count of the DPB buffers required by MFC hw
* @total_dpb_count: count of DPB buffers with additional buffers
* requested by the application
* @ctx: context buffer information
@@ -606,11 +612,15 @@ struct s5p_mfc_codec_ops {
* @tmv_buffer_size: size of temporal predictor motion vector buffer
* @frame_type: used to force the type of the next encoded frame
* @ref_queue: list of the reference buffers for encoding
+ * @force_frame_type: encoder's frame type forcing control
* @ref_queue_cnt: number of the buffers in the reference list
+ * @slice_size: slice size
+ * @slice_mode: mode of dividing frames into slices
* @c_ops: ops for encoding
* @ctrls: array of controls, used when adding controls to the
* v4l2 control framework
* @ctrl_handler: handler for v4l2 framework
+ * @scratch_buf_size: scratch buffer size
*/
struct s5p_mfc_ctx {
struct s5p_mfc_dev *dev;
@@ -709,7 +719,6 @@ struct s5p_mfc_ctx {
struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
struct v4l2_ctrl_handler ctrl_handler;
- unsigned int frame_tag;
size_t scratch_buf_size;
};
@@ -725,7 +734,7 @@ struct s5p_mfc_fmt {
u32 versions;
};
-/**
+/*
* struct mfc_control - structure used to store information about MFC controls
* it is used to initialize the control framework.
*/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index a71753d459ba..a92a9ca6e87e 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -168,6 +168,13 @@ static struct mfc_control controls[] = {
.default_value = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 16383,
+ .default_value = 0,
+ },
+ {
.id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "H264 Display Delay Enable",
@@ -177,6 +184,13 @@ static struct mfc_control controls[] = {
.default_value = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mpeg4 Loop Filter Enable",
@@ -690,9 +704,11 @@ static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+ case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY:
ctx->display_delay = ctrl->val;
break;
case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE:
ctx->display_delay_enable = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c
index 2b270093009c..a27f638df11c 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c
@@ -480,7 +480,7 @@ static int regs_show(struct seq_file *s, void *data)
int ret;
unsigned int i;
- ret = pm_runtime_get_sync(bdisp->dev);
+ ret = pm_runtime_resume_and_get(bdisp->dev);
if (ret < 0) {
seq_puts(s, "Cannot wake up IP\n");
return 0;
diff --git a/drivers/media/platform/sti/bdisp/bdisp-filter.h b/drivers/media/platform/sti/bdisp/bdisp-filter.h
index d25adb57e3d0..9e1a95fd27ed 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-filter.h
+++ b/drivers/media/platform/sti/bdisp/bdisp-filter.h
@@ -12,7 +12,7 @@
*
* @min: min scale factor for this filter (6.10 fixed point)
* @max: max scale factor for this filter (6.10 fixed point)
- * coef: filter coefficients
+ * @coef: filter coefficients
*/
struct bdisp_filter_h_spec {
const u16 min;
@@ -24,7 +24,7 @@ struct bdisp_filter_h_spec {
*
* @min: min scale factor for this filter (6.10 fixed point)
* @max: max scale factor for this filter (6.10 fixed point)
- * coef: filter coefficients
+ * @coef: filter coefficients
*/
struct bdisp_filter_v_spec {
const u16 min;
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index a7a6ea666740..338b205ae3a7 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -655,7 +655,7 @@ static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
/*
* TODO FIXME we should detect some error conditions here
- * and ideally so something about them!
+ * and ideally do something about them!
*/
return IRQ_HANDLED;
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h
index b8c30bcc8df9..d2c35fb32d7e 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h
@@ -1,11 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
-/**
+/*
* c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header
*
* Copyright (c) STMicroelectronics 2015
*
* Authors: Peter Griffin <peter.griffin@linaro.org>
- *
*/
#ifndef __C8SECTPFE_DEBUG_H
diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/sti/hva/hva-h264.c
index c34f7cf5aed2..98cb00d2d868 100644
--- a/drivers/media/platform/sti/hva/hva-h264.c
+++ b/drivers/media/platform/sti/hva/hva-h264.c
@@ -428,8 +428,10 @@ static int hva_h264_fill_slice_header(struct hva_ctx *pctx,
*/
struct device *dev = ctx_to_dev(pctx);
int cabac = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC;
- const unsigned char slice_header[] = { 0x00, 0x00, 0x00, 0x01,
- 0x41, 0x34, 0x07, 0x00};
+ static const unsigned char slice_header[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x41, 0x34, 0x07, 0x00
+ };
int idr_pic_id = frame_num % 2;
enum hva_picture_coding_type type;
u32 frame_order = frame_num % ctrls->gop_size;
@@ -488,7 +490,7 @@ static int hva_h264_fill_data_nal(struct hva_ctx *pctx,
unsigned int stream_size, unsigned int *size)
{
struct device *dev = ctx_to_dev(pctx);
- const u8 start[] = { 0x00, 0x00, 0x00, 0x01 };
+ static const u8 start[] = { 0x00, 0x00, 0x00, 0x01 };
dev_dbg(dev, "%s %s stuffing bytes %d\n", pctx->name, __func__,
stuffing_bytes);
@@ -521,7 +523,7 @@ static int hva_h264_fill_sei_nal(struct hva_ctx *pctx,
u8 *addr, u32 *size)
{
struct device *dev = ctx_to_dev(pctx);
- const u8 start[] = { 0x00, 0x00, 0x00, 0x01 };
+ static const u8 start[] = { 0x00, 0x00, 0x00, 0x01 };
struct hva_h264_stereo_video_sei info;
u8 offset = 7;
u8 msg = 0;
diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
index 1226d60cc367..ba6b893416ec 100644
--- a/drivers/media/platform/sti/hva/hva.h
+++ b/drivers/media/platform/sti/hva/hva.h
@@ -130,7 +130,7 @@ struct hva_frame {
/**
* struct hva_stream - hva stream buffer (capture)
*
- * @v4l2: video buffer information for V4L2
+ * @vbuf: video buffer information for V4L2
* @list: V4L2 m2m list that the frame belongs to
* @paddr: physical address (for hardware)
* @vaddr: virtual address (kernel can read/write)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
index b55de9ab64d8..3181d0781b61 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -151,8 +151,10 @@ static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
}
subdev = sun6i_video_remote_subdev(video, NULL);
- if (!subdev)
+ if (!subdev) {
+ ret = -EINVAL;
goto stop_media_pipeline;
+ }
config.pixelformat = video->fmt.fmt.pix.pixelformat;
config.code = video->mbus_code;
diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
index ed863bf5ea80..671e4a928993 100644
--- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
+++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
@@ -589,7 +589,7 @@ static int deinterlace_start_streaming(struct vb2_queue *vq, unsigned int count)
int ret;
if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err(dev, "Failed to enable module\n");
diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c
index dd48017859cd..cbe6114908de 100644
--- a/drivers/media/platform/ti-vpe/cal-camerarx.c
+++ b/drivers/media/platform/ti-vpe/cal-camerarx.c
@@ -830,6 +830,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
sd = &phy->subdev;
v4l2_subdev_init(sd, &cal_camerarx_subdev_ops);
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance);
sd->dev = cal->dev;
diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti-vpe/cal-video.c
index 779f1e1bc529..7b7436a355ee 100644
--- a/drivers/media/platform/ti-vpe/cal-video.c
+++ b/drivers/media/platform/ti-vpe/cal-video.c
@@ -40,7 +40,35 @@ static char *fourcc_to_str(u32 fmt)
}
/* ------------------------------------------------------------------
- * V4L2 Video IOCTLs
+ * V4L2 Common IOCTLs
+ * ------------------------------------------------------------------
+ */
+
+static int cal_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card));
+
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev_name(ctx->cal->dev));
+ return 0;
+}
+
+static int cal_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ *f = ctx->v_fmt;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ * V4L2 Video Node Centric IOCTLs
* ------------------------------------------------------------------
*/
@@ -74,19 +102,6 @@ static const struct cal_format_info *find_format_by_code(struct cal_ctx *ctx,
return NULL;
}
-static int cal_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cal_ctx *ctx = video_drvdata(file);
-
- strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver));
- strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card));
-
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", dev_name(ctx->cal->dev));
- return 0;
-}
-
static int cal_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
@@ -174,16 +189,6 @@ static void cal_calc_format_size(struct cal_ctx *ctx,
f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
}
-static int cal_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cal_ctx *ctx = video_drvdata(file);
-
- *f = ctx->v_fmt;
-
- return 0;
-}
-
static int cal_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -383,16 +388,7 @@ static int cal_enum_frameintervals(struct file *file, void *priv,
return 0;
}
-static const struct v4l2_file_operations cal_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops cal_ioctl_ops = {
+static const struct v4l2_ioctl_ops cal_ioctl_video_ops = {
.vidioc_querycap = cal_querycap,
.vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
@@ -418,7 +414,155 @@ static const struct v4l2_ioctl_ops cal_ioctl_ops = {
};
/* ------------------------------------------------------------------
- * videobuf2 Operations
+ * V4L2 Media Controller Centric IOCTLs
+ * ------------------------------------------------------------------
+ */
+
+static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= cal_num_formats)
+ return -EINVAL;
+
+ f->pixelformat = cal_formats[f->index].fourcc;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
+ const struct cal_format_info **info)
+{
+ struct v4l2_pix_format *format = &f->fmt.pix;
+ const struct cal_format_info *fmtinfo;
+ unsigned int bpp;
+
+ /*
+ * Default to the first format if the requested pixel format code isn't
+ * supported.
+ */
+ fmtinfo = cal_format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmtinfo)
+ fmtinfo = &cal_formats[0];
+
+ /*
+ * Clamp the size, update the pixel format. The field and colorspace are
+ * accepted as-is, except for V4L2_FIELD_ANY that is turned into
+ * V4L2_FIELD_NONE.
+ */
+ bpp = ALIGN(fmtinfo->bpp, 8);
+
+ format->width = clamp_t(unsigned int, format->width,
+ CAL_MIN_WIDTH_BYTES * 8 / bpp,
+ CAL_MAX_WIDTH_BYTES * 8 / bpp);
+ format->height = clamp_t(unsigned int, format->height,
+ CAL_MIN_HEIGHT_LINES, CAL_MAX_HEIGHT_LINES);
+ format->pixelformat = fmtinfo->fourcc;
+
+ if (format->field == V4L2_FIELD_ANY)
+ format->field = V4L2_FIELD_NONE;
+
+ /*
+ * Calculate the number of bytes per line and the image size. The
+ * hardware stores the stride as a number of 16 bytes words, in a
+ * signed 15-bit value. Only 14 bits are thus usable.
+ */
+ format->bytesperline = ALIGN(clamp(format->bytesperline,
+ format->width * bpp / 8,
+ ((1U << 14) - 1) * 16), 16);
+
+ format->sizeimage = format->height * format->bytesperline;
+
+ format->colorspace = ctx->v_fmt.fmt.pix.colorspace;
+
+ if (info)
+ *info = fmtinfo;
+
+ ctx_dbg(3, ctx, "%s: %s %ux%u (bytesperline %u sizeimage %u)\n",
+ __func__, fourcc_to_str(format->pixelformat),
+ format->width, format->height,
+ format->bytesperline, format->sizeimage);
+}
+
+static int cal_mc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+
+ cal_mc_try_fmt(ctx, f, NULL);
+ return 0;
+}
+
+static int cal_mc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_format_info *fmtinfo;
+
+ if (vb2_is_busy(&ctx->vb_vidq)) {
+ ctx_dbg(3, ctx, "%s device busy\n", __func__);
+ return -EBUSY;
+ }
+
+ cal_mc_try_fmt(ctx, f, &fmtinfo);
+
+ ctx->v_fmt = *f;
+ ctx->fmtinfo = fmtinfo;
+
+ return 0;
+}
+
+static int cal_mc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct cal_ctx *ctx = video_drvdata(file);
+ const struct cal_format_info *fmtinfo;
+ unsigned int bpp;
+
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ fmtinfo = cal_format_by_fourcc(fsize->pixel_format);
+ if (!fmtinfo) {
+ ctx_dbg(3, ctx, "Invalid pixel format 0x%08x\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ bpp = ALIGN(fmtinfo->bpp, 8);
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = CAL_MIN_WIDTH_BYTES * 8 / bpp;
+ fsize->stepwise.max_width = CAL_MAX_WIDTH_BYTES * 8 / bpp;
+ fsize->stepwise.step_width = 64 / bpp;
+ fsize->stepwise.min_height = CAL_MIN_HEIGHT_LINES;
+ fsize->stepwise.max_height = CAL_MAX_HEIGHT_LINES;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops cal_ioctl_mc_ops = {
+ .vidioc_querycap = cal_querycap,
+ .vidioc_enum_fmt_vid_cap = cal_mc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cal_mc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cal_mc_s_fmt_vid_cap,
+ .vidioc_enum_framesizes = cal_mc_enum_framesizes,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+};
+
+/* ------------------------------------------------------------------
+ * videobuf2 Common Operations
* ------------------------------------------------------------------
*/
@@ -504,6 +648,26 @@ static void cal_release_buffers(struct cal_ctx *ctx,
spin_unlock_irq(&ctx->dma.lock);
}
+/* ------------------------------------------------------------------
+ * videobuf2 Operations
+ * ------------------------------------------------------------------
+ */
+
+static int cal_video_check_format(struct cal_ctx *ctx)
+{
+ const struct v4l2_mbus_framefmt *format;
+
+ format = &ctx->phy->formats[CAL_CAMERARX_PAD_SOURCE];
+
+ if (ctx->fmtinfo->code != format->code ||
+ ctx->v_fmt.fmt.pix.height != format->height ||
+ ctx->v_fmt.fmt.pix.width != format->width ||
+ ctx->v_fmt.fmt.pix.field != format->field)
+ return -EPIPE;
+
+ return 0;
+}
+
static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct cal_ctx *ctx = vb2_get_drv_priv(vq);
@@ -511,6 +675,23 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
dma_addr_t addr;
int ret;
+ ret = media_pipeline_start(&ctx->vdev.entity, &ctx->phy->pipe);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
+ goto error_release_buffers;
+ }
+
+ /*
+ * Verify that the currently configured format matches the output of
+ * the connected CAMERARX.
+ */
+ ret = cal_video_check_format(ctx);
+ if (ret < 0) {
+ ctx_dbg(3, ctx,
+ "Format mismatch between CAMERARX and video node\n");
+ goto error_pipeline;
+ }
+
spin_lock_irq(&ctx->dma.lock);
buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, list);
ctx->dma.pending = buf;
@@ -526,18 +707,22 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1);
if (ret)
- goto err;
+ goto error_stop;
if (cal_debug >= 4)
cal_quickdump_regs(ctx->cal);
return 0;
-err:
+error_stop:
cal_ctx_stop(ctx);
pm_runtime_put_sync(ctx->cal->dev);
+error_pipeline:
+ media_pipeline_stop(&ctx->vdev.entity);
+error_release_buffers:
cal_release_buffers(ctx, VB2_BUF_STATE_QUEUED);
+
return ret;
}
@@ -552,6 +737,8 @@ static void cal_stop_streaming(struct vb2_queue *vq)
pm_runtime_put_sync(ctx->cal->dev);
cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
+
+ media_pipeline_stop(&ctx->vdev.entity);
}
static const struct vb2_ops cal_video_qops = {
@@ -569,13 +756,13 @@ static const struct vb2_ops cal_video_qops = {
* ------------------------------------------------------------------
*/
-static const struct video_device cal_videodev = {
- .name = CAL_MODULE_NAME,
- .fops = &cal_fops,
- .ioctl_ops = &cal_ioctl_ops,
- .minor = -1,
- .release = video_device_release_empty,
- .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
+static const struct v4l2_file_operations cal_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = vb2_fop_mmap,
};
static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
@@ -591,15 +778,21 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
sizeof(*ctx->active_fmt), GFP_KERNEL);
ctx->num_active_fmt = 0;
- for (j = 0, i = 0; ret != -EINVAL; ++j) {
+ for (j = 0, i = 0; ; ++j) {
memset(&mbus_code, 0, sizeof(mbus_code));
mbus_code.index = j;
mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code,
NULL, &mbus_code);
- if (ret)
- continue;
+ if (ret == -EINVAL)
+ break;
+
+ if (ret) {
+ ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n",
+ ctx->phy->sensor->name, ret);
+ return ret;
+ }
ctx_dbg(2, ctx,
"subdev %s: code: %04x idx: %u\n",
@@ -640,7 +833,6 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ctx->v_fmt.fmt.pix.pixelformat = fmtinfo->fourcc;
- ctx->v_fmt.fmt.pix.field = mbus_fmt.field;
cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
ctx->fmtinfo = fmtinfo;
@@ -649,7 +841,6 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
int cal_ctx_v4l2_register(struct cal_ctx *ctx)
{
- struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
struct video_device *vfd = &ctx->vdev;
int ret;
@@ -657,11 +848,15 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx)
if (ret)
return ret;
- ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL,
- true);
- if (ret < 0) {
- ctx_err(ctx, "Failed to add sensor ctrl handler\n");
- return ret;
+ if (!cal_mc_api) {
+ struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
+
+ ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler,
+ NULL, true);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to add sensor ctrl handler\n");
+ return ret;
+ }
}
ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr);
@@ -698,7 +893,6 @@ void cal_ctx_v4l2_unregister(struct cal_ctx *ctx)
int cal_ctx_v4l2_init(struct cal_ctx *ctx)
{
- struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
struct video_device *vfd = &ctx->vdev;
struct vb2_queue *q = &ctx->vb_vidq;
int ret;
@@ -725,10 +919,14 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx)
return ret;
/* Initialize the video device and media entity. */
- *vfd = cal_videodev;
+ vfd->fops = &cal_fops;
+ vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
+ | (cal_mc_api ? V4L2_CAP_IO_MC : 0);
vfd->v4l2_dev = &ctx->cal->v4l2_dev;
vfd->queue = q;
snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index);
+ vfd->release = video_device_release_empty;
+ vfd->ioctl_ops = cal_mc_api ? &cal_ioctl_mc_ops : &cal_ioctl_video_ops;
vfd->lock = &ctx->mutex;
video_set_drvdata(vfd, ctx);
@@ -737,14 +935,18 @@ int cal_ctx_v4l2_init(struct cal_ctx *ctx)
if (ret < 0)
return ret;
- /* Initialize the control handler. */
- ret = v4l2_ctrl_handler_init(hdl, 11);
- if (ret < 0) {
- ctx_err(ctx, "Failed to init ctrl handler\n");
- goto error;
- }
+ if (!cal_mc_api) {
+ /* Initialize the control handler. */
+ struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler;
- vfd->ctrl_handler = hdl;
+ ret = v4l2_ctrl_handler_init(hdl, 11);
+ if (ret < 0) {
+ ctx_err(ctx, "Failed to init ctrl handler\n");
+ goto error;
+ }
+
+ vfd->ctrl_handler = hdl;
+ }
return 0;
@@ -755,6 +957,8 @@ error:
void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx)
{
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ if (!cal_mc_api)
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+
media_entity_cleanup(&ctx->vdev.entity);
}
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c
index fa0931788040..2e2bef91b2b0 100644
--- a/drivers/media/platform/ti-vpe/cal.c
+++ b/drivers/media/platform/ti-vpe/cal.c
@@ -43,6 +43,16 @@ unsigned int cal_debug;
module_param_named(debug, cal_debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
+#ifdef CONFIG_VIDEO_TI_CAL_MC
+#define CAL_MC_API_DEFAULT 1
+#else
+#define CAL_MC_API_DEFAULT 0
+#endif
+
+bool cal_mc_api = CAL_MC_API_DEFAULT;
+module_param_named(mc_api, cal_mc_api, bool, 0444);
+MODULE_PARM_DESC(mc_api, "activates the MC API");
+
/* ------------------------------------------------------------------
* Format Handling
* ------------------------------------------------------------------
@@ -660,13 +670,17 @@ static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier);
unsigned int i;
+ int ret = 0;
for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
if (cal->ctx[i])
cal_ctx_v4l2_register(cal->ctx[i]);
}
- return 0;
+ if (cal_mc_api)
+ ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev);
+
+ return ret;
}
static const struct v4l2_async_notifier_operations cal_async_notifier_ops = {
diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti-vpe/cal.h
index d471b7f82519..db0e408eaa94 100644
--- a/drivers/media/platform/ti-vpe/cal.h
+++ b/drivers/media/platform/ti-vpe/cal.h
@@ -84,35 +84,34 @@ struct cal_buffer {
/**
* struct cal_dmaqueue - Queue of DMA buffers
- * @active: Buffer being DMA'ed to for the current frame
*/
struct cal_dmaqueue {
/**
- * Protects all fields in the cal_dmaqueue.
+ * @lock: Protects all fields in the cal_dmaqueue.
*/
spinlock_t lock;
/**
- * Buffers queued to the driver and waiting for DMA processing.
+ * @queue: Buffers queued to the driver and waiting for DMA processing.
* Buffers are added to the list by the vb2 .buffer_queue() operation,
* and move to @pending when they are scheduled for the next frame.
*/
struct list_head queue;
/**
- * Buffer provided to the hardware to DMA the next frame. Will move to
- * @active at the end of the current frame.
+ * @pending: Buffer provided to the hardware to DMA the next frame.
+ * Will move to @active at the end of the current frame.
*/
struct cal_buffer *pending;
/**
- * Buffer being DMA'ed to for the current frame. Will be retired and
- * given back to vb2 at the end of the current frame if a @pending
- * buffer has been scheduled to replace it.
+ * @active: Buffer being DMA'ed to for the current frame. Will be
+ * retired and given back to vb2 at the end of the current frame if
+ * a @pending buffer has been scheduled to replace it.
*/
struct cal_buffer *active;
- /** State of the DMA engine. */
+ /** @state: State of the DMA engine. */
enum cal_dma_state state;
- /** Wait queue to signal a @state transition to CAL_DMA_STOPPED. */
+ /** @wait: Wait queue to signal a @state transition to CAL_DMA_STOPPED. */
struct wait_queue_head wait;
};
@@ -160,6 +159,7 @@ struct cal_camerarx {
struct device_node *sensor_ep_node;
struct device_node *sensor_node;
struct v4l2_subdev *sensor;
+ struct media_pipeline pipe;
struct v4l2_subdev subdev;
struct media_pad pads[2];
@@ -224,6 +224,7 @@ struct cal_ctx {
extern unsigned int cal_debug;
extern int cal_video_nr;
+extern bool cal_mc_api;
#define cal_dbg(level, cal, fmt, arg...) \
do { \
diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c
index f4e0cf72d1cf..ff15bc589f1b 100644
--- a/drivers/media/platform/ti-vpe/csc.c
+++ b/drivers/media/platform/ti-vpe/csc.c
@@ -267,10 +267,8 @@ struct csc_data *csc_create(struct platform_device *pdev, const char *res_name)
}
csc->base = devm_ioremap_resource(&pdev->dev, csc->res);
- if (IS_ERR(csc->base)) {
- dev_err(&pdev->dev, "failed to ioremap\n");
+ if (IS_ERR(csc->base))
return ERR_CAST(csc->base);
- }
return csc;
}
diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c
index 98f95082a6fd..0202d278523f 100644
--- a/drivers/media/platform/ti-vpe/sc.c
+++ b/drivers/media/platform/ti-vpe/sc.c
@@ -294,10 +294,8 @@ struct sc_data *sc_create(struct platform_device *pdev, const char *res_name)
}
sc->base = devm_ioremap_resource(&pdev->dev, sc->res);
- if (IS_ERR(sc->base)) {
- dev_err(&pdev->dev, "failed to ioremap\n");
+ if (IS_ERR(sc->base))
return ERR_CAST(sc->base);
- }
return sc;
}
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
index 2e5148ae7a0f..f8998a8ad371 100644
--- a/drivers/media/platform/ti-vpe/vpdma.c
+++ b/drivers/media/platform/ti-vpe/vpdma.c
@@ -942,8 +942,7 @@ int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv)
unsigned long flags;
spin_lock_irqsave(&vpdma->lock, flags);
- for (i = 0; i < VPDMA_MAX_NUM_LIST &&
- vpdma->hwlist_used[i] == true; i++)
+ for (i = 0; i < VPDMA_MAX_NUM_LIST && vpdma->hwlist_used[i]; i++)
;
if (i < VPDMA_MAX_NUM_LIST) {
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index e07b135613eb..ad3fa1c9cc73 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -142,7 +142,7 @@ struct vsp1_dl_body_pool {
};
/**
- * struct vsp1_cmd_pool - Display List commands pool
+ * struct vsp1_dl_cmd_pool - Display List commands pool
* @dma: DMA address of the entries
* @size: size of the full DMA memory pool in bytes
* @mem: CPU memory pointer for the pool
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index e85ad4366fbb..ab8b7e3161a2 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -18,7 +18,7 @@
#include "vsp1_pipe.h"
/**
- * vsp1_drm_pipeline - State for the API exposed to the DRM driver
+ * struct vsp1_drm_pipeline - State for the API exposed to the DRM driver
* @pipe: the VSP1 pipeline used for display
* @width: output display width
* @height: output display height
@@ -47,7 +47,7 @@ struct vsp1_drm_pipeline {
};
/**
- * vsp1_drm - State for the API exposed to the DRM driver
+ * struct vsp1_drm - State for the API exposed to the DRM driver
* @pipe: the VSP1 DRM pipeline used for display
* @lock: protects the BRU and BRS allocation
* @inputs: source crop rectangle, destination compose rectangle and z-order
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index bf4015d852e3..2ce31d7ce1a6 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -525,6 +525,7 @@ static int xvip_graph_init(struct xvip_composite_device *xdev)
if (list_empty(&xdev->notifier.asd_list)) {
dev_err(xdev->dev, "no subdev found in graph\n");
+ ret = -ENOENT;
goto done;
}