summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
commitf4cdf7ca9a1fdcca413157df19753f388a5a224e (patch)
tree60a31895b2446cd7fa27514e8c2843534a0de0da /drivers/media/platform
parente5c91aac491def6ab3f90c4cc246e3fcb0f8f058 (diff)
parent4900cad020c0580dfb1be27776ff10a4ef110cfa (diff)
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - v4l2-core: added ISP statistics support and per-block validation - v4l2-core: Allow unknown HDR10 white point and luminance - New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor - New codec: Milos: VPU v2.0 codec support - isp driver: gained support for Dreamchip RPPX1 ISP framework - vsp1 driver: gained support for RZ/T2H and RZ/N2H - Novalake driver: gained CVS support for new NVL hardware - dvb-core: fix feed leak on failed DMX_ADD_PID - several driver fixes, cleanups and minor improvements * tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits) media: ipu-bridge: check all DMI entries when overriding sensor rotation media: v4l2-async: avoid deleting unlinked ASC entry on link error media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement media: intel/ipu6: fix async notifier cleanup leak on parse error media: staging/ipu7: fix async notifier UAF on probe error path media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem() media: rcar-isp: Fix VSPX reference leaks media: rcar-isp: Release ISPCORE resources media: i2c: imx415: Release runtime PM reference on VBLANK error media: i2c: imx415: Return test pattern write errors media: renesas: vsp1: Declare index variables in for loop statement media: renesas: vsp1: Make reset control optional to support platforms without a reset line media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs media: nxp: imx8-isi: Add additional 32-bit RGB format support media: nxp: imx8-isi: Add 16-bit raw Bayer format support media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks media: nxp: imx8-isi: Correct color map between V4L2 and ISI ...
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig1
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/amd/isp4/Kconfig1
-rw-r--r--drivers/media/platform/amd/isp4/isp4_interface.c10
-rw-r--r--drivers/media/platform/amd/isp4/isp4_interface.h2
-rw-r--r--drivers/media/platform/amd/isp4/isp4_subdev.c30
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c42
-rw-r--r--drivers/media/platform/amphion/venc.c2
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-capture.c4
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c2
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-params.c140
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-registers.h15
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-resizer.c2
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c5
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c64
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c6
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h2
-rw-r--r--drivers/media/platform/dreamchip/Kconfig3
-rw-r--r--drivers/media/platform/dreamchip/Makefile6
-rw-r--r--drivers/media/platform/dreamchip/rppx1/Kconfig12
-rw-r--r--drivers/media/platform/dreamchip/rppx1/Makefile33
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rpp_module.c39
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rpp_module.h119
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rpp_params.c115
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rpp_stats.c66
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1.c381
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1.h105
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_acq.c148
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c63
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_bd.c53
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c81
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_bls.c157
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_cac.c30
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c166
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_db.c45
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c77
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_exm.c127
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_ga.c91
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_hist.c186
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c47
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_is.c43
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_lin.c112
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c188
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c49
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c42
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_outif.c46
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c76
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c63
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c47
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c65
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c165
-rw-r--r--drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c27
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c6
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h6
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c29
-rw-r--r--drivers/media/platform/microchip/microchip-sama7g5-isc.c7
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c3
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h12
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c111
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c22
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c2
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c79
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h12
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c98
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c4
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c123
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h2
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.c12
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c43
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h2
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c119
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.c26
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c5
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.c905
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c7
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h8
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c4
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h11
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_milos.h27
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_vpu2.c46
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_vpu3x.c10
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c8
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c19
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c4
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c14
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c4
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h6
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c9
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c7
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c6
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Kconfig2
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Makefile2
-rw-r--r--drivers/media/platform/renesas/rcar-isp/core-io.c997
-rw-r--r--drivers/media/platform/renesas/rcar-isp/core.c916
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c46
-rw-r--r--drivers/media/platform/renesas/rcar-isp/risp-core.h178
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h2
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c5
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c12
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c14
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c44
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c4
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.c7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.c13
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c5
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c34
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c8
-rw-r--r--drivers/media/platform/rockchip/rga/rga3-hw.c12
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c3
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c4
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c8
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c8
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c8
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.c17
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c19
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c4
-rw-r--r--drivers/media/platform/ti/Kconfig1
-rw-r--r--drivers/media/platform/ti/vpe/vip.c37
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c12
126 files changed, 7197 insertions, 469 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 0b33e927bd59..2c7699b6610b 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -72,6 +72,7 @@ source "drivers/media/platform/atmel/Kconfig"
source "drivers/media/platform/broadcom/Kconfig"
source "drivers/media/platform/cadence/Kconfig"
source "drivers/media/platform/chips-media/Kconfig"
+source "drivers/media/platform/dreamchip/Kconfig"
source "drivers/media/platform/imagination/Kconfig"
source "drivers/media/platform/intel/Kconfig"
source "drivers/media/platform/marvell/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 16c185752474..d47c47d817da 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -15,6 +15,7 @@ obj-y += atmel/
obj-y += broadcom/
obj-y += cadence/
obj-y += chips-media/
+obj-y += dreamchip/
obj-y += imagination/
obj-y += intel/
obj-y += marvell/
diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig
index 9d1927af1cb8..77b61fae82ba 100644
--- a/drivers/media/platform/amd/isp4/Kconfig
+++ b/drivers/media/platform/amd/isp4/Kconfig
@@ -5,6 +5,7 @@ config VIDEO_AMD_ISP4_CAPTURE
depends on DRM_AMDGPU && DRM_AMD_ISP
depends on HAS_DMA
depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
select VIDEOBUF2_V4L2
diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c
index 8d73f66bb42c..4801617f9559 100644
--- a/drivers/media/platform/amd/isp4/isp4_interface.c
+++ b/drivers/media/platform/amd/isp4/isp4_interface.c
@@ -148,12 +148,9 @@ static void isp4if_gpu_mem_free(struct isp4_interface *ispif,
struct isp4if_gpu_mem_info **mem_info_ptr)
{
struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr;
- struct device *dev = ispif->dev;
- if (!mem_info) {
- dev_err(dev, "invalid mem_info\n");
+ if (!mem_info)
return;
- }
*mem_info_ptr = NULL;
isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr,
@@ -201,6 +198,7 @@ static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif)
error_no_memory:
dev_err(dev, "failed to allocate gpu memory\n");
+ isp4if_dealloc_fw_gpumem(ispif);
return -ENOMEM;
}
@@ -375,7 +373,7 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id,
return -ENOMEM;
/* Get two references: one for the resp thread, one for us */
- atomic_set(&ele->refcnt, 2);
+ refcount_set(&ele->refcnt, 2);
init_completion(&ele->cmd_done);
}
@@ -455,7 +453,7 @@ err_dequeue_ele:
put_ele_ref:
/* Don't free the command if we didn't put the last reference */
- if (ele && atomic_dec_return(&ele->refcnt))
+ if (ele && !refcount_dec_and_test(&ele->refcnt))
ele = NULL;
free_ele:
diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h
index ce3ac9b9e5cd..04db71cd54e6 100644
--- a/drivers/media/platform/amd/isp4/isp4_interface.h
+++ b/drivers/media/platform/amd/isp4/isp4_interface.h
@@ -68,7 +68,7 @@ struct isp4if_cmd_element {
u32 seq_num;
u32 cmd_id;
struct completion cmd_done;
- atomic_t refcnt;
+ refcount_t refcnt;
};
struct isp4_interface {
diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c
index 48deea79ce6c..6716ab9c128a 100644
--- a/drivers/media/platform/amd/isp4/isp4_subdev.c
+++ b/drivers/media/platform/amd/isp4/isp4_subdev.c
@@ -391,7 +391,7 @@ static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev,
if (ele) {
complete(&ele->cmd_done);
- if (atomic_dec_and_test(&ele->refcnt))
+ if (refcount_dec_and_test(&ele->refcnt))
kfree(ele);
}
}
@@ -687,7 +687,7 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
if (ret) {
dev_err(dev, "fail to power on isp_subdev ret %d\n",
ret);
- goto err_deinit;
+ goto err_module_disable;
}
/* ISPPG ISP Power Status */
@@ -697,7 +697,7 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
dev_err(dev,
"fail to set performance state %u, ret %d\n",
perf_state, ret);
- goto err_deinit;
+ goto err_power_off;
}
ispif->status = ISP4IF_STATUS_PWR_ON;
@@ -709,12 +709,12 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
ret = isp4if_start(ispif);
if (ret) {
dev_err(dev, "fail to start isp_subdev interface\n");
- goto err_deinit;
+ goto err_perf_restore;
}
if (isp4sd_start_resp_proc_threads(isp_subdev)) {
dev_err(dev, "isp_start_resp_proc_threads fail\n");
- goto err_deinit;
+ goto err_stop_interface;
}
dev_dbg(dev, "create resp threads ok\n");
@@ -724,8 +724,24 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
isp_subdev->irq_enabled = true;
return 0;
-err_deinit:
- isp4sd_pwroff_and_deinit(sd);
+
+err_stop_interface:
+ isp4if_stop(ispif);
+err_perf_restore:
+ ret = dev_pm_genpd_set_performance_state(dev, ISP4SD_PERFORMANCE_STATE_LOW);
+ if (ret)
+ dev_err(dev, "fail to set performance state %u, ret %d\n",
+ ISP4SD_PERFORMANCE_STATE_LOW, ret);
+err_power_off:
+ isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0);
+ isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0);
+ ret = pm_runtime_put_sync(dev);
+ if (ret)
+ dev_err(dev, "power off isp_subdev fail %d\n", ret);
+ ispif->status = ISP4IF_STATUS_PWR_OFF;
+err_module_disable:
+ isp4sd_module_enable(isp_subdev, false);
+ msleep(20);
return -EINVAL;
}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
index aec3eed0e443..ae0777a20bda 100644
--- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
@@ -535,16 +535,52 @@ static const c3_isp_block_handler c3_isp_params_handlers[] = {
[C3_ISP_PARAMS_BLOCK_BLC] = c3_isp_params_cfg_blc,
};
-#define C3_ISP_PARAMS_BLOCK_INFO(block, data) \
+static int
+c3_isp_params_awb_config_validate(struct device *dev,
+ const struct v4l2_isp_block_header *block)
+{
+ const struct c3_isp_params_awb_config *cfg =
+ (const struct c3_isp_params_awb_config *)block;
+
+ if (cfg->horiz_zones_num * cfg->vert_zones_num > C3_ISP_AWB_MAX_ZONES) {
+ dev_dbg(dev, "Invalid number of AWB measurement zones\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+c3_isp_params_ae_config_validate(struct device *dev,
+ const struct v4l2_isp_block_header *block)
+{
+ const struct c3_isp_params_ae_config *cfg =
+ (const struct c3_isp_params_ae_config *)block;
+
+ if (cfg->horiz_zones_num * cfg->vert_zones_num > C3_ISP_AE_MAX_ZONES) {
+ dev_dbg(dev, "Invalid number of AE measurement zones\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define C3_ISP_PARAMS_BLOCK_INFO_CBK(block, data, cbk) \
[C3_ISP_PARAMS_BLOCK_ ## block] = { \
.size = sizeof(struct c3_isp_params_ ## data), \
+ .block_validate = (cbk)\
}
+#define C3_ISP_PARAMS_BLOCK_INFO(block, data) \
+ C3_ISP_PARAMS_BLOCK_INFO_CBK(block, data, NULL)
+
static const struct v4l2_isp_params_block_type_info
c3_isp_params_block_types_info[] = {
C3_ISP_PARAMS_BLOCK_INFO(AWB_GAINS, awb_gains),
- C3_ISP_PARAMS_BLOCK_INFO(AWB_CONFIG, awb_config),
- C3_ISP_PARAMS_BLOCK_INFO(AE_CONFIG, ae_config),
+ C3_ISP_PARAMS_BLOCK_INFO_CBK(AWB_CONFIG, awb_config,
+ &c3_isp_params_awb_config_validate),
+ C3_ISP_PARAMS_BLOCK_INFO_CBK(AE_CONFIG, ae_config,
+ &c3_isp_params_ae_config_validate),
C3_ISP_PARAMS_BLOCK_INFO(AF_CONFIG, af_config),
C3_ISP_PARAMS_BLOCK_INFO(PST_GAMMA, pst_gamma),
C3_ISP_PARAMS_BLOCK_INFO(CCM, ccm),
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
index 0b3d58b9f2f7..2ba4ae20f829 100644
--- a/drivers/media/platform/amphion/venc.c
+++ b/drivers/media/platform/amphion/venc.c
@@ -973,8 +973,6 @@ static int venc_start_session(struct vpu_inst *inst, u32 type)
venc->ready_count = 0;
venc->stopped = false;
vpu_process_output_buffer(inst);
- if (venc->frame_count == 0)
- dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id);
return 0;
error:
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-capture.c b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c
index 7aaa5c3f7354..ff01553026fb 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-capture.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c
@@ -857,10 +857,8 @@ static int mali_c55_register_cap_dev(struct mali_c55 *mali_c55,
cap_dev->pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&cap_dev->vdev.entity, 1, &cap_dev->pad);
- if (ret) {
- mutex_destroy(&cap_dev->lock);
+ if (ret)
goto err_destroy_mutex;
- }
vb2q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
vb2q->io_modes = VB2_MMAP | VB2_DMABUF;
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
index 94a389b3f833..f28e9f4354ac 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
@@ -698,6 +698,8 @@ static int __mali_c55_power_on(struct mali_c55 *mali_c55)
mali_c55->resets);
if (ret) {
dev_err(mali_c55->dev, "failed to deassert resets\n");
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mali_c55->clks),
+ mali_c55->clks);
return ret;
}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c
index de0e9d898db7..70106276b7e4 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c
@@ -46,6 +46,9 @@
* @awb_config: For header->type == MALI_C55_PARAM_BLOCK_AWB_CONFIG
* @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG
* @shading_selection: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION
+ * @ccm: For header->type == MALI_C55_PARAM_BLOCK_CCM
+ * @gamma: For header->type == MALI_C55_PARAM_BLOCK_GAMMA_FR and
+ * header->type = MALI_C55_PARAM_BLOCK_GAMMA_DS
* @data: Allows easy initialisation of a union variable with a
* pointer into a __u8 array.
*/
@@ -59,6 +62,8 @@ union mali_c55_params_block {
const struct mali_c55_params_awb_config *awb_config;
const struct mali_c55_params_mesh_shading_config *shading_config;
const struct mali_c55_params_mesh_shading_selection *shading_selection;
+ const struct mali_c55_params_ccm *ccm;
+ const struct mali_c55_params_gamma *gamma;
const __u8 *data;
};
@@ -212,6 +217,7 @@ mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55,
val = params->zone_weights[MALI_C55_MAX_ZONES - 1];
addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56);
+ mali_c55_ctx_write(mali_c55, addr, val & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK);
}
static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55,
@@ -414,6 +420,116 @@ static void mali_c55_params_lsc_selection(struct mali_c55 *mali_c55,
params->mesh_strength);
}
+static void mali_c55_params_ccm(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_ccm *params = block.ccm;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0);
+ return;
+ }
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_R_R,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[0][0]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_R_G,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[0][1]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_R_B,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[0][2]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_G_R,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[1][0]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_G_G,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[1][1]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_G_B,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[1][2]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_B_R,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[2][0]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_B_G,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[2][1]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_B_B,
+ MALI_C55_CCM_COEF_MASK, params->coeffs[2][2]);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_GAIN_R,
+ MALI_C55_CCM_ANTIFOG_GAIN_MASK, params->gains[0]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_GAIN_G,
+ MALI_C55_CCM_ANTIFOG_GAIN_MASK, params->gains[1]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_GAIN_B,
+ MALI_C55_CCM_ANTIFOG_GAIN_MASK, params->gains[2]);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_OFFSET_R,
+ MALI_C55_CCM_ANTIFOG_OFFSET_MASK, params->offs[0]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_OFFSET_G,
+ MALI_C55_CCM_ANTIFOG_OFFSET_MASK, params->offs[1]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_OFFSET_B,
+ MALI_C55_CCM_ANTIFOG_OFFSET_MASK, params->offs[2]);
+
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 1);
+}
+
+static void mali_c55_params_gamma(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block,
+ __u32 offset, __u32 lut_base)
+{
+ const struct mali_c55_params_gamma *params = block.gamma;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ mali_c55_ctx_update_bits(mali_c55,
+ MALI_C55_REG_GAMMA_RGB_ENABLE + offset,
+ MALI_C55_GAMMA_ENABLE_MASK, 0x00);
+ return;
+ }
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_GAMMA_GAINS_RG + offset,
+ MALI_C55_GAMMA_GAIN_R_MASK, params->gains[0]);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_GAMMA_GAINS_RG + offset,
+ MALI_C55_GAMMA_GAIN_G_MASK,
+ MALI_C55_GAMMA_GAIN_G(params->gains[1]));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_GAMMA_GAINS_B + offset,
+ MALI_C55_GAMMA_GAIN_B_MASK, params->gains[2]);
+ mali_c55_ctx_update_bits(mali_c55,
+ MALI_C55_REG_GAMMA_OFFSETS_RG + offset,
+ MALI_C55_GAMMA_OFFSET_R_MASK,
+ params->offs[0]);
+ mali_c55_ctx_update_bits(mali_c55,
+ MALI_C55_REG_GAMMA_OFFSETS_RG + offset,
+ MALI_C55_GAMMA_OFFSET_G_MASK,
+ MALI_C55_GAMMA_OFFSET_G(params->offs[1]));
+ mali_c55_ctx_update_bits(mali_c55,
+ MALI_C55_REG_GAMMA_OFFSETS_B + offset,
+ MALI_C55_GAMMA_OFFSET_B_MASK,
+ params->offs[2]);
+
+ for (unsigned int i = 0; i < MALI_C55_NUM_GAMMA_LUT_ELEMENTS; i++) {
+ __u32 addr = lut_base + (i * 4);
+
+ mali_c55_ctx_write(mali_c55, addr, params->lut[i]);
+ }
+
+ mali_c55_ctx_update_bits(mali_c55,
+ MALI_C55_REG_GAMMA_RGB_ENABLE + offset,
+ MALI_C55_GAMMA_ENABLE_MASK, 0x1);
+}
+
+static void mali_c55_params_gamma_fr(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ return mali_c55_params_gamma(mali_c55, block,
+ MALI_C55_CAP_DEV_FR_REG_OFFSET,
+ MALI_C55_REG_FR_GAMMA_RGB_MEM);
+}
+
+static void mali_c55_params_gamma_ds(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ /* We cannot apply parameters to DS if it is not fitted. */
+ if (!(mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED))
+ return;
+
+ return mali_c55_params_gamma(mali_c55, block,
+ MALI_C55_CAP_DEV_DS_REG_OFFSET,
+ MALI_C55_REG_DS_GAMMA_RGB_MEM);
+}
+
static const mali_c55_params_handler mali_c55_params_handlers[] = {
[MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs,
[MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist,
@@ -426,6 +542,9 @@ static const mali_c55_params_handler mali_c55_params_handlers[] = {
[MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = &mali_c55_params_awb_gains,
[MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config,
[MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection,
+ [MALI_C55_PARAM_BLOCK_CCM] = &mali_c55_params_ccm,
+ [MALI_C55_PARAM_BLOCK_GAMMA_FR] = &mali_c55_params_gamma_fr,
+ [MALI_C55_PARAM_BLOCK_GAMMA_DS] = &mali_c55_params_gamma_ds,
};
static const struct v4l2_isp_params_block_type_info
@@ -463,6 +582,15 @@ mali_c55_params_block_types_info[] = {
[MALI_C55_PARAM_MESH_SHADING_SELECTION] = {
.size = sizeof(struct mali_c55_params_mesh_shading_selection),
},
+ [MALI_C55_PARAM_BLOCK_CCM] = {
+ .size = sizeof(struct mali_c55_params_ccm),
+ },
+ [MALI_C55_PARAM_BLOCK_GAMMA_FR] = {
+ .size = sizeof(struct mali_c55_params_gamma),
+ },
+ [MALI_C55_PARAM_BLOCK_GAMMA_DS] = {
+ .size = sizeof(struct mali_c55_params_gamma),
+ },
};
static_assert(ARRAY_SIZE(mali_c55_params_handlers) ==
@@ -487,7 +615,7 @@ static int mali_c55_params_g_fmt_meta_out(struct file *file, void *fh,
{
static const struct v4l2_meta_format mfmt = {
.dataformat = V4L2_META_FMT_MALI_C55_PARAMS,
- .buffersize = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE),
+ .buffersize = v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE),
};
f->fmt.meta = mfmt;
@@ -540,13 +668,13 @@ mali_c55_params_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
if (*num_planes && *num_planes > 1)
return -EINVAL;
- if (sizes[0] && sizes[0] < v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE))
+ if (sizes[0] && sizes[0] < v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE))
return -EINVAL;
*num_planes = 1;
if (!sizes[0])
- sizes[0] = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE);
+ sizes[0] = v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE);
return 0;
}
@@ -556,7 +684,7 @@ static int mali_c55_params_buf_init(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf);
- buf->config = kvmalloc(v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE),
+ buf->config = kvmalloc(v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE),
GFP_KERNEL);
if (!buf->config)
return -ENOMEM;
@@ -583,7 +711,7 @@ static int mali_c55_params_buf_prepare(struct vb2_buffer *vb)
int ret;
ret = v4l2_isp_params_validate_buffer_size(mali_c55->dev, vb,
- v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE));
+ v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE));
if (ret)
return ret;
@@ -593,7 +721,7 @@ static int mali_c55_params_buf_prepare(struct vb2_buffer *vb)
* changed to the buffer content whilst the driver processes it.
*/
- memcpy(buf->config, config, v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE));
+ memcpy(buf->config, config, v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE));
return v4l2_isp_params_validate_buffer(mali_c55->dev, vb, buf->config,
mali_c55_params_block_types_info,
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
index f098effde7b4..8a7c3bfe7051 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
@@ -173,7 +173,7 @@ enum mali_c55_interrupts {
#define MALI_C55_AEXP_HIST_SWITCH_MASK GENMASK(14, 13)
#define MALI_C55_AEXP_HIST_SWITCH(x) ((x) << 13)
#define MALI_C55_AEXP_IHIST_DISABLE_MASK BIT(16)
-#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 12)
+#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 16)
#define MALI_C55_AEXP_SRC_MASK BIT(24)
#define MALI_C55_REG_TPG_CH0 0x18ed8
@@ -422,15 +422,17 @@ enum mali_c55_interrupts {
#define MALI_C55_REG_GAMMA_RGB_ENABLE 0x1c064
#define MALI_C55_GAMMA_ENABLE_MASK BIT(0)
-#define MALI_C55_REG_GAMMA_GAINS_1 0x1c068
+#define MALI_C55_REG_GAMMA_GAINS_RG 0x1c068
#define MALI_C55_GAMMA_GAIN_R_MASK GENMASK(11, 0)
#define MALI_C55_GAMMA_GAIN_G_MASK GENMASK(27, 16)
-#define MALI_C55_REG_GAMMA_GAINS_2 0x1c06c
+#define MALI_C55_GAMMA_GAIN_G(x) ((x) << 16)
+#define MALI_C55_REG_GAMMA_GAINS_B 0x1c06c
#define MALI_C55_GAMMA_GAIN_B_MASK GENMASK(11, 0)
-#define MALI_C55_REG_GAMMA_OFFSETS_1 0x1c070
+#define MALI_C55_REG_GAMMA_OFFSETS_RG 0x1c070
#define MALI_C55_GAMMA_OFFSET_R_MASK GENMASK(11, 0)
#define MALI_C55_GAMMA_OFFSET_G_MASK GENMASK(27, 16)
-#define MALI_C55_REG_GAMMA_OFFSETS_2 0x1c074
+#define MALI_C55_GAMMA_OFFSET_G(x) ((x) << 16)
+#define MALI_C55_REG_GAMMA_OFFSETS_B 0x1c074
#define MALI_C55_GAMMA_OFFSET_B_MASK GENMASK(11, 0)
/*
@@ -441,6 +443,9 @@ enum mali_c55_interrupts {
#define MALI_C55_REG_FR_GAMMA_RGB_ENABLE 0x1c064
#define MALI_C55_REG_DS_GAMMA_RGB_ENABLE 0x1c1d8
+#define MALI_C55_REG_FR_GAMMA_RGB_MEM 0x18280
+#define MALI_C55_REG_DS_GAMMA_RGB_MEM 0x18484
+
#define MALI_C55_REG_FR_SCALER_HFILT 0x34a8
#define MALI_C55_REG_FR_SCALER_VFILT 0x44a8
#define MALI_C55_REG_DS_SCALER_HFILT 0x14a8
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
index c4f46651dcee..6706939b4a90 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
@@ -15,7 +15,7 @@
#include "mali-c55-registers.h"
/* Scaling factor in Q4.20 format. */
-#define MALI_C55_RSZ_SCALER_FACTOR (1U << 20)
+#define MALI_C55_RSZ_SCALER_FACTOR BIT_ULL(20)
#define MALI_C55_RSZ_COEFS_BANKS 8
#define MALI_C55_RSZ_COEFS_ENTRIES 64
diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
index 8d28ba0b59a3..14bb916dd7b1 100644
--- a/drivers/media/platform/broadcom/bcm2835-unicam.c
+++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
@@ -2158,7 +2158,8 @@ static int unicam_video_link_validate(struct media_link *link)
* In order to allow the applications using the old behaviour to
* run, let's accept the old combination, but warn about it.
*/
- if (fmtinfo->fourcc != fmt->pixelformat) {
+ if (fmt->pixelformat != fmtinfo->fourcc &&
+ fmt->pixelformat != fmtinfo->unpacked_fourcc) {
if ((fmt->pixelformat == V4L2_PIX_FMT_BGR24 &&
format->code == MEDIA_BUS_FMT_BGR888_1X24) ||
(fmt->pixelformat == V4L2_PIX_FMT_RGB24 &&
@@ -2613,6 +2614,7 @@ static int unicam_async_nf_init(struct unicam_device *unicam)
return 0;
error:
+ v4l2_async_nf_cleanup(&unicam->notifier);
fwnode_handle_put(ep_handle);
return ret;
}
@@ -2745,6 +2747,7 @@ static void unicam_remove(struct platform_device *pdev)
v4l2_device_unregister(&unicam->v4l2_dev);
media_device_unregister(&unicam->mdev);
v4l2_async_nf_unregister(&unicam->notifier);
+ v4l2_async_nf_cleanup(&unicam->notifier);
unicam_subdev_cleanup(unicam);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index bb2ba9204a83..6564cf3ec739 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -5,6 +5,7 @@
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
*/
+#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include "wave5-helper.h"
@@ -474,7 +475,10 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
- inst->queuing_fail = false;
+ if (inst->queuing_fail) {
+ inst->queuing_fail = false;
+ v4l2_m2m_try_schedule(m2m_ctx);
+ }
}
static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
@@ -819,7 +823,15 @@ static int wave5_vpu_dec_stop(struct vpu_instance *inst)
* calls do not block on a mutex while inside this spinlock.
*/
spin_unlock_irqrestore(&inst->state_spinlock, flags);
+ /*
+ * V4L2_DEC_CMD_STOP can arrive while the device is runtime
+ * suspended (e.g. on pipeline teardown). Setting the EOS flag
+ * accesses VPU registers via send_firmware_command(), so the
+ * device must be resumed first to avoid an asynchronous SError.
+ */
+ pm_runtime_resume_and_get(inst->dev->dev);
ret = wave5_vpu_dec_set_eos_on_firmware(inst);
+ pm_runtime_put_autosuspend(inst->dev->dev);
if (ret)
return ret;
@@ -1403,6 +1415,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
struct dec_initial_info *initial_info =
&inst->codec_info->dec_info.initial_info;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
if (inst->state == VPU_INST_STATE_STOP)
ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
@@ -1410,6 +1423,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
goto return_buffers;
if (inst->state == VPU_INST_STATE_INIT_SEQ &&
+ p_dec_info->initial_info_obtained &&
inst->dev->product_code == WAVE521C_CODE) {
if (initial_info->luma_bitdepth != 8) {
dev_info(inst->dev->dev, "%s: no support for %d bit depth",
@@ -1418,7 +1432,6 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
goto return_buffers;
}
}
-
}
pm_runtime_put_autosuspend(inst->dev->dev);
return ret;
@@ -1536,15 +1549,15 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
{
struct vpu_instance *inst = vb2_get_drv_priv(q);
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
-
- bool check_cmd = TRUE;
+ unsigned long timeout;
dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
pm_runtime_resume_and_get(inst->dev->dev);
inst->empty_queue = true;
- while (check_cmd) {
+
+ timeout = jiffies + msecs_to_jiffies(VPU_DEC_STOP_TIMEOUT);
+ while (true) {
struct queue_status_info q_status;
- struct dec_output_info dec_output_info;
wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
if ((inst->state == VPU_INST_STATE_STOP ||
@@ -1553,8 +1566,10 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
q_status.report_queue_count == 0)
break;
- if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
- dev_dbg(inst->dev->dev, "there is no output info\n");
+ if (time_after(jiffies, timeout))
+ break;
+
+ usleep_range(1000, 2000);
}
v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
@@ -1651,6 +1666,7 @@ static void wave5_vpu_dec_device_run(void *priv)
struct queue_status_info q_status;
u32 fail_res = 0;
int ret = 0;
+ bool cmd_issued = false;
dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
pm_runtime_resume_and_get(inst->dev->dev);
@@ -1662,9 +1678,13 @@ static void wave5_vpu_dec_device_run(void *priv)
} else if (!inst->eos &&
inst->queuing_num == 0 &&
inst->state == VPU_INST_STATE_PIC_RUN) {
- dev_dbg(inst->dev->dev, "%s: no bitstream for feeding, so skip ", __func__);
- inst->empty_queue = true;
- goto finish_job_and_return;
+ wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status);
+ if (q_status.instance_queue_count == v4l2_m2m_num_src_bufs_ready(m2m_ctx)) {
+ dev_dbg(inst->dev->dev, "%s: no bitstream, skip\n",
+ __func__);
+ inst->empty_queue = true;
+ goto finish_job_and_return;
+ }
}
}
@@ -1744,6 +1764,7 @@ static void wave5_vpu_dec_device_run(void *priv)
inst->retry = false;
if (!inst->eos)
inst->queuing_num--;
+ cmd_issued = true;
}
break;
default:
@@ -1761,8 +1782,16 @@ finish_job_and_return:
* in power and CPU time.
* If EOS is passed, device_run will not call job_finish no more, it is called
* only if HW is idle status in order to reduce overhead.
+ *
+ * Deferring job_finish() is only safe when this run actually queued a
+ * DEC_PIC command (cmd_issued): that guarantees a completion IRQ, and
+ * thus a later finish_decode(), will release the shared job slot. When
+ * device_run() is entered with no command to issue (e.g. a job that was
+ * queued while draining but reached the STOP state by the time it ran),
+ * no IRQ follows, so finish the job here to avoid leaking the slot and
+ * stalling every instance sharing the VPU.
*/
- if (!inst->sent_eos)
+ if (!inst->sent_eos || !cmd_issued)
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
@@ -1776,11 +1805,22 @@ static void wave5_vpu_dec_job_abort(void *priv)
if (ret)
return;
+ /*
+ * job_abort() runs from the STREAMOFF path and may be called while the
+ * device is runtime suspended. Setting the EOS flag talks to the
+ * firmware (send_firmware_command() accesses VPU registers), so the
+ * device must be resumed first; otherwise the register access faults
+ * with an asynchronous SError.
+ */
+ pm_runtime_resume_and_get(inst->dev->dev);
+
ret = wave5_vpu_dec_set_eos_on_firmware(inst);
if (ret)
dev_warn(inst->dev->dev,
"Setting EOS for the bitstream, fail: %d\n", ret);
+ pm_runtime_put_autosuspend(inst->dev->dev);
+
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
index e6c94b6f2671..f9fcdf4c224b 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -1494,7 +1494,8 @@ static const struct vb2_ops wave5_vpu_enc_vb2_ops = {
.stop_streaming = wave5_vpu_enc_stop_streaming,
};
-static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
+static void wave5_set_default_format(struct vpu_instance *inst,
+ struct v4l2_pix_format_mplane *src_fmt,
struct v4l2_pix_format_mplane *dst_fmt)
{
src_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
@@ -1506,6 +1507,7 @@ static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_CODEC,
W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT,
&enc_frmsize[VPU_FMT_TYPE_CODEC]);
+ inst->std = wave5_to_vpu_std(dst_fmt->pixelformat, inst->type);
}
static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
@@ -1770,7 +1772,7 @@ static int wave5_vpu_open_enc(struct file *filp)
inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl;
v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
- wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
+ wave5_set_default_format(inst, &inst->src_fmt, &inst->dst_fmt);
inst->conf_win.width = inst->dst_fmt.width;
inst->conf_win.height = inst->dst_fmt.height;
inst->colorspace = V4L2_COLORSPACE_REC709;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
index 4ebd48d5550e..e04f2dbf3b65 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
@@ -59,7 +59,7 @@
// application specific configuration
#define VPU_ENC_TIMEOUT 60000
#define VPU_DEC_TIMEOUT 60000
-#define VPU_DEC_STOP_TIMEOUT 10
+#define VPU_DEC_STOP_TIMEOUT 300
// for WAVE encoder
#define USE_SRC_PRP_AXI 0
diff --git a/drivers/media/platform/dreamchip/Kconfig b/drivers/media/platform/dreamchip/Kconfig
new file mode 100644
index 000000000000..d177d4ee79ae
--- /dev/null
+++ b/drivers/media/platform/dreamchip/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/dreamchip/rppx1/Kconfig"
diff --git a/drivers/media/platform/dreamchip/Makefile b/drivers/media/platform/dreamchip/Makefile
new file mode 100644
index 000000000000..ba47ba2d136e
--- /dev/null
+++ b/drivers/media/platform/dreamchip/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Dreamchip device drivers.
+#
+
+obj-y += rppx1/
diff --git a/drivers/media/platform/dreamchip/rppx1/Kconfig b/drivers/media/platform/dreamchip/rppx1/Kconfig
new file mode 100644
index 000000000000..0998a7d10bf2
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+config VIDEO_DCT_RPPX1
+ tristate
+ depends on V4L_PLATFORM_DRIVERS
+ select V4L2_ISP
+ help
+ Support library for Dreamchip HDR RPP X1 High Dynamic Range Real-time
+ Pixel Processor (RPP). The library can be used by other drivers who
+ utilises the RPP as part of an ISP implementation.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rppx1.
diff --git a/drivers/media/platform/dreamchip/rppx1/Makefile b/drivers/media/platform/dreamchip/rppx1/Makefile
new file mode 100644
index 000000000000..b2bd6b5d68bc
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/Makefile
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
+dct-rpp-x1-objs = \
+ rpp_module.o \
+ rpp_params.o \
+ rpp_stats.o \
+ rppx1.o \
+ rppx1_acq.o \
+ rppx1_awbg.o \
+ rppx1_bd.o \
+ rppx1_bdrgb.o \
+ rppx1_bls.o \
+ rppx1_cac.o \
+ rppx1_ccor.o \
+ rppx1_db.o \
+ rppx1_dpcc.o \
+ rppx1_exm.o \
+ rppx1_ga.o \
+ rppx1_hist.o \
+ rppx1_hist256.o \
+ rppx1_is.o \
+ rppx1_lin.o \
+ rppx1_lsc.o \
+ rppx1_ltm.o \
+ rppx1_ltmmeas.o \
+ rppx1_outif.o \
+ rppx1_outregs.o \
+ rppx1_rmap.o \
+ rppx1_rmapmeas.o \
+ rppx1_shrp.o \
+ rppx1_wbmeas.o \
+ rppx1_xyz2luv.o
+
+obj-$(CONFIG_VIDEO_DCT_RPPX1) += dct-rpp-x1.o
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.c b/drivers/media/platform/dreamchip/rppx1/rpp_module.c
new file mode 100644
index 000000000000..cb3e5a33d68c
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rppx1.h"
+#include "rpp_module.h"
+
+int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp,
+ const struct rpp_module_ops *ops, u32 base)
+{
+ mod->rpp = rpp;
+ mod->base = base;
+ mod->ops = ops;
+
+ if (ops->probe)
+ return ops->probe(mod);
+
+ return 0;
+}
+
+void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value)
+{
+ rppx1_write(mod->rpp, mod->base + offset, value);
+}
+
+u32 rpp_module_read(struct rpp_module *mod, u32 offset)
+{
+ return rppx1_read(mod->rpp, mod->base + offset);
+}
+
+void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value)
+{
+ u32 reg = rpp_module_read(mod, offset) & ~mask;
+
+ rpp_module_write(mod, offset, reg | value);
+}
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
new file mode 100644
index 000000000000..072309d8662d
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#ifndef __RPPX1_MODULE_H__
+#define __RPPX1_MODULE_H__
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <linux/media/dreamchip/rppx1-config.h>
+
+#include <media/rppx1.h>
+
+struct rpp_module_ops;
+
+enum rpp_raw_pattern {
+ RPP_RGGB = 0,
+ RPP_GRBG,
+ RPP_GBRG,
+ RPP_BGGR,
+};
+
+struct rpp_module {
+ struct rppx1 *rpp;
+ u32 base;
+
+ const struct rpp_module_ops *ops;
+
+ union {
+ struct {
+ enum rpp_raw_pattern raw_pattern;
+ } acq;
+ } info;
+};
+
+int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp,
+ const struct rpp_module_ops *ops, u32 base);
+
+void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value);
+u32 rpp_module_read(struct rpp_module *mod, u32 offset);
+void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value);
+
+union rppx1_params_block {
+ struct v4l2_isp_block_header header;
+ struct rppx1_bls_params bls;
+ struct rppx1_lin_params lin;
+ struct rppx1_lsc_params lsc;
+ struct rppx1_awbg_params awbg;
+ struct rppx1_ccor_params ccor;
+ struct rppx1_hist_params hist;
+ struct rppx1_exm_params exm;
+ struct rppx1_wbmeas_params wbmeas;
+ struct rppx1_ga_params ga;
+};
+
+union rppx1_stats_block {
+ struct v4l2_isp_block_header header;
+ struct rppx1_hist_stats hist;
+ struct rppx1_exm_stats exm;
+ struct rppx1_wbmeas_stats wbmeas;
+};
+
+struct rpp_module_ops {
+ int (*probe)(struct rpp_module *mod);
+ int (*start)(struct rpp_module *mod, const struct v4l2_mbus_framefmt *fmt);
+
+ int (*fill_params)(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv);
+ int (*fill_stats)(struct rpp_module *mod,
+ union rppx1_stats_block *block);
+};
+
+extern const struct rpp_module_ops rppx1_acq_ops;
+extern const struct rpp_module_ops rppx1_awbg_ops;
+extern const struct rpp_module_ops rppx1_bd_ops;
+extern const struct rpp_module_ops rppx1_bdrgb_ops;
+extern const struct rpp_module_ops rppx1_bls_ops;
+extern const struct rpp_module_ops rppx1_cac_ops;
+extern const struct rpp_module_ops rppx1_ccor_ops;
+extern const struct rpp_module_ops rppx1_ccor_csm_ops;
+extern const struct rpp_module_ops rppx1_db_ops;
+extern const struct rpp_module_ops rppx1_dpcc_ops;
+extern const struct rpp_module_ops rppx1_exm_ops;
+extern const struct rpp_module_ops rppx1_ga_ops;
+extern const struct rpp_module_ops rppx1_hist256_ops;
+extern const struct rpp_module_ops rppx1_hist_ops;
+extern const struct rpp_module_ops rppx1_is_ops;
+extern const struct rpp_module_ops rppx1_lin_ops;
+extern const struct rpp_module_ops rppx1_lsc_ops;
+extern const struct rpp_module_ops rppx1_ltm_ops;
+extern const struct rpp_module_ops rppx1_ltmmeas_ops;
+extern const struct rpp_module_ops rppx1_outif_ops;
+extern const struct rpp_module_ops rppx1_outregs_ops;
+extern const struct rpp_module_ops rppx1_rmapmeas_ops;
+extern const struct rpp_module_ops rppx1_rmap_ops;
+extern const struct rpp_module_ops rppx1_shrp_ops;
+extern const struct rpp_module_ops rppx1_wbmeas_ops;
+extern const struct rpp_module_ops rppx1_xyz2luv_ops;
+
+#define rpp_module_call(mod, op, args...) \
+ ({ \
+ struct rpp_module *__mod = (mod); \
+ int __result; \
+ if (!__mod) \
+ __result = -ENODEV; \
+ else if (!__mod->ops->op) \
+ __result = 0; \
+ else \
+ __result = __mod->ops->op(__mod, ##args); \
+ __result; \
+ })
+
+#endif /* __RPPX1_MODULE_H__ */
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
new file mode 100644
index 000000000000..a75a27a8afd0
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include <media/v4l2-isp.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "rppx1.h"
+
+#define RPPX1_PARAMS_BLOCK_INFO(block, data) \
+ [RPPX1_PARAMS_BLOCK_TYPE_ ## block] = { \
+ .size = sizeof(struct rppx1_ ## data ## _params), \
+ }
+
+static const struct v4l2_isp_params_block_type_info
+rppx1_ext_params_blocks_info[] = {
+ RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls),
+ RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls),
+ RPPX1_PARAMS_BLOCK_INFO(LIN_PRE1, lin),
+ RPPX1_PARAMS_BLOCK_INFO(LIN_PRE2, lin),
+ RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc),
+ RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc),
+ RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg),
+ RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor),
+ RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist),
+ RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist),
+ RPPX1_PARAMS_BLOCK_INFO(HIST_POST, hist),
+ RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm),
+ RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm),
+ RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas),
+ RPPX1_PARAMS_BLOCK_INFO(GA_HV, ga),
+ RPPX1_PARAMS_BLOCK_INFO(GA_MV, ga),
+};
+
+int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
+ rppx1_reg_write write, void *priv)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct v4l2_isp_buffer *cfg;
+ size_t block_offset;
+ int ret;
+
+ ret = v4l2_isp_params_validate_buffer_size(rpp->dev, vb, max_size);
+ if (ret)
+ return ret;
+
+ cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+
+ ret = v4l2_isp_params_validate_buffer(rpp->dev, vb, cfg,
+ rppx1_ext_params_blocks_info,
+ ARRAY_SIZE(rppx1_ext_params_blocks_info));
+ if (ret)
+ return ret;
+
+ /* Walk the list of parameter blocks and process them. */
+ block_offset = 0;
+ while (block_offset < cfg->data_size) {
+ const union rppx1_params_block *block =
+ (const union rppx1_params_block *)&cfg->data[block_offset];
+ struct rpp_module *module;
+ int ret;
+
+ block_offset += block->header.size;
+
+ switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
+ module = &rpp->pre1.bls;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1:
+ module = &rpp->pre1.lin;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1:
+ module = &rpp->pre1.lsc;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1:
+ module = &rpp->pre1.awbg;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST:
+ module = &rpp->post.ccor;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_HIST_POST:
+ module = &rpp->post.hist;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1:
+ module = &rpp->pre1.exm;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST:
+ module = &rpp->post.wbmeas;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_GA_HV:
+ module = &rpp->hv.ga;
+ break;
+ default:
+ dev_warn(rpp->dev,
+ "Not handled RPPX1 block type: 0x%04x\n",
+ block->header.type);
+ continue;
+ }
+
+ ret = rpp_module_call(module, fill_params, block, write, priv);
+ if (ret) {
+ dev_err(rpp->dev,
+ "Error processing RPPX1 block type: 0x%04x\n",
+ block->header.type);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rppx1_params);
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
new file mode 100644
index 000000000000..4c7fe611d004
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rppx1.h"
+#include "rpp_module.h"
+
+#include <media/v4l2-isp.h>
+
+#define RPPX1_STATS_BLOCK_INFO(type, block) \
+ [RPPX1_STATS_BLOCK_TYPE_ ## type] = { \
+ .size = sizeof(struct rppx1_ ## block ## _stats), \
+ }
+
+static const struct v4l2_isp_stats_block_type_info
+rppx1_stats_blocks_info[] = {
+ RPPX1_STATS_BLOCK_INFO(HIST_POST, hist),
+ RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm),
+ RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas),
+};
+
+#define rppx1_init_stats_block(rpp, buf, type) \
+ ((union rppx1_stats_block *) \
+ v4l2_isp_stats_init_block((rpp)->dev, (buf), \
+ rppx1_stats_blocks_info, \
+ ARRAY_SIZE(rppx1_stats_blocks_info), \
+ (type), RPPX1_STATS_MAX_SIZE)) \
+
+void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
+{
+ struct v4l2_isp_buffer *stats = buf;
+ union rppx1_stats_block *block;
+
+ v4l2_isp_stats_init_buffer(stats, V4L2_ISP_VERSION_V1);
+
+ if (isc & RPPX1_IRQ_ID_POST_HIST_MEAS) {
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_HIST_POST);
+ if (IS_ERR(block))
+ return;
+
+ rpp_module_call(&rpp->post.hist, fill_stats, block);
+ }
+
+ if (isc & RPPX1_IRQ_ID_PRE1_EXM) {
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_EXM_PRE1);
+ if (IS_ERR(block))
+ return;
+
+ rpp_module_call(&rpp->pre1.exm, fill_stats, block);
+ }
+
+ if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) {
+ block = rppx1_init_stats_block(rpp, stats,
+ RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST);
+ if (IS_ERR(block))
+ return;
+
+ rpp_module_call(&rpp->post.wbmeas, fill_stats, block);
+ }
+}
+EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.c b/drivers/media/platform/dreamchip/rppx1/rppx1.c
new file mode 100644
index 000000000000..c502be797914
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ *
+ * Support library for Dreamchip HDR RPPX1 High Dynamic Range Real-time Pixel
+ * Processor.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "rppx1.h"
+
+/* RPP_HDR Base Addresses */
+#define RPPX1_HDRREGS_BASE 0x0000
+#define RPPX1_HDR_IRQ_BASE 0x0200
+#define RPPX1_RPP_OUT_BASE 0x0800
+#define RPPX1_RPP_RMAP_BASE 0x0c00
+#define RPPX1_RPP_RMAP_MEAS_BASE 0x1000
+#define RPPX1_RPP_MAIN_PRE1_BASE 0x2000
+#define RPPX1_RPP_MAIN_PRE2_BASE 0x4000
+#define RPPX1_RPP_MAIN_POST_BASE 0xa000
+#define RPPX1_RPP_MVOUT_BASE 0xc000
+#define RPPX1_RPP_FUSA_BASE 0xf000
+
+#define RPPX1_RPP_HDRREGS_VERSION_REG (RPPX1_HDRREGS_BASE + 0x0000)
+
+#define RPPX1_RPP_HDR_UPD_REG (RPPX1_HDRREGS_BASE + 0x0004)
+#define RPPX1_RPP_HDR_UPD_REGS_GEN_CFG_UPD BIT(1)
+#define RPPX1_RPP_HDR_UPD_REGS_CFG_UPD BIT(0)
+
+#define RPPX1_RESERVED_3_REG (RPPX1_HDRREGS_BASE + 0x0008)
+
+#define RPPX1_RPP_HDR_INFORM_ENABLE_REG (RPPX1_HDRREGS_BASE + 0x000c)
+#define RPPX1_RPP_HDR_INFORM_ENABLE_ENABLE 1
+#define RPPX1_RPP_HDR_INFORM_ENABLE_DISABLE 0
+
+#define RPPX1_RPP_HDR_OUT_IF_ON_REG (RPPX1_HDRREGS_BASE + 0x0010)
+#define RPPX1_RPP_HDR_OUT_IF_OFF_REG (RPPX1_HDRREGS_BASE + 0x0014)
+
+#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG (RPPX1_HDRREGS_BASE + 0x0018)
+#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_ENABLE 1
+#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_DISABLE 0
+
+#define RPPX1_RPP_ISM (RPPX1_HDR_IRQ_BASE + 0x00)
+#define RPPX1_RPP_RIS (RPPX1_HDR_IRQ_BASE + 0x04)
+#define RPPX1_RPP_MIS (RPPX1_HDR_IRQ_BASE + 0x08)
+#define RPPX1_RPP_ISC (RPPX1_HDR_IRQ_BASE + 0x0c)
+
+/* RPP_OUT/MV_OUT Pipelines - Base Addresses */
+#define RPPX1_GAMMA_OUT_BASE 0x0000 /* HV, MV */
+#define RPPX1_IS_BASE 0x00c0 /* HV, MV */
+#define RPPX1_CSM_BASE 0x0100 /* HV, MV */
+#define RPPX1_OUT_IF_BASE 0x0200 /* HV, MV */
+#define RPPX1_RPP_OUTREGS_BASE 0x02c0 /* HV, MV */
+#define RPPX1_LUV_BASE 0x0300 /* MV */
+
+/* PRE1/PRE2/POST Pipelines - Base Addresses */
+#define RPPX1_ACQ_BASE 0x0080 /* PRE1, PRE2 */
+#define RPPX1_BLS_BASE 0x0100 /* PRE1, PRE2 */
+#define RPPX1_GAMMA_IN_BASE 0x0200 /* PRE1, PRE2 */
+#define RPPX1_LSC_BASE 0x0400 /* PRE1, PRE2 */
+#define RPPX1_AWB_GAIN_BASE 0x0500 /* PRE1, PRE2, POST */
+#define RPPX1_DPCC_BASE 0x0600 /* PRE1, PRE2 */
+#define RPPX1_DPF_BASE 0x0700 /* PRE1, PRE2 */
+#define RPPX1_FILT_BASE 0x0800 /* POST */
+#define RPPX1_CAC_BASE 0x0880 /* POST */
+#define RPPX1_CCOR_BASE 0x0900 /* POST */
+#define RPPX1_HIST_BASE 0x0a00 /* PRE1, PRE2, POST */
+#define RPPX1_HIST256_BASE 0x0b00 /* PRE1 */
+#define RPPX1_EXM_BASE 0x0c00 /* PRE1, PRE2 */
+#define RPPX1_LTM_BASE 0x1000 /* POST */
+#define RPPX1_LTM_MEAS_BASE 0x1200 /* POST */
+#define RPPX1_WBMEAS_BASE 0x1700 /* POST */
+#define RPPX1_BDRGB_BASE 0x1800 /* POST */
+#define RPPX1_SHRP_BASE 0x1a00 /* POST */
+
+/* Functional Safety Module Base Addresses */
+#define RPPX1_FMU_BASE 0x0100
+
+#define RPPX1_RPP_HDR_FMU_FSM_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x00)
+#define RPPX1_RPP_HDR_FMU_FSM_FSM_IRQM_FAULT BIT(23)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE2_SIZE_FAULT BIT(20)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE2_TIME_FAULT BIT(19)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE1_SIZE_FAULT BIT(18)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE1_TIME_FAULT BIT(17)
+#define RPPX1_RPP_HDR_FMU_FSM_SIZE_FAULT BIT(16)
+#define RPPX1_RPP_HDR_FMU_FSM_TIME_FAULT BIT(15)
+#define RPPX1_RPP_HDR_FMU_FSM_MV_OUT_SIZE_FAULT BIT(14)
+#define RPPX1_RPP_HDR_FMU_FSM_MV_OUT_TIME_FAULT BIT(13)
+#define RPPX1_RPP_HDR_FMU_FSM_HV_OUT_SIZE_FAULT BIT(12)
+#define RPPX1_RPP_HDR_FMU_FSM_HV_OUT_TIME_FAULT BIT(11)
+#define RPPX1_RPP_HDR_FMU_FSM_MV_OUT_SIZE_ERR BIT(10)
+#define RPPX1_RPP_HDR_FMU_FSM_IS_OUT_SIZE_ERR BIT(9)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE2_FIFO_OVFLW BIT(7)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE1_FIFO_OVFLW BIT(6)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE1_INFORM_SIZE BIT(5)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE1_OUTFORM_SIZE BIT(4)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE2_INFORM_SIZE BIT(3)
+#define RPPX1_RPP_HDR_FMU_FSM_PRE2_OUTFORM_SIZE BIT(2)
+
+#define RPPX1_RPP_HDR_FMU_RFS_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x04)
+#define RPPX1_RPP_HDR_FMU_MFS_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x08)
+#define RPPX1_RPP_HDR_FMU_FSC_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x0c)
+
+void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value)
+{
+ iowrite32(value, rpp->base + offset);
+}
+
+u32 rppx1_read(struct rppx1 *rpp, u32 offset)
+{
+ return ioread32(rpp->base + offset);
+}
+
+bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc)
+{
+ u32 status, raw, fault;
+
+ fault = rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS_REG);
+ if (fault) {
+ dev_err(rpp->dev, "%s: fault 0x%08x\n", __func__, fault);
+ rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC_REG, fault);
+ }
+
+ /* Read raw interrupt status. */
+ raw = rppx1_read(rpp, RPPX1_RPP_RIS);
+ status = rppx1_read(rpp, RPPX1_RPP_MIS);
+
+ /* Propagate the isc status. */
+ if (isc)
+ *isc = status | raw;
+
+ /* Clear enabled interrupts */
+ rppx1_write(rpp, RPPX1_RPP_ISC, status);
+
+ return !!(status & RPPX1_IRQ_ID_OUT_FRAME);
+}
+EXPORT_SYMBOL_GPL(rppx1_interrupt);
+
+void rppx1_destroy(struct rppx1 *rpp)
+{
+ kfree(rpp);
+}
+EXPORT_SYMBOL_GPL(rppx1_destroy);
+
+/*
+ * Allocate the private data structure and verify the hardware is present.
+ */
+struct rppx1 *rppx1_create(void __iomem *base, struct device *dev)
+{
+ struct rppx1 *rpp;
+ u32 reg;
+
+ /* Allocate library structure */
+ rpp = kzalloc_obj(*rpp);
+ if (!rpp)
+ return NULL;
+
+ rpp->base = base;
+ rpp->dev = dev;
+
+ /* Check communication with RPP and verify it truly is a X1. */
+ reg = rppx1_read(rpp, RPPX1_RPP_HDRREGS_VERSION_REG);
+ if (reg != 3) {
+ dev_err(rpp->dev, "Unsupported HDR version (%u)\n", reg);
+ rppx1_destroy(rpp);
+ return NULL;
+ }
+
+ /* Probe the PRE1 pipeline. */
+ if (rpp_module_probe(&rpp->pre1.acq, rpp, &rppx1_acq_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_ACQ_BASE) ||
+ rpp_module_probe(&rpp->pre1.bls, rpp, &rppx1_bls_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_BLS_BASE) ||
+ rpp_module_probe(&rpp->pre1.lin, rpp, &rppx1_lin_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_GAMMA_IN_BASE) ||
+ rpp_module_probe(&rpp->pre1.lsc, rpp, &rppx1_lsc_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_LSC_BASE) ||
+ rpp_module_probe(&rpp->pre1.awbg, rpp, &rppx1_awbg_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_AWB_GAIN_BASE) ||
+ rpp_module_probe(&rpp->pre1.dpcc, rpp, &rppx1_dpcc_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPCC_BASE) ||
+ rpp_module_probe(&rpp->pre1.bd, rpp, &rppx1_bd_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPF_BASE) ||
+ rpp_module_probe(&rpp->pre1.hist, rpp, &rppx1_hist_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST_BASE) ||
+ rpp_module_probe(&rpp->pre1.hist256, rpp, &rppx1_hist256_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST256_BASE) ||
+ rpp_module_probe(&rpp->pre1.exm, rpp, &rppx1_exm_ops,
+ RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_EXM_BASE))
+ goto err;
+
+ /* Probe the PRE2 pipeline. */
+ if (rpp_module_probe(&rpp->pre2.acq, rpp, &rppx1_acq_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_ACQ_BASE) ||
+ rpp_module_probe(&rpp->pre2.bls, rpp, &rppx1_bls_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_BLS_BASE) ||
+ rpp_module_probe(&rpp->pre2.lin, rpp, &rppx1_lin_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_GAMMA_IN_BASE) ||
+ rpp_module_probe(&rpp->pre2.lsc, rpp, &rppx1_lsc_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_LSC_BASE) ||
+ rpp_module_probe(&rpp->pre2.awbg, rpp, &rppx1_awbg_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_AWB_GAIN_BASE) ||
+ rpp_module_probe(&rpp->pre2.dpcc, rpp, &rppx1_dpcc_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPCC_BASE) ||
+ rpp_module_probe(&rpp->pre2.bd, rpp, &rppx1_bd_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPF_BASE) ||
+ rpp_module_probe(&rpp->pre2.hist, rpp, &rppx1_hist_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_HIST_BASE) ||
+ rpp_module_probe(&rpp->pre2.exm, rpp, &rppx1_exm_ops,
+ RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_EXM_BASE))
+ goto err;
+
+ /* Probe the POST pipeline. */
+ if (rpp_module_probe(&rpp->post.awbg, rpp, &rppx1_awbg_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_AWB_GAIN_BASE) ||
+ rpp_module_probe(&rpp->post.ccor, rpp, &rppx1_ccor_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_CCOR_BASE) ||
+ rpp_module_probe(&rpp->post.hist, rpp, &rppx1_hist_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_HIST_BASE) ||
+ rpp_module_probe(&rpp->post.db, rpp, &rppx1_db_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_FILT_BASE) ||
+ rpp_module_probe(&rpp->post.cac, rpp, &rppx1_cac_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_CAC_BASE) ||
+ rpp_module_probe(&rpp->post.ltm, rpp, &rppx1_ltm_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_BASE) ||
+ rpp_module_probe(&rpp->post.ltmmeas, rpp, &rppx1_ltmmeas_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_MEAS_BASE) ||
+ rpp_module_probe(&rpp->post.wbmeas, rpp, &rppx1_wbmeas_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_WBMEAS_BASE) ||
+ rpp_module_probe(&rpp->post.bdrgb, rpp, &rppx1_bdrgb_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_BDRGB_BASE) ||
+ rpp_module_probe(&rpp->post.shrp, rpp, &rppx1_shrp_ops,
+ RPPX1_RPP_MAIN_POST_BASE + RPPX1_SHRP_BASE))
+ goto err;
+
+ /* Probe the Human Vision pipeline. */
+ if (rpp_module_probe(&rpp->hv.ga, rpp, &rppx1_ga_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_GAMMA_OUT_BASE) ||
+ rpp_module_probe(&rpp->hv.is, rpp, &rppx1_is_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_IS_BASE) ||
+ rpp_module_probe(&rpp->hv.ccor, rpp, &rppx1_ccor_csm_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_CSM_BASE) ||
+ rpp_module_probe(&rpp->hv.outif, rpp, &rppx1_outif_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_OUT_IF_BASE) ||
+ rpp_module_probe(&rpp->hv.outregs, rpp, &rppx1_outregs_ops,
+ RPPX1_RPP_OUT_BASE + RPPX1_RPP_OUTREGS_BASE))
+ goto err;
+
+ /* Probe the Machine Vision pipeline. */
+ if (rpp_module_probe(&rpp->mv.ga, rpp, &rppx1_ga_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_GAMMA_OUT_BASE) ||
+ rpp_module_probe(&rpp->mv.is, rpp, &rppx1_is_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_IS_BASE) ||
+ rpp_module_probe(&rpp->mv.ccor, rpp, &rppx1_ccor_csm_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_CSM_BASE) ||
+ rpp_module_probe(&rpp->mv.outif, rpp, &rppx1_outif_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_OUT_IF_BASE) ||
+ rpp_module_probe(&rpp->mv.outregs, rpp, &rppx1_outregs_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_RPP_OUTREGS_BASE) ||
+ rpp_module_probe(&rpp->mv.xyz2luv, rpp, &rppx1_xyz2luv_ops,
+ RPPX1_RPP_MVOUT_BASE + RPPX1_LUV_BASE))
+ goto err;
+
+ /* Probe the standalone Radiance Mapping modules. */
+ if (rpp_module_probe(&rpp->rmap, rpp, &rppx1_rmap_ops,
+ RPPX1_RPP_RMAP_BASE) ||
+ rpp_module_probe(&rpp->rmapmeas, rpp, &rppx1_rmapmeas_ops,
+ RPPX1_RPP_RMAP_MEAS_BASE))
+ goto err;
+
+ return rpp;
+err:
+ rppx1_destroy(rpp);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rppx1_create);
+
+int rppx1_start(struct rppx1 *rpp,
+ const struct v4l2_mbus_framefmt *input,
+ const struct v4l2_mbus_framefmt *hv,
+ const struct v4l2_mbus_framefmt *mv)
+{
+ if (rpp_module_call(&rpp->pre1.acq, start, input) ||
+ rpp_module_call(&rpp->pre1.bls, start, input) ||
+ rpp_module_call(&rpp->pre1.lin, start, input) ||
+ rpp_module_call(&rpp->pre1.lsc, start, input) ||
+ rpp_module_call(&rpp->pre1.awbg, start, input) ||
+ rpp_module_call(&rpp->pre1.dpcc, start, input) ||
+ rpp_module_call(&rpp->pre1.bd, start, input) ||
+ rpp_module_call(&rpp->pre1.hist, start, input) ||
+ rpp_module_call(&rpp->pre1.exm, start, input) ||
+ rpp_module_call(&rpp->pre1.hist256, start, input))
+ return -EINVAL;
+
+ if (rpp_module_call(&rpp->rmap, start, NULL) ||
+ rpp_module_call(&rpp->rmapmeas, start, NULL))
+ return -EINVAL;
+
+ if (rpp_module_call(&rpp->post.awbg, start, input) ||
+ rpp_module_call(&rpp->post.db, start, input) ||
+ rpp_module_call(&rpp->post.cac, start, input) ||
+ rpp_module_call(&rpp->post.ccor, start, input) ||
+ rpp_module_call(&rpp->post.ltm, start, input) ||
+ rpp_module_call(&rpp->post.bdrgb, start, input) ||
+ rpp_module_call(&rpp->post.shrp, start, input) ||
+ rpp_module_call(&rpp->post.ltmmeas, start, input) ||
+ rpp_module_call(&rpp->post.wbmeas, start, input) ||
+ rpp_module_call(&rpp->post.hist, start, input))
+ return -EINVAL;
+
+ if (hv && (rpp_module_call(&rpp->hv.ga, start, hv) ||
+ rpp_module_call(&rpp->hv.ccor, start, hv) ||
+ rpp_module_call(&rpp->hv.outregs, start, hv) ||
+ rpp_module_call(&rpp->hv.is, start, hv) ||
+ rpp_module_call(&rpp->hv.outif, start, hv)))
+ return -EINVAL;
+
+ if (mv && (rpp_module_call(&rpp->mv.ga, start, mv) ||
+ rpp_module_call(&rpp->mv.ccor, start, mv) ||
+ rpp_module_call(&rpp->mv.xyz2luv, start, mv) ||
+ rpp_module_call(&rpp->mv.outregs, start, mv) ||
+ rpp_module_call(&rpp->mv.is, start, mv) ||
+ rpp_module_call(&rpp->mv.outif, start, mv)))
+ return -EINVAL;
+
+ /* Immediate update for shadows. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, RPPX1_RPP_HDR_UPD_REGS_CFG_UPD);
+
+ /* Clear fault interrupts. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG,
+ RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_ENABLE);
+ rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSM_REG,
+ RPPX1_RPP_HDR_FMU_FSM_PRE2_FIFO_OVFLW |
+ RPPX1_RPP_HDR_FMU_FSM_PRE1_FIFO_OVFLW);
+ rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC_REG,
+ rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS_REG));
+ rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG,
+ RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_DISABLE);
+
+ /* Set interrupt mask. */
+ rppx1_write(rpp, RPPX1_RPP_ISM, RPPX1_IRQ_ID_OUT_FRAME);
+
+ /* Immediate commit update for shadows. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, RPPX1_RPP_HDR_UPD_REGS_CFG_UPD);
+
+ /* Then for operation update shadows with picture synchronization. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, RPPX1_RPP_HDR_UPD_REGS_GEN_CFG_UPD);
+
+ /* Clear any pending interrupts. */
+ rppx1_interrupt(rpp, NULL);
+
+ /* Enable input formatters. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG,
+ RPPX1_RPP_HDR_INFORM_ENABLE_ENABLE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rppx1_start);
+
+int rppx1_stop(struct rppx1 *rpp)
+{
+ /* Disable input formatters. */
+ rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG,
+ RPPX1_RPP_HDR_INFORM_ENABLE_DISABLE);
+
+ /* Clear any pending interrupts. */
+ rppx1_interrupt(rpp, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rppx1_stop);
+
+MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
+MODULE_DESCRIPTION("Dreamchip HDR RPPX1 support library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.h b/drivers/media/platform/dreamchip/rppx1/rppx1.h
new file mode 100644
index 000000000000..3bf3955afbae
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#ifndef __MEDIA_RPPX1_H__
+#define __MEDIA_RPPX1_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include "rpp_module.h"
+
+#define RPPX1_IRQ_ID_256HIST BIT(27)
+#define RPPX1_IRQ_ID_PRE2_DPCC BIT(25)
+#define RPPX1_IRQ_ID_PRE1_DPCC BIT(24)
+#define RPPX1_IRQ_ID_MV_OUT_FRAME_OUT BIT(23)
+#define RPPX1_IRQ_ID_MV_OUT_OFF BIT(22)
+#define RPPX1_IRQ_ID_POST_AWB_MEAS BIT(21)
+#define RPPX1_IRQ_ID_POST_HIST_MEAS BIT(20)
+#define RPPX1_IRQ_ID_POST_TM BIT(19)
+#define RPPX1_IRQ_ID_PRE1_EXM BIT(18)
+#define RPPX1_IRQ_ID_PRE1_HIST BIT(17)
+#define RPPX1_IRQ_ID_PRE1_FRAME_IN BIT(16)
+#define RPPX1_IRQ_ID_PRE1_HSTART BIT(15)
+#define RPPX1_IRQ_ID_PRE1_VSTART BIT(14)
+#define RPPX1_IRQ_ID_PRE2_EXM BIT(13)
+#define RPPX1_IRQ_ID_PRE2_HIST BIT(12)
+#define RPPX1_IRQ_ID_PRE2_FRAME_IN BIT(11)
+#define RPPX1_IRQ_ID_PRE2_HSTART BIT(10)
+#define RPPX1_IRQ_ID_PRE2_VSTART BIT(9)
+#define RPPX1_IRQ_ID_OUT_FRAME BIT(3)
+#define RPPX1_IRQ_ID_OUT_OFF BIT(2)
+#define RPPX1_IRQ_ID_RMAP_MEAS BIT(1)
+#define RPPX1_IRQ_ID_RMAP_DONE BIT(0)
+
+struct rppx1 {
+ struct device *dev;
+ void __iomem *base;
+
+ struct {
+ struct rpp_module acq;
+ struct rpp_module bls;
+ struct rpp_module lin;
+ struct rpp_module lsc;
+ struct rpp_module awbg;
+ struct rpp_module dpcc;
+ struct rpp_module bd;
+ struct rpp_module hist;
+ struct rpp_module hist256;
+ struct rpp_module exm;
+ } pre1;
+
+ struct {
+ struct rpp_module acq;
+ struct rpp_module bls;
+ struct rpp_module lin;
+ struct rpp_module lsc;
+ struct rpp_module awbg;
+ struct rpp_module dpcc;
+ struct rpp_module bd;
+ struct rpp_module hist;
+ struct rpp_module exm;
+ } pre2;
+
+ struct {
+ struct rpp_module awbg;
+ struct rpp_module ccor;
+ struct rpp_module hist;
+ struct rpp_module db;
+ struct rpp_module cac;
+ struct rpp_module ltm;
+ struct rpp_module ltmmeas;
+ struct rpp_module wbmeas;
+ struct rpp_module bdrgb;
+ struct rpp_module shrp;
+ } post;
+
+ struct {
+ struct rpp_module ga;
+ struct rpp_module is;
+ struct rpp_module ccor;
+ struct rpp_module outif;
+ struct rpp_module outregs;
+ } hv;
+
+ struct {
+ struct rpp_module ga;
+ struct rpp_module is;
+ struct rpp_module ccor;
+ struct rpp_module outif;
+ struct rpp_module outregs;
+ struct rpp_module xyz2luv;
+ } mv;
+
+ struct rpp_module rmap;
+ struct rpp_module rmapmeas;
+};
+
+void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value);
+u32 rppx1_read(struct rppx1 *rpp, u32 offset);
+
+#endif /* __MEDIA_RPPX1_H__ */
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
new file mode 100644
index 000000000000..d5624e3c76a9
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define ACQ_VERSION_REG 0x0000
+
+#define ACQ_CTRL_REG 0x0004
+#define ACQ_CTRL_ALTERNATIVE_CFG_MODE_ENABLE BIT(8)
+#define ACQ_CTRL_RPP_MODE_MASK GENMASK(3, 1)
+#define ACQ_CTRL_RPP_MODE_RAWBT601 (0 << 1)
+#define ACQ_CTRL_RPP_MODE_BT656 (1 << 1)
+#define ACQ_CTRL_RPP_MODE_BT601 (2 << 1)
+#define ACQ_CTRL_RPP_MODE_BAYER (3 << 1)
+#define ACQ_CTRL_RPP_MODE_DATA (4 << 1)
+#define ACQ_CTRL_RPP_MODE_BAYERRGB (5 << 1)
+#define ACQ_CTRL_RPP_MODE_RAWBT656 (6 << 1)
+#define ACQ_CTRL_INFORM_EN_ENABLE BIT(0)
+
+#define ACQ_PROP_REG 0x0008
+
+#define ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB BIT(30)
+#define ACQ_PROP_YUV_OUT_SEL BIT(25)
+#define ACQ_PROP_MUX_DMA_SEL BIT(24)
+#define ACQ_PROP_SECOND_INPUT_TYPE BIT(18)
+#define ACQ_PROP_LATENCY_FIFO_INPUT_SELECTION BIT(15)
+#define ACQ_PROP_INPUT_SELECTION_MASK GENMASK(14, 12)
+#define ACQ_PROP_INPUT_SELECTION_8BIT (0 << 12)
+#define ACQ_PROP_INPUT_SELECTION_10BIT (1 << 12)
+#define ACQ_PROP_INPUT_SELECTION_12BIT (2 << 12)
+#define ACQ_PROP_BAYER_PAT_MASK GENMASK(4, 3)
+#define ACQ_PROP_BAYER_PAT_RGRG (0 << 3)
+#define ACQ_PROP_BAYER_PAT_GRGR (1 << 3)
+#define ACQ_PROP_BAYER_PAT_GBGB (2 << 3)
+#define ACQ_PROP_BAYER_PAT_BGBG (3 << 3)
+#define ACQ_PROP_VSYNC_POL BIT(2)
+#define ACQ_PROP_HSYNC_POL BIT(1)
+#define ACQ_PROP_SAMPLE_EDGE BIT(0)
+
+#define ACQ_H_OFFS_REG 0x000c
+#define ACQ_V_OFFS_REG 0x0010
+#define ACQ_H_SIZE_REG 0x0014
+#define ACQ_V_SIZE_REG 0x0018
+#define ACQ_OUT_H_OFFS_REG 0x001c
+#define ACQ_OUT_V_OFFS_REG 0x0020
+#define ACQ_OUT_H_SIZE_REG 0x0024
+#define ACQ_OUT_V_SIZE_REG 0x0028
+#define FLAGS_SHD_REG 0x002c
+#define ACQ_OUT_H_OFFS_SHD_REG 0x0030
+#define ACQ_OUT_V_OFFS_SHD_REG 0x0034
+#define ACQ_OUT_H_SIZE_SHD_REG 0x0038
+#define ACQ_OUT_V_SIZE_SHD_REG 0x003c
+
+static int rppx1_acq_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, ACQ_VERSION_REG) != 0x0b)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_acq_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ u32 bayerpat, selection;
+
+ rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_RPP_MODE_MASK,
+ ACQ_CTRL_RPP_MODE_BAYER);
+
+ rpp_module_write(mod, ACQ_H_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_V_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_H_SIZE_REG, fmt->width);
+ rpp_module_write(mod, ACQ_V_SIZE_REG, fmt->height);
+ rpp_module_write(mod, ACQ_OUT_H_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_OUT_V_OFFS_REG, 0);
+ rpp_module_write(mod, ACQ_OUT_H_SIZE_REG, fmt->width);
+ rpp_module_write(mod, ACQ_OUT_V_SIZE_REG, fmt->height);
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ mod->info.acq.raw_pattern = RPP_BGGR;
+ bayerpat = ACQ_PROP_BAYER_PAT_BGBG;
+ break;
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ mod->info.acq.raw_pattern = RPP_GBRG;
+ bayerpat = ACQ_PROP_BAYER_PAT_GBGB;
+ break;
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ mod->info.acq.raw_pattern = RPP_GRBG;
+ bayerpat = ACQ_PROP_BAYER_PAT_GRGR;
+ break;
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ mod->info.acq.raw_pattern = RPP_RGGB;
+ bayerpat = ACQ_PROP_BAYER_PAT_RGRG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ selection = ACQ_PROP_INPUT_SELECTION_8BIT;
+ break;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ selection = ACQ_PROP_INPUT_SELECTION_10BIT;
+ break;
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ selection = ACQ_PROP_INPUT_SELECTION_12BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rpp_module_write(mod, ACQ_PROP_REG, bayerpat | selection |
+ ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB);
+
+ rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_INFORM_EN_ENABLE,
+ ACQ_CTRL_INFORM_EN_ENABLE);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_acq_ops = {
+ .probe = rppx1_acq_probe,
+ .start = rppx1_acq_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
new file mode 100644
index 000000000000..f30e12d6f880
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define AWB_GAIN_VERSION_REG 0x0000
+
+#define AWB_ENABLE_REG 0x0004
+#define AWB_ENABLE_AWB_GAIN_EN BIT(0)
+
+#define AWB_GAIN_GR_REG 0x0008
+#define AWB_GAIN_GB_REG 0x000c
+#define AWB_GAIN_R_REG 0x0010
+#define AWB_GAIN_B_REG 0x0014
+
+static int rppx1_awbg_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, AWB_GAIN_VERSION_REG) != 3)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+rppx1_awbg_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_awbg_params *cfg = &block->awbg;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + AWB_ENABLE_REG, 0);
+ return 0;
+ }
+
+ /*
+ * RPP gains are 18-bit with 12 bit fractional part and 0x1000 = 1.0,
+ * giving a possible range of 0.0 to 64.0. NOTE: RPP documentation is
+ * contradictory this is the register definition, the function
+ * description states 0x400 = 1.0 AND 18-bit with 12 fractional bits,
+ * which is not possible...
+ */
+
+ write(priv, mod->base + AWB_GAIN_GR_REG, cfg->gain_green_r);
+ write(priv, mod->base + AWB_GAIN_GB_REG, cfg->gain_green_b);
+ write(priv, mod->base + AWB_GAIN_R_REG, cfg->gain_red);
+ write(priv, mod->base + AWB_GAIN_B_REG, cfg->gain_blue);
+
+ write(priv, mod->base + AWB_ENABLE_REG, AWB_ENABLE_AWB_GAIN_EN);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_awbg_ops = {
+ .probe = rppx1_awbg_probe,
+ .fill_params = rppx1_awbg_fill_params,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
new file mode 100644
index 000000000000..fcbaa0ee9281
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define DPF_VERSION_REG 0x0000
+
+#define DPF_MODE_REG 0x0004
+#define DPF_MODE_USE_NF_GAIN BIT(9)
+#define DPF_MODE_LSC_GAIN_COMP BIT(8)
+#define DPF_MODE_NLL_SEGMENTATION BIT(6)
+#define DPF_MODE_RB_FILTER_SIZE BIT(5)
+#define DPF_MODE_R_FILTER_OFF BIT(4)
+#define DPF_MODE_GR_FILTER_OFF BIT(3)
+#define DPF_MODE_GB_FILTER_OFF BIT(2)
+#define DPF_MODE_B_FILTER_OFF BIT(1)
+#define DPF_MODE_DPF_ENABLE BIT(0)
+
+#define DPF_STRENGTH_R_REG 0x0008
+#define DPF_STRENGTH_G_REG 0x000c
+#define DPF_STRENGTH_B_REG 0x0010
+#define DPF_S_WEIGHT_G_1_4_REG 0x0014
+#define DPF_S_WEIGHT_G_5_6_REG 0x0018
+#define DPF_S_WEIGHT_RB_1_4_REG 0x001c
+#define DPF_S_WEIGHT_RB_5_6_REG 0x0020
+
+#define DPF_NLL_G_COEFF_REG_NUM 17
+#define DPF_NLL_G_COEFF_REG(n) (0x0024 + (4 * (n)))
+
+#define DPF_NLL_RB_COEFF_REG_NUM 17
+#define DPF_NLL_RB_COEFF_REG(n) (0x0068 + (4 * (n)))
+
+#define DPF_NF_GAIN_R_REG 0x00ac
+#define DPF_NF_GAIN_GR_REG 0x00b0
+#define DPF_NF_GAIN_GB_REG 0x00b4
+#define DPF_NF_GAIN_B_REG 0x00b8
+
+static int rppx1_bd_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, DPF_VERSION_REG) != 5)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_bd_ops = {
+ .probe = rppx1_bd_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
new file mode 100644
index 000000000000..1accc106d65b
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define RGBDENOISE_VERSION_REG 0x0000
+
+#define RGBDENOISE_HW_BYPASS_REG 0x0004
+#define RGBDENOISE_HW_BYPASS_BYPASS_EN BIT(0)
+
+#define RGBDENOISE_SPNR_CTRL_REG 0x0008
+#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_C_MASK GENMASK(11, 8)
+#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_Y_MASK GENMASK(7, 4)
+#define RGBDENOISE_SPNR_CTRL_C2NR_EN BIT(0)
+
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_REG 0x000c
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_REG 0x0010
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_REG 0x0014
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_REG 0x0018
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_REG 0x001c
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_REG 0x0020
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_REG 0x0024
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_REG 0x0028
+#define RGBDENOISE_SPNR_SPATIAL_COEF_0_3_REG 0x002c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_REG 0x0030
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_REG 0x0034
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_REG 0x0038
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_REG 0x003c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_REG 0x0040
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_REG 0x0044
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_REG 0x0048
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_REG 0x004c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_REG 0x0050
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_REG 0x0054
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_REG 0x0058
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_REG 0x005c
+#define RGBDENOISE_HW_BYPASS_SDW_REG 0x0060
+#define RGBDENOISE_SPNR_CTRL_SDW_REG 0x0064
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_SDW_REG 0x0068
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_SDW_REG 0x006c
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_SDW_REG 0x0070
+#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_SDW_REG 0x0074
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_SDW_REG 0x0078
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_SDW_REG 0x007c
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_SDW_REG 0x0080
+#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_SDW_REG 0x0084
+#define RGBDENOISE_SPNR_SPATIAL_COEFF_0_3_SDW_REG 0x0088
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_SDW_REG 0x008c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_SDW_REG 0x0090
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_SDW_REG 0x0094
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_SDW_REG 0x0098
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_SDW_REG 0x009c
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_SDW_REG 0x00a0
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_SDW_REG 0x00a4
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_SDW_REG 0x00a8
+#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_SDW_REG 0x00ac
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_SDW_REG 0x00b0
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_SDW_REG 0x00b4
+#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_SDW_REG 0x00b8
+
+static int rppx1_bdrgb_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, RGBDENOISE_VERSION_REG)) {
+ case 6:
+ /* 12-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_bdrgb_ops = {
+ .probe = rppx1_bdrgb_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
new file mode 100644
index 000000000000..01a61db279bf
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+#include "rppx1.h"
+
+#define BLS_VERSION_REG 0x0000
+
+#define BLS_CTRL_REG 0x0004
+#define BLS_CTRL_BLS_WIN2 BIT(3)
+#define BLS_CTRL_BLS_WIN1 BIT(2)
+#define BLS_CTRL_BLS_MODE_MEASURED BIT(1)
+#define BLS_CTRL_BLS_EN BIT(0)
+
+#define BLS_SAMPLES_REG 0x0008
+#define BLS_H1_START_REG 0x000c
+#define BLS_H1_STOP_REG 0x0010
+#define BLS_V1_START_REG 0x0014
+#define BLS_V1_STOP_REG 0x0018
+#define BLS_H2_START_REG 0x001c
+#define BLS_H2_STOP_REG 0x0020
+#define BLS_V2_START_REG 0x0024
+#define BLS_V2_STOP_REG 0x0028
+#define BLS_A_FIXED_REG 0x002c
+#define BLS_B_FIXED_REG 0x0030
+#define BLS_C_FIXED_REG 0x0034
+#define BLS_D_FIXED_REG 0x0038
+#define BLS_A_MEASURED_REG 0x003c
+#define BLS_B_MEASURED_REG 0x0040
+#define BLS_C_MEASURED_REG 0x0044
+#define BLS_D_MEASURED_REG 0x0048
+#define BLS_PRE1_FIXED_MASK GENMASK(24, 0)
+#define BLS_PRE2_FIXED_MASK GENMASK(12, 0)
+
+static int rppx1_bls_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, BLS_VERSION_REG)) {
+ case 3:
+ case 5:
+ /* 12-bit. */
+ break;
+ case 2:
+ case 4:
+ /* 20-bit. */
+ break;
+ case 6:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void
+rppx1_bls_swap_regs(struct rpp_module *mod, const u32 input[4], u32 output[4])
+{
+ static const unsigned int swap[4][4] = {
+ [RPP_RGGB] = { 0, 1, 2, 3 },
+ [RPP_GRBG] = { 1, 0, 3, 2 },
+ [RPP_GBRG] = { 2, 3, 0, 1 },
+ [RPP_BGGR] = { 3, 2, 1, 0 },
+ };
+
+ /* Swap to pattern used in our path, PRE1 or PRE2. */
+ struct rpp_module *acq = mod == &mod->rpp->pre1.bls ?
+ &mod->rpp->pre1.acq : &mod->rpp->pre2.bls;
+ enum rpp_raw_pattern pattern = acq->info.acq.raw_pattern;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ output[i] = input[swap[pattern][i]];
+}
+
+static int
+rppx1_bls_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_bls_params *cfg = &block->bls;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + BLS_CTRL_REG, 0);
+ return 0;
+ }
+
+ u32 ctrl = BLS_CTRL_BLS_EN;
+
+ if (cfg->mode == RPPX1_BLS_MODE_FIXED) {
+ static const u32 regs[] = {
+ BLS_A_FIXED_REG,
+ BLS_B_FIXED_REG,
+ BLS_C_FIXED_REG,
+ BLS_D_FIXED_REG,
+ };
+ u32 swapped[4];
+
+ rppx1_bls_swap_regs(mod, regs, swapped);
+
+ /*
+ * The PRE1 pipe fixed values are 24-bits + 1 sign bit, while
+ * the PRE2 pipe values are 12-bits + 1 sign bit.
+ */
+ u32 mask;
+
+ switch (cfg->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1:
+ mask = BLS_PRE1_FIXED_MASK;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2:
+ mask = BLS_PRE2_FIXED_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ write(priv, mod->base + swapped[0], cfg->fixed.a & mask);
+ write(priv, mod->base + swapped[1], cfg->fixed.b & mask);
+ write(priv, mod->base + swapped[2], cfg->fixed.c & mask);
+ write(priv, mod->base + swapped[3], cfg->fixed.d & mask);
+ } else {
+ write(priv, mod->base + BLS_SAMPLES_REG, cfg->samples);
+
+ if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN1) {
+ write(priv, mod->base + BLS_H1_START_REG, cfg->window1.h_offs);
+ write(priv, mod->base + BLS_H1_STOP_REG, cfg->window1.h_size);
+ write(priv, mod->base + BLS_V1_START_REG, cfg->window1.v_offs);
+ write(priv, mod->base + BLS_V1_STOP_REG, cfg->window1.v_size);
+ ctrl |= BLS_CTRL_BLS_WIN1;
+ }
+
+ if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN2) {
+ write(priv, mod->base + BLS_H2_START_REG, cfg->window2.h_offs);
+ write(priv, mod->base + BLS_H2_STOP_REG, cfg->window2.h_size);
+ write(priv, mod->base + BLS_V2_START_REG, cfg->window2.v_offs);
+ write(priv, mod->base + BLS_V2_STOP_REG, cfg->window2.v_size);
+ ctrl |= BLS_CTRL_BLS_WIN2;
+ }
+
+ ctrl |= BLS_CTRL_BLS_MODE_MEASURED;
+ }
+
+ write(priv, mod->base + BLS_CTRL_REG, ctrl);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_bls_ops = {
+ .probe = rppx1_bls_probe,
+ .fill_params = rppx1_bls_fill_params,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
new file mode 100644
index 000000000000..fb3bfa668425
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define CAC_VERSION_REG 0x0000
+#define CAC_CTRL_REG 0x0004
+#define CAC_COUNT_START_REG 0x0008
+#define CAC_A_REG 0x000c
+#define CAC_B_REG 0x0010
+#define CAC_C_REG 0x0014
+#define CAC_X_NORM_REG 0x0018
+#define CAC_Y_NORM_REG 0x001c
+
+static int rppx1_cac_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, CAC_VERSION_REG) != 3)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_cac_ops = {
+ .probe = rppx1_cac_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
new file mode 100644
index 000000000000..5ddc7edf6930
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define CCOR_VERSION_REG 0x0000
+
+#define CCOR_COEFF_REG_NUM 9
+#define CCOR_COEFF_REG(n) (0x0004 + (4 * (n)))
+
+#define CCOR_OFFSET_R_REG 0x0028
+#define CCOR_OFFSET_G_REG 0x002c
+#define CCOR_OFFSET_B_REG 0x0030
+
+#define CCOR_CONFIG_TYPE_REG 0x0034
+#define CCOR_CONFIG_TYPE_USE_OFFSETS_AS_PRE_OFFSETS BIT(1)
+#define CCOR_CONFIG_TYPE_CCOR_RANGE_AVAILABLE BIT(0)
+
+#define CCOR_RANGE_REG 0x0038
+#define CCOR_RANGE_CCOR_C_RANGE BIT(1)
+#define CCOR_RANGE_CCOR_Y_RANGE BIT(0)
+
+static int rppx1_ccor_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, CCOR_VERSION_REG)) {
+ case 3:
+ /* 12-bit. */
+ break;
+ case 4:
+ /* 20-bit. */
+ break;
+ case 5:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_ccor_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Configure matrix in bypass mode. */
+ rpp_module_write(mod, CCOR_COEFF_REG(0), 0x1000);
+ rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0000);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(3), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(4), 0x1000);
+ rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0000);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(7), 0x0000);
+ rpp_module_write(mod, CCOR_COEFF_REG(8), 0x1000);
+
+ rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000);
+ rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000000);
+ rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000000);
+
+ return 0;
+}
+
+static int
+rppx1_ccor_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_ccor_params *cfg = &block->ccor;
+
+ /* If the modules is disabled, configure in bypass mode. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + CCOR_COEFF_REG(0), 0x1000);
+ write(priv, mod->base + CCOR_COEFF_REG(1), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(2), 0x0000);
+
+ write(priv, mod->base + CCOR_COEFF_REG(3), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(4), 0x1000);
+ write(priv, mod->base + CCOR_COEFF_REG(5), 0x0000);
+
+ write(priv, mod->base + CCOR_COEFF_REG(6), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(7), 0x0000);
+ write(priv, mod->base + CCOR_COEFF_REG(8), 0x1000);
+
+ write(priv, mod->base + CCOR_OFFSET_R_REG, 0x00000000);
+ write(priv, mod->base + CCOR_OFFSET_G_REG, 0x00000000);
+ write(priv, mod->base + CCOR_OFFSET_B_REG, 0x00000000);
+
+ return 0;
+ }
+
+ /*
+ * Coefficient n for color correction matrix.
+ *
+ * RPP coefficients are 16-bit signed fixed-point numbers with 4 bit
+ * integer and 12 bit fractional part ranging from -8 (0x8000) to
+ * +7.9996 (0x7FFF). 0 is represented by 0x0000 and a coefficient
+ * value of 1 as 0x1000.
+ */
+ write(priv, mod->base + CCOR_COEFF_REG(0), cfg->coeff[0][0]);
+ write(priv, mod->base + CCOR_COEFF_REG(1), cfg->coeff[0][1]);
+ write(priv, mod->base + CCOR_COEFF_REG(2), cfg->coeff[0][2]);
+
+ write(priv, mod->base + CCOR_COEFF_REG(3), cfg->coeff[1][0]);
+ write(priv, mod->base + CCOR_COEFF_REG(4), cfg->coeff[1][1]);
+ write(priv, mod->base + CCOR_COEFF_REG(5), cfg->coeff[1][2]);
+
+ write(priv, mod->base + CCOR_COEFF_REG(6), cfg->coeff[2][0]);
+ write(priv, mod->base + CCOR_COEFF_REG(7), cfg->coeff[2][1]);
+ write(priv, mod->base + CCOR_COEFF_REG(8), cfg->coeff[2][2]);
+
+ /*
+ * Offset for color components correction matrix.
+ *
+ * Values are a two's complement integer with one sign bit.
+ */
+ write(priv, mod->base + CCOR_OFFSET_R_REG, cfg->offset[0]);
+ write(priv, mod->base + CCOR_OFFSET_G_REG, cfg->offset[1]);
+ write(priv, mod->base + CCOR_OFFSET_B_REG, cfg->offset[2]);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ccor_ops = {
+ .probe = rppx1_ccor_probe,
+ .start = rppx1_ccor_start,
+ .fill_params = rppx1_ccor_fill_params,
+};
+
+static int rppx1_ccor_csm_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Reuse bypass matrix setup. */
+ if (fmt->code == MEDIA_BUS_FMT_RGB888_1X24)
+ return rppx1_ccor_start(mod, fmt);
+
+ /* Color Transformation RGB to YUV according to ITU-R BT.709. */
+ rpp_module_write(mod, CCOR_COEFF_REG(0), 0x0367);
+ rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0b71);
+ rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0128);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(3), 0xfe2b);
+ rpp_module_write(mod, CCOR_COEFF_REG(4), 0xf9d5);
+ rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0800);
+
+ rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0800);
+ rpp_module_write(mod, CCOR_COEFF_REG(7), 0xf8bc);
+ rpp_module_write(mod, CCOR_COEFF_REG(8), 0xff44);
+
+ rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000);
+ rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000800);
+ rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000800);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ccor_csm_ops = {
+ .probe = rppx1_ccor_probe,
+ .start = rppx1_ccor_csm_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
new file mode 100644
index 000000000000..78a330066b7e
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define FILT_VERSION_REG 0x0000
+
+#define DEMOSAIC_REG 0x0004
+#define DEMOSAIC_DEMOSAIC_BYPASS BIT(16)
+#define DEMOSAIC_DEMOSAIC_TH_MASK GENMASK(15, 0)
+
+#define FILT_MODE_REG 0x0008
+#define FILT_MODE_FILT_LP_SELECT_MASK GENMASK(11, 8)
+#define FILT_MODE_FILT_CHR_H_MODE_MASK GENMASK(7, 6)
+#define FILT_MODE_FILT_CHR_V_MODE_MASK GENMASK(5, 4)
+#define FILT_MODE_FILT_MODE BIT(1)
+#define FILT_MODE_FILT_ENABLE BIT(0)
+
+#define FILT_THRESH_BL0_REG 0x000c
+#define FILT_THRESH_BL1_REG 0x0010
+#define FILT_THRESH_SH0_REG 0x0014
+#define FILT_THRESH_SH1_REG 0x0018
+#define FILT_LUM_WEIGHT_REG 0x001c
+#define FILT_FAC_SH1_REG 0x0020
+#define FILT_FAC_SH0_REG 0x0024
+#define FILT_FAC_MID_REG 0x0028
+#define FILT_FAC_BL0_REG 0x002c
+#define FILT_FAC_BL1_REG 0x0030
+
+static int rppx1_db_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, FILT_VERSION_REG) != 5)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_db_ops = {
+ .probe = rppx1_db_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
new file mode 100644
index 000000000000..3d5d9c0a7e72
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define DPCC_VERSION_REG 0x0000
+
+#define DPCC_MODE_REG 0x0004
+#define DPCC_MODE_STAGE1_ENABLE BIT(2)
+#define DPCC_MODE_GRAYSCALE_MODE BIT(1)
+#define DPCC_MODE_DPCC_ENABLE BIT(0)
+
+#define DPCC_OUTPUT_MODE_REG 0x0008
+#define DPCC_SET_USE_REG 0x000c
+#define DPCC_METHODS_SET_1_REG 0x0010
+#define DPCC_METHODS_SET_2_REG 0x0014
+#define DPCC_METHODS_SET_3_REG 0x0018
+#define DPCC_LINE_THRESH_1_REG 0x001c
+#define DPCC_LINE_MAD_FAC_1_REG 0x0020
+#define DPCC_PG_FAC_1_REG 0x0024
+#define DPCC_RND_THRESH_1_REG 0x0028
+#define DPCC_RG_FAC_1_REG 0x002c
+#define DPCC_LINE_THRESH_2_REG 0x0030
+#define DPCC_LINE_MAD_FAC_2_REG 0x0034
+#define DPCC_PG_FAC_2_REG 0x0038
+#define DPCC_RND_THRESH_2_REG 0x003c
+#define DPCC_RG_FAC_2_REG 0x0040
+#define DPCC_LINE_THRESH_3_REG 0x0044
+#define DPCC_LINE_MAD_FAC_3_REG 0x0048
+#define DPCC_PG_FAC_3_REG 0x004c
+#define DPCC_RND_THRESH_3_REG 0x0050
+#define DPCC_RG_FAC_3_REG 0x0054
+#define DPCC_RO_LIMITS_REG 0x0058
+#define DPCC_RND_OFFS_REG 0x005c
+#define DPCC_BPT_CTRL_REG 0x0060
+#define DPCC_BP_NUMBER_REG 0x0064
+#define DPCC_BP_TADDR_REG 0x0068
+#define DPCC_BP_POSITION_REG 0x006c
+
+static int rppx1_dpcc_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, DPCC_VERSION_REG)) {
+ case 2:
+ case 4:
+ case 6:
+ /* 12-bit. */
+ break;
+ case 3:
+ case 5:
+ case 7:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_dpcc_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Bypass stage1 and DPCC. */
+ rpp_module_write(mod, DPCC_MODE_REG, 0);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_dpcc_ops = {
+ .probe = rppx1_dpcc_probe,
+ .start = rppx1_dpcc_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
new file mode 100644
index 000000000000..c7b24c356222
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define EXM_VERSION_REG 0x0000
+#define EXM_START_REG 0x0004
+
+#define EXM_CTRL_REG 0x0008
+#define EXM_CTRL_EXM_AUTOSTOP BIT(1) /* HW doc says not supported. */
+#define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0)
+
+#define EXM_MODE_REG 0x000c
+
+#define EXM_CHANNEL_SEL_REG 0x0010
+#define EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0)
+
+#define EXM_LAST_MEAS_LINE_REG 0x0014
+#define EXM_COEFF_R_REG 0x0018
+#define EXM_COEFF_G_GR_REG 0x001c
+#define EXM_COEFF_B_REG 0x0020
+#define EXM_COEFF_GB_REG 0x0024
+#define EXM_H_OFFS_REG 0x0028
+#define EXM_V_OFFS_REG 0x002c
+#define EXM_H_SIZE_REG 0x0030
+#define EXM_V_SIZE_REG 0x0034
+#define EXM_FORCED_UPD_START_LINE_REG 0x0038
+#define EXM_VSTART_STATUS_REG 0x003c
+
+#define EXM_MEAN_REG(n) (0x0040 + (4 * (n)))
+
+static int rppx1_exm_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, EXM_VERSION_REG)) {
+ case 1:
+ /* 8-bit. */
+ break;
+ case 3:
+ /* 20-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+rppx1_exm_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_exm_params *cfg = &block->exm;
+ u32 h_offs, v_offs, h_size, v_size;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + EXM_MODE_REG, 0);
+ return 0;
+ }
+
+ switch (cfg->mode) {
+ case RPPX1_EXP_MEASURING_MODE_RGB:
+ case RPPX1_EXP_MEASURING_MODE_BAYER:
+ write(priv, mod->base + EXM_MODE_REG, cfg->mode);
+ break;
+ default:
+ write(priv, mod->base + EXM_MODE_REG, 0);
+ return 0;
+ }
+
+ write(priv, mod->base + EXM_COEFF_R_REG, cfg->coeff_r);
+ write(priv, mod->base + EXM_COEFF_G_GR_REG, cfg->coeff_g_gr);
+ write(priv, mod->base + EXM_COEFF_GB_REG, cfg->coeff_gb);
+ write(priv, mod->base + EXM_COEFF_B_REG, cfg->coeff_b);
+
+ /* Select sample point */
+ write(priv, mod->base + EXM_CHANNEL_SEL_REG,
+ cfg->channel_sel & EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK);
+
+ /*
+ * Adjust and set measurement window,
+ * - Offsets must be even.
+ * - Width and height must be even and divisible in 5 windows.
+ */
+ h_offs = cfg->wnd.h_offs & 0x1ffe;
+ v_offs = cfg->wnd.v_offs & 0x1ffe;
+ h_size = (cfg->wnd.h_size - 1) - ((cfg->wnd.h_size - 1) % 10);
+ v_size = (cfg->wnd.v_size - 1) - ((cfg->wnd.v_size - 1) % 10);
+
+ write(priv, mod->base + EXM_H_OFFS_REG, h_offs);
+ write(priv, mod->base + EXM_V_OFFS_REG, v_offs);
+ write(priv, mod->base + EXM_H_SIZE_REG, h_size / 5);
+ write(priv, mod->base + EXM_V_SIZE_REG, v_size / 5);
+
+ /*
+ * Set last measurement line for ready interrupt. Ignore the value
+ * from the parameters as it is only useful for fast-channel switching.
+ */
+ write(priv, mod->base + EXM_LAST_MEAS_LINE_REG, v_offs + v_size + 1);
+
+ write(priv, mod->base + EXM_START_REG, 1);
+
+ return 0;
+}
+
+static int rppx1_exm_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_exm_stats *stats = &block->exm;
+
+ for (unsigned int i = 0; i < RPPX1_EXM_NUM_WIN; i++)
+ stats->exp_mean[i] = rpp_module_read(mod, EXM_MEAN_REG(i));
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_exm_ops = {
+ .probe = rppx1_exm_probe,
+ .fill_params = rppx1_exm_fill_params,
+ .fill_stats = rppx1_exm_fill_stats,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
new file mode 100644
index 000000000000..1d9c24c43f77
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define GAMMA_OUT_VERSION_REG 0x0000
+
+#define GAMMA_OUT_ENABLE_REG 0x0004
+#define GAMMA_OUT_ENABLE_GAMMA_OUT_EN BIT(0)
+
+#define GAMMA_OUT_MODE_REG 0x0008
+#define GAMMA_OUT_MODE_GAMMA_OUT_EQU_SEGM BIT(0)
+
+#define GAMMA_OUT_Y_REG(n) (0x000c + (4 * (n)))
+
+#define GAMMA_OUT_HV_GAMMA_CURVE_MASK GENMASK(11, 0)
+#define GAMMA_OUT_MV_GAMMA_CURVE_MASK GENMASK(23, 0)
+
+static int rppx1_ga_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, GAMMA_OUT_VERSION_REG)) {
+ case 1:
+ /* 12-bit. */
+ break;
+ case 2:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_ga_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Disable stage. */
+ rpp_module_write(mod, GAMMA_OUT_ENABLE_REG, 0);
+
+ return 0;
+}
+
+static int
+rppx1_ga_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_ga_params *cfg = &block->ga;
+ u32 mask;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + GAMMA_OUT_ENABLE_REG, 0);
+ return 0;
+ }
+
+ switch (cfg->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_GA_HV:
+ mask = GAMMA_OUT_HV_GAMMA_CURVE_MASK;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_GA_MV:
+ mask = GAMMA_OUT_MV_GAMMA_CURVE_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ write(priv, mod->base + GAMMA_OUT_MODE_REG, cfg->mode);
+
+ for (unsigned int i = 0; i < RPPX1_GA_MAX_SAMPLES; i++)
+ write(priv, mod->base + GAMMA_OUT_Y_REG(i),
+ cfg->gamma_y[i] & mask);
+
+ /* Enable module. */
+ write(priv, mod->base + GAMMA_OUT_ENABLE_REG,
+ GAMMA_OUT_ENABLE_GAMMA_OUT_EN);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ga_ops = {
+ .probe = rppx1_ga_probe,
+ .start = rppx1_ga_start,
+ .fill_params = rppx1_ga_fill_params,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
new file mode 100644
index 000000000000..8fe8cbbc85df
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#include <linux/bitfield.h>
+
+#define HIST_VERSION_REG 0x0000
+
+#define HIST_CTRL_REG 0x0004
+#define HIST_CTRL_HIST_UPDATE_ENABLE BIT(0)
+
+#define HIST_MODE_REG 0x0008
+#define HIST_MODE_HIST_MODE_MASK GENMASK(2, 0)
+#define HIST_MODE_HIST_MODE_DISABLE 0
+#define HIST_MODE_HIST_MODE_YRGB 1
+#define HIST_MODE_HIST_MODE_R 2
+#define HIST_MODE_HIST_MODE_GR 3
+#define HIST_MODE_HIST_MODE_B 4
+#define HIST_MODE_HIST_MODE_GB 5
+
+#define HIST_CHANNEL_SEL_REG 0x000c
+#define HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0)
+
+#define HIST_LAST_MEAS_LINE_REG 0x0010
+#define HIST_SUBSAMPLING_REG 0x0014
+#define HIST_SUBSAMPLING_V_STEPSIZE(x) (((x) & 0x7f) << 24)
+#define HIST_SUBSAMPLING_H_STEP_INC(x) (((x) & 0x1ffff))
+
+#define HIST_COEFF_R_REG 0x0018
+#define HIST_COEFF_G_REG 0x001c
+#define HIST_COEFF_B_REG 0x0020
+#define HIST_H_OFFS_REG 0x0024
+#define HIST_V_OFFS_REG 0x0028
+#define HIST_H_SIZE_REG 0x002c
+#define HIST_V_SIZE_REG 0x0030
+
+#define HIST_SAMPLE_RANGE_REG 0x0034
+#define HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK GENMASK(28, 24)
+#define HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK GENMASK(23, 0)
+
+#define HIST_WEIGHT_00TO30_REG 0x0038
+#define HIST_WEIGHT_40TO21_REG 0x003c
+#define HIST_WEIGHT_31TO12_REG 0x0040
+#define HIST_WEIGHT_22TO03_REG 0x0044
+#define HIST_WEIGHT_13TO43_REG 0x0048
+#define HIST_WEIGHT_04TO34_REG 0x004c
+#define HIST_WEIGHT_44_REG 0x0050
+#define HIST_FORCED_UPD_START_LINE_REG 0x0054
+#define HIST_FORCED_UPDATE_REG 0x0058
+#define HIST_VSTART_STATUS_REG 0x005c
+
+#define HIST_BIN_REG(n) (0x0060 + (4 * (n)))
+
+static int rppx1_hist_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, HIST_VERSION_REG)) {
+ case 3:
+ /* 12-bit. */
+ break;
+ case 4:
+ /* 20-bit. */
+ break;
+ case 5:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define RPPX1_HIST_WEIGHT(v0, v1, v2, v3) \
+ (((v0) & 0x1f) | (((v1) & 0x1f) << 8) | \
+ (((v2) & 0x1f) << 16) | \
+ (((v3) & 0x1f) << 24))
+
+static int rppx1_hist_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_hist_params *cfg = &block->hist;
+ u32 h_offs, v_offs, h_size, v_size;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + HIST_MODE_REG,
+ HIST_MODE_HIST_MODE_DISABLE);
+ return 0;
+ }
+
+ /* Select sample point */
+ write(priv, mod->base + HIST_CHANNEL_SEL_REG,
+ cfg->channel_sel & HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK);
+
+ /*
+ * Configure the input subsampling.
+ *
+ * In Bayer mode the vertical and horizontal subsampling counters are
+ * only incremented for color channels selected by hist_mode.
+ */
+ write(priv, mod->base + HIST_SUBSAMPLING_REG,
+ HIST_SUBSAMPLING_V_STEPSIZE(cfg->v_stepsize) |
+ HIST_SUBSAMPLING_H_STEP_INC(cfg->h_step_inc));
+
+ /*
+ * Adjust and set measurement window to hardware limitations,
+ * - Offsets must be even.
+ * - Width and height must be even and divisible in 5 windows.
+ */
+ h_offs = cfg->wnd.h_offs & 0x1ffe;
+ v_offs = cfg->wnd.v_offs & 0x1ffe;
+ h_size = cfg->wnd.h_size - cfg->wnd.h_size % 10;
+ v_size = cfg->wnd.v_size - cfg->wnd.v_size % 10;
+
+ write(priv, mod->base + HIST_H_OFFS_REG, h_offs);
+ write(priv, mod->base + HIST_V_OFFS_REG, v_offs);
+ write(priv, mod->base + HIST_H_SIZE_REG, h_size / 5);
+ write(priv, mod->base + HIST_V_SIZE_REG, v_size / 5);
+
+ /*
+ * Set last measurement line for ready interrupt. Ignore the value
+ * from the parameters as it is only useful for fast-channel switching.
+ */
+ write(priv, mod->base + HIST_LAST_MEAS_LINE_REG, v_offs + v_size + 1);
+
+ /* Set measurement window weights. */
+ write(priv, mod->base + HIST_WEIGHT_00TO30_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[0], cfg->weights[1],
+ cfg->weights[2], cfg->weights[3]));
+ write(priv, mod->base + HIST_WEIGHT_40TO21_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[4], cfg->weights[5],
+ cfg->weights[6], cfg->weights[7]));
+ write(priv, mod->base + HIST_WEIGHT_31TO12_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[8], cfg->weights[9],
+ cfg->weights[10], cfg->weights[11]));
+ write(priv, mod->base + HIST_WEIGHT_22TO03_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[12], cfg->weights[13],
+ cfg->weights[14], cfg->weights[15]));
+ write(priv, mod->base + HIST_WEIGHT_13TO43_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[16], cfg->weights[17],
+ cfg->weights[18], cfg->weights[19]));
+ write(priv, mod->base + HIST_WEIGHT_04TO34_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[20], cfg->weights[21],
+ cfg->weights[22], cfg->weights[23]));
+ write(priv, mod->base + HIST_WEIGHT_44_REG,
+ RPPX1_HIST_WEIGHT(cfg->weights[24], 0, 0, 0));
+
+ write(priv, mod->base + HIST_MODE_REG, cfg->mode);
+ write(priv, mod->base + HIST_COEFF_R_REG, cfg->coeff[0]);
+ write(priv, mod->base + HIST_COEFF_G_REG, cfg->coeff[1]);
+ write(priv, mod->base + HIST_COEFF_B_REG, cfg->coeff[2]);
+
+ u32 sample_reg = FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK,
+ cfg->sample_shift) |
+ FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK,
+ cfg->sample_offs);
+ write(priv, mod->base + HIST_SAMPLE_RANGE_REG, sample_reg);
+
+ write(priv, mod->base + HIST_FORCED_UPDATE_REG, 1);
+
+ return 0;
+}
+
+static int rppx1_hist_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_hist_stats *stats = &block->hist;
+
+ for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++)
+ stats->hist_bins[i] = rpp_module_read(mod, HIST_BIN_REG(i));
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_hist_ops = {
+ .probe = rppx1_hist_probe,
+ .fill_params = rppx1_hist_fill_params,
+ .fill_stats = rppx1_hist_fill_stats,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
new file mode 100644
index 000000000000..80b3244c77aa
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define HIST256_VERSION_REG 0x0000
+#define HIST256_MODE_REG 0x0004
+#define HIST256_MODE_HIST256_MODE BIT(0)
+
+#define HIST256_CHANNEL_SEL_REG 0x0008
+#define HIST256_CHANNEL_SEL_CHANNEL_SELECT GENMASK(2, 0)
+
+#define HIST256_H_OFFS_REG 0x000c
+#define HIST256_V_OFFS_REG 0x0010
+#define HIST256_H_SIZE_REG 0x0014
+#define HIST256_V_SIZE_REG 0x0018
+#define HIST256_SAMPLE_OFFSET_REG 0x001c
+#define HIST256_SAMPLE_SCALE_REG 0x0020
+#define HIST256_MEAS_RESULT_ADDR_AUTOINCR_REG 0x0024
+#define HIST256_MEAS_RESULT_ADDR_REG 0x0028
+#define HIST256_MEAS_RESULT_DATA_REG 0x002c
+
+#define HIST256_LOG_ENABLE_REG 0x0030
+#define HIST256_LOG_ENABLE_HIST256_LOG_EN BIT(0)
+
+#define HIST256_LOG_DX_LO_REG 0x0034
+#define HIST256_LOG_DX_HI_REG 0x0038
+
+#define HIST256_Y_REG_NUM 17
+#define HIST256_Y_REG(n) (0x0040 + (4 * (n)))
+
+static int rppx1_hist256_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, HIST256_VERSION_REG) != 2)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_hist256_ops = {
+ .probe = rppx1_hist256_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_is.c b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c
new file mode 100644
index 000000000000..7b1b6f6538cf
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define IS_VERSION 0x0000
+#define IS_H_OFFS 0x0008
+#define IS_V_OFFS 0x000c
+#define IS_H_SIZE 0x0010
+#define IS_V_SIZE 0x0014
+#define IS_H_OFFS_SHD 0x0024
+#define IS_V_OFFS_SHD 0x0028
+#define IS_H_SIZE_SHD 0x002c
+#define IS_V_SIZE_SHD 0x0030
+
+static int rppx1_is_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, IS_VERSION) != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_is_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ rpp_module_write(mod, IS_H_OFFS, 0);
+ rpp_module_write(mod, IS_V_OFFS, 0);
+ rpp_module_write(mod, IS_H_SIZE, fmt->width);
+ rpp_module_write(mod, IS_V_SIZE, fmt->height);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_is_ops = {
+ .probe = rppx1_is_probe,
+ .start = rppx1_is_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
new file mode 100644
index 000000000000..b389778f9383
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+/* NOTE: The module is called LIN the registers GAMMA_IN. */
+#define LIN_VERSION_REG 0x0000
+
+#define LIN_ENABLE_REG 0x0004
+#define LIN_ENABLE_GAMMA_IN_EN BIT(0)
+
+#define LIN_DX_LO_REG 0x0008
+#define LIN_DX_HI_REG 0x000c
+
+#define LIN_R_Y_REG_NUM 17
+#define LIN_R_Y_REG(n) (0x0010 + (4 * (n)))
+
+#define LIN_G_Y_REG_NUM 17
+#define LIN_G_Y_REG(n) (0x0054 + (4 * (n)))
+
+#define LIN_B_Y_REG_NUM 17
+#define LIN_B_Y_REG(n) (0x0098 + (4 * (n)))
+
+#define LIN_PRE1_DEGAMMA_CURVE_MASK GENMASK(23, 0)
+#define LIN_PRE1_SAMPLE_POINTS_MASK GENMASK(3, 0)
+#define LIN_PRE2_DEGAMMA_CURVE_MASK GENMASK(11, 0)
+#define LIN_PRE2_SAMPLE_POINTS_MASK GENMASK(2, 0)
+
+static int rppx1_lin_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, LIN_VERSION_REG)) {
+ case 7:
+ /* 12-bit. */
+ break;
+ case 8:
+ /* 20-bit. */
+ break;
+ case 9:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_lin_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ rpp_module_clrset(mod, LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN, 0);
+
+ return 0;
+}
+
+static int rppx1_lin_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_lin_params *cfg = &block->lin;
+ u8 sample_mask;
+ u32 dx_lo = 0;
+ u32 dx_hi = 0;
+ u32 mask;
+
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + LIN_ENABLE_REG, 0);
+ return 0;
+ }
+
+ switch (cfg->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1:
+ mask = LIN_PRE1_DEGAMMA_CURVE_MASK;
+ sample_mask = LIN_PRE1_SAMPLE_POINTS_MASK;
+ break;
+ case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2:
+ mask = LIN_PRE2_DEGAMMA_CURVE_MASK;
+ sample_mask = LIN_PRE2_SAMPLE_POINTS_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < 8; ++i) {
+ dx_lo |= (cfg->dx[i] & sample_mask) << 4 * i;
+ dx_hi |= (cfg->dx[i + 8] & sample_mask) << 4 * i;
+ }
+
+ write(priv, mod->base + LIN_DX_LO_REG, dx_lo);
+ write(priv, mod->base + LIN_DX_HI_REG, dx_hi);
+
+ for (unsigned int i = 0; i < RPPX1_LIN_DEGAMMA_CURVE_NUM; i++) {
+ write(priv, mod->base + LIN_R_Y_REG(i), cfg->curve_r[i] & mask);
+ write(priv, mod->base + LIN_G_Y_REG(i), cfg->curve_g[i] & mask);
+ write(priv, mod->base + LIN_B_Y_REG(i), cfg->curve_b[i] & mask);
+ }
+
+ write(priv, mod->base + LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_lin_ops = {
+ .probe = rppx1_lin_probe,
+ .start = rppx1_lin_start,
+ .fill_params = rppx1_lin_fill_params,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
new file mode 100644
index 000000000000..8badeca23e24
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define LSC_VERSION_REG 0x0000
+
+#define LSC_CTRL_REG 0x0004
+#define LSC_CTRL_LSC_EN BIT(0)
+
+#define LSC_R_TABLE_ADDR_REG 0x0008
+#define LSC_GR_TABLE_ADDR_REG 0x000c
+#define LSC_B_TABLE_ADDR_REG 0x0010
+#define LSC_GB_TABLE_ADDR_REG 0x0014
+#define LSC_R_TABLE_DATA_REG 0x0018
+#define LSC_GR_TABLE_DATA_REG 0x001c
+#define LSC_B_TABLE_DATA_REG 0x0020
+#define LSC_GB_TABLE_DATA_REG 0x0024
+#define LSC_XGRAD_01_REG 0x0028
+#define LSC_XGRAD_23_REG 0x002c
+#define LSC_XGRAD_45_REG 0x0030
+#define LSC_XGRAD_67_REG 0x0034
+#define LSC_XGRAD_89_REG 0x0038
+#define LSC_XGRAD_1011_REG 0x003c
+#define LSC_XGRAD_1213_REG 0x0040
+#define LSC_XGRAD_1415_REG 0x0044
+#define LSC_YGRAD_01_REG 0x0048
+#define LSC_YGRAD_23_REG 0x004c
+#define LSC_YGRAD_45_REG 0x0050
+#define LSC_YGRAD_67_REG 0x0054
+#define LSC_YGRAD_89_REG 0x0058
+#define LSC_YGRAD_1011_REG 0x005c
+#define LSC_YGRAD_1213_REG 0x0060
+#define LSC_YGRAD_1415_REG 0x0064
+#define LSC_XSIZE_01_REG 0x0068
+#define LSC_XSIZE_23_REG 0x006c
+#define LSC_XSIZE_45_REG 0x0070
+#define LSC_XSIZE_67_REG 0x0074
+#define LSC_XSIZE_89_REG 0x0078
+#define LSC_XSIZE_1011_REG 0x007c
+#define LSC_XSIZE_1213_REG 0x0080
+#define LSC_XSIZE_1415_REG 0x0084
+#define LSC_YSIZE_01_REG 0x0088
+#define LSC_YSIZE_23_REG 0x008c
+#define LSC_YSIZE_45_REG 0x0090
+#define LSC_YSIZE_67_REG 0x0094
+#define LSC_YSIZE_89_REG 0x0098
+#define LSC_YSIZE_1011_REG 0x009c
+#define LSC_YSIZE_1213_REG 0x00a0
+#define LSC_YSIZE_1415_REG 0x00a4
+#define LSC_TABLE_SEL_REG 0x00a8
+#define LSC_STATUS_REG 0x00ac
+
+#define LSC_R_TABLE_DATA_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 12))
+#define LSC_GRAD_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 16))
+#define LSC_SIZE_VALUE(v1, v2) (((v1) & 0x1ff) | (((v2) & 0x1ff) << 16))
+
+static int rppx1_lsc_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, LSC_VERSION_REG) != 0x04)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+rppx1_lsc_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_lsc_params *cfg = &block->lsc;
+ const __u16 *v;
+
+ /* Always disable module as it needs be disabled before configuring. */
+ write(priv, mod->base + LSC_CTRL_REG, 0);
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)
+ return 0;
+
+ /*
+ * Program the color correction sectors.
+ *
+ * There are two tables to one can program and switch between. As the
+ * RPPX1 supports preparing a buffer of commands to be applied later
+ * only use table 0. This works as long as the ISP is not used in
+ * inline-mode.
+ *
+ * For inline-mode support using DMA for configuration is not possible
+ * so this is not an issue, but needs to be address if inline-mode
+ * support is added to the driver.
+ */
+
+ /* Start writing at beginning of table 0. */
+ write(priv, mod->base + LSC_R_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_GR_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_B_TABLE_ADDR_REG, 0);
+ write(priv, mod->base + LSC_GB_TABLE_ADDR_REG, 0);
+
+ /* Program data tables. */
+ for (unsigned int i = 0; i < RPPX1_LSC_SAMPLES_MAX; i++) {
+ const __u16 *r = cfg->r_data[i];
+ const __u16 *gr = cfg->gr_data[i];
+ const __u16 *b = cfg->b_data[i];
+ const __u16 *gb = cfg->gb_data[i];
+ unsigned int j;
+
+ for (j = 0; j < RPPX1_LSC_SAMPLES_MAX - 1; j += 2) {
+ write(priv, mod->base + LSC_R_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(r[j], r[j + 1]));
+ write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gr[j], gr[j + 1]));
+ write(priv, mod->base + LSC_B_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(b[j], b[j + 1]));
+ write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gb[j], gb[j + 1]));
+ }
+
+ write(priv, mod->base + LSC_R_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(r[j], 0));
+ write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gr[j], 0));
+ write(priv, mod->base + LSC_B_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(b[j], 0));
+ write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
+ LSC_R_TABLE_DATA_VALUE(gb[j], 0));
+ }
+
+ /* Activate table 0. */
+ write(priv, mod->base + LSC_TABLE_SEL_REG, 0);
+
+ /*
+ * Program X- and Y- sizes, and gradients.
+ */
+
+ v = cfg->x_grad;
+ write(priv, mod->base + LSC_XGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_XGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_XGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_XGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_XGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ v = cfg->y_grad;
+ write(priv, mod->base + LSC_YGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_YGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_YGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_YGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_YGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ v = cfg->x_sect_size;
+ write(priv, mod->base + LSC_XSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_XSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_XSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_XSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_XSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ v = cfg->y_sect_size;
+ write(priv, mod->base + LSC_YSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
+ write(priv, mod->base + LSC_YSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
+ write(priv, mod->base + LSC_YSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
+ write(priv, mod->base + LSC_YSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
+ write(priv, mod->base + LSC_YSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
+ write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
+ write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
+ write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
+
+ /* Enable module. */
+ write(priv, mod->base + LSC_CTRL_REG, LSC_CTRL_LSC_EN);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_lsc_ops = {
+ .probe = rppx1_lsc_probe,
+ .fill_params = rppx1_lsc_fill_params,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
new file mode 100644
index 000000000000..9a71155e7e13
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define LTM_VERSION_REG 0x0000
+
+#define LTM_CTRL_REG 0x0004
+#define LTM_CTRL_LTM_ENABLE BIT(0)
+
+#define LTM_RGB_WEIGHTS_REG 0x0008
+#define LTM_CLB_LINESIZE_REG 0x000c
+#define LTM_TONECURVE_1_REG 0x0010
+#define LTM_TONECURVE_2_REG 0x0014
+#define LTM_TONECURVE_3_REG 0x0018
+#define LTM_TONECURVE_4_REG 0x001c
+#define LTM_TONECURVE_5_REG 0x0020
+#define LTM_TONECURVE_6_REG 0x0024
+#define LTM_TONECURVE_YM_REG(n) (0x0028 + (4 * (n)))
+#define LTM_L0W_REG 0x00ec
+#define LTM_L0W_R_REG 0x00f0
+#define LTM_L0D_REG 0x00f4
+#define LTM_L0D_R_REG 0x00f8
+#define LTM_KMIND_REG 0x00fc
+#define LTM_KMAXD_REG 0x0100
+#define LTM_KDIFFD_REG 0x0104
+#define LTM_KDIFFD_R_REG 0x0108
+#define LTM_KW_REG 0x010c
+#define LTM_KW_R_REG 0x0110
+#define LTM_CGAIN_REG 0x0114
+#define LTM_LPRCH_R_HIGH_REG 0x0118
+#define LTM_LPRCH_R_LOW_REG 0x011c
+
+static int rppx1_ltm_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, LTM_VERSION_REG) != 8)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ltm_ops = {
+ .probe = rppx1_ltm_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
new file mode 100644
index 000000000000..c874b8fa8999
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define LTM_MEAS_VERSION_REG 0x0000
+
+#define LTM_MEAS_CTRL_REG 0x0004
+#define LTM_MEAS_CTRL_LTM_MEAS_ENABLE BIT(0)
+
+#define LTM_MEAS_RGB_WEIGHTS_REG 0x0008
+#define LTM_MEAS_H_OFFS_REG 0x000c
+#define LTM_MEAS_V_OFFS_REG 0x0010
+#define LTM_MEAS_H_SIZE_REG 0x0014
+#define LTM_MEAS_V_SIZE_REG 0x0018
+
+#define LTM_MEAS_PRC_THRESH_NUM 8
+#define LTM_MEAS_PRC_THRESH_REG(n) (0x001c + (4 * (n)))
+
+#define LTM_MEAS_PRC_REG_NUM 8
+#define LTM_MEAS_PRC_REG(n) (0x003c + (4 * (n)))
+
+#define LTM_MEAS_L_MIN_REG 0x005c
+#define LTM_MEAS_L_MAX_REG 0x0060
+#define LTM_MEAS_L_GMEAN_REG 0x0064
+
+static int rppx1_ltmmeas_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, LTM_MEAS_VERSION_REG) != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_ltmmeas_ops = {
+ .probe = rppx1_ltmmeas_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
new file mode 100644
index 000000000000..973b82a40d7c
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define OUT_IF_VERSION_REG 0x0000
+
+#define OUT_IF_ON_REG 0x0004
+#define OUT_IF_ON_RPP_ON BIT(0)
+
+#define OUT_IF_OFF_REG 0x0008
+
+#define OUT_IF_NR_FRAMES_REG 0x000c
+#define OUT_IF_NR_FRAMES_NR_FRAMES GENMASK(9, 0)
+
+#define OUT_IF_NR_FRAMES_CNT_REG 0x0010
+#define FLAGS_SHD_REG 0x0018
+
+static int rppx1_outif_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, OUT_IF_VERSION_REG) != 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_outif_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ rpp_module_clrset(mod, OUT_IF_NR_FRAMES_REG,
+ OUT_IF_NR_FRAMES_NR_FRAMES, 0);
+
+ rpp_module_write(mod, OUT_IF_ON_REG, OUT_IF_ON_RPP_ON);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_outif_ops = {
+ .probe = rppx1_outif_probe,
+ .start = rppx1_outif_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
new file mode 100644
index 000000000000..ac8b71867137
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define OUTREGS_VERSION_REG 0x0000
+
+#define OUT_MODE_REG 0x0004
+#define OUT_MODE_UNSELECTED_MODE_MASK GENMASK(11, 8)
+#define OUT_MODE_UNSELECTED_MODE_MAIN (0x1 << 8)
+#define OUT_MODE_UNSELECTED_MODE_PRE1 (0x2 << 8)
+#define OUT_MODE_UNSELECTED_MODE_PRE2 (0x4 << 8)
+#define OUT_MODE_IN_SEL_MASK GENMASK(3, 0)
+#define OUT_MODE_IN_SEL_MAIN 1
+#define OUT_MODE_IN_SEL_PRE1 2
+#define OUT_MODE_IN_SEL_PRE2 4
+
+#define OUT_CONV_422_METHOD_REG 0x0008
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_MASK GENMASK(1, 0)
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1 0
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED2 1
+#define OUT_CONV_422_METHOD_CONV_422_METHOD_NON_CO_SITED 2
+
+#define OUTREGS_FORMAT_REG 0x000c
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_MASK GENMASK(1, 0)
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_RGB 0
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422 1
+#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV420 2
+
+static int rppx1_outregs_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, OUTREGS_VERSION_REG) != 2)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rppx1_outregs_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ u32 format;
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_YUYV12_1X24:
+ format = OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ format = OUTREGS_FORMAT_OUTPUT_FORMAT_RGB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rpp_module_clrset(mod, OUT_MODE_REG,
+ OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MASK,
+ OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MAIN);
+
+ rpp_module_clrset(mod, OUT_CONV_422_METHOD_REG,
+ OUT_CONV_422_METHOD_CONV_422_METHOD_MASK,
+ OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1);
+
+ rpp_module_clrset(mod, OUTREGS_FORMAT_REG,
+ OUTREGS_FORMAT_OUTPUT_FORMAT_MASK, format);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_outregs_ops = {
+ .probe = rppx1_outregs_probe,
+ .start = rppx1_outregs_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
new file mode 100644
index 000000000000..0a891caf3c52
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define RMAP_DATA_VERSION_REG 0x0000
+
+#define RMAP_CTRL_REG 0x0004
+#define RMAP_CTRL_BYPASS_LONG BIT(2)
+
+#define RMAP_WBTHRESHOLD_LONG_REG 0x0008
+#define RMAP_WBTHRESHOLD_SHORT_REG 0x000c
+#define RMAP_RESERVED_1_REG 0x0010
+#define RMAP_WBGAIN_LONG_RED_REG 0x0014
+#define RMAP_WBGAIN_LONG_BLUE_REG 0x0018
+#define RMAP_WBGAIN_SHORT_RED_REG 0x001c
+#define RMAP_WBGAIN_SHORT_BLUE_REG 0x0020
+#define RMAP_RESERVED_2_REG 0x0024
+#define RMAP_RESERVED_3_REG 0x0028
+#define RMAP_MAP_FAC_SHORT_REG 0x002c
+#define RMAP_RESERVED_4_REG 0x0030
+#define RMAP_MIN_THRES_SHORT_REG 0x0034
+#define RMAP_MAX_THRES_SHORT_REG 0x0038
+#define RMAP_STEPSIZE_SHORT_REG 0x003c
+#define RMAP_MIN_THRES_LONG_REG 0x0040
+#define RMAP_MAX_THRES_LONG_REG 0x0044
+#define RMAP_STEPSIZE_LONG_REG 0x0048
+#define RMAP_CLB_LINESIZE_REG 0x004c
+
+static int rppx1_rmap_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, RMAP_DATA_VERSION_REG)) {
+ case 8:
+ /* low: 12-bit, high: 20-bit. */
+ break;
+ case 9:
+ /* low: 12-bit, high: 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rppx1_rmap_start(struct rpp_module *mod,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ /* Bypass radiance mapping and use the long exposure channel (PRE1). */
+ rpp_module_write(mod, RMAP_CTRL_REG, RMAP_CTRL_BYPASS_LONG);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_rmap_ops = {
+ .probe = rppx1_rmap_probe,
+ .start = rppx1_rmap_start,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
new file mode 100644
index 000000000000..ef709d2a6ee9
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define RMAP_MEAS_VERSION_REG 0x0000
+#define RMAP_MEAS_MODE_REG 0x0004
+#define RMAP_MEAS_SUBSAMPLING_REG 0x0008
+#define RMAP_MEAS_RESERVED_1_REG 0x000c
+#define RMAP_MEAS_MIN_THRES_SHORT_REG 0x0010
+#define RMAP_MEAS_MAX_THRES_SHORT_REG 0x0014
+#define RMAP_MEAS_MAX_THRES_LONG_REG 0x0018
+#define RMAP_MEAS_H_OFFS_REG 0x001c
+#define RMAP_MEAS_V_OFFS_REG 0x0020
+#define RMAP_MEAS_H_SIZE_REG 0x0024
+#define RMAP_MEAS_V_SIZE_REG 0x0028
+#define RMAP_MEAS_LAST_MEAS_LINE_REG 0x002c
+#define RMAP_MEAS_LS_RESULTSHORT0_REG 0x0030
+#define RMAP_MEAS_LS_RESULTLONG0_REG 0x0034
+#define RMAP_MEAS_RESERVED_2_REG 0x0038
+#define RMAP_MEAS_RESERVED_3_REG 0x003c
+#define RMAP_MEAS_LS_RESULTSHORT1_REG 0x0040
+#define RMAP_MEAS_LS_RESULTLONG1_REG 0x0044
+#define RMAP_MEAS_RESERVED_4_REG 0x0048
+#define RMAP_MEAS_RESERVED_5_REG 0x004c
+
+static int rppx1_rmapmeas_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, RMAP_MEAS_VERSION_REG)) {
+ case 3:
+ /* low: 12-bit, high: 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_rmapmeas_ops = {
+ .probe = rppx1_rmapmeas_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
new file mode 100644
index 000000000000..c155418129e5
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define SHRPCNR_VERSION_REG 0x0000
+
+#define SHRPCNR_CTRL_REG 0x0004
+#define SHRPCNR_CTRL_CAD_EN BIT(3)
+#define SHRPCNR_CTRL_DESAT_EN BIT(2)
+#define SHRPCNR_CTRL_CNR_EN BIT(1)
+#define SHRPCNR_CTRL_SHARPEN_EN BIT(0)
+
+#define SHRPCNR_PARAM_REG 0x0008
+#define SHRPCNR_PARAM_SHARP_FACTOR_MASK GENMASK(19, 12)
+#define SHRPCNR_PARAM_CORING_THR_MASK GENMASK(11, 0)
+
+#define SHRPCNR_MAT_1_REG 0x000c
+#define SHRPCNR_MAT_2_REG 0x0010
+#define SHRPCNR_CLB_LINESIZE_REG 0x0014
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_0_REG 0x0018
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_1_REG 0x001c
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_2_REG 0x0020
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_3_REG 0x0024
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_4_REG 0x0028
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_5_REG 0x002c
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_6_REG 0x0030
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_7_REG 0x0034
+#define SHRPCNR_YUV2RGB_CCOR_COEFF_8_REG 0x0038
+#define SHRPCNR_YUV2RGB_CCOR_OFFSET_R_REG 0x003c
+#define SHRPCNR_YUV2RGB_CCOR_OFFSET_G_REG 0x0040
+#define SHRPCNR_YUV2RGB_CCOR_OFFSET_B_REG 0x0044
+
+#define SHRPCNR_CNR_THRES_REG 0x0048
+#define SHRPCNR_CNR_THRES_CNR_THRES_CR_MASK GENMASK(27, 16)
+#define SHRPCNR_CNR_THRES_CNR_THRES_CB_MASK GENMASK(11, 0)
+
+#define SHRPCNR_CRED_THRES_REG 0x004c
+#define SHRPCNR_CRED_SLOPE_REG 0x0050
+#define SHRPCNR_CAD_RESTORE_LVL_REG 0x0054
+#define SHRPCNR_CAD_THRESH_V_UNEG_REG 0x0058
+#define SHRPCNR_CAD_THRESH_V_UPOS_REG 0x005c
+#define SHRPCNR_CAD_THRESH_U_REG 0x0060
+
+static int rppx1_shrp_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, SHRPCNR_VERSION_REG)) {
+ case 2:
+ /* 12-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_shrp_ops = {
+ .probe = rppx1_shrp_probe,
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
new file mode 100644
index 000000000000..f0a943556238
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define AWB_MEAS_VERSION_REG 0x0000
+
+#define AWB_MEAS_PROP_REG 0x0004
+#define AWB_MEAS_PROP_MEAS_MODE_RGB BIT(16) /* 0: YCbCr 1: RGB */
+#define AWB_MEAS_PROP_YMAX BIT(2)
+#define AWB_MEAS_PROP_AWB_MODE_ON BIT(1)
+
+#define AWB_MEAS_H_OFFS_REG 0x0008
+#define AWB_MEAS_V_OFFS_REG 0x000c
+#define AWB_MEAS_H_SIZE_REG 0x0010
+#define AWB_MEAS_V_SIZE_REG 0x0014
+#define AWB_MEAS_FRAMES_REG 0x0018
+#define AWB_MEAS_REF_CB_MAX_B_REG 0x001c
+#define AWB_MEAS_REF_CR_MAX_R_REG 0x0020
+#define AWB_MEAS_MAX_Y_REG 0x0024
+#define AWB_MEAS_MIN_Y_MAX_G_REG 0x0028
+#define AWB_MEAS_MAX_CSUM_REG 0x002c
+#define AWB_MEAS_MIN_C_REG 0x0030
+#define AWB_MEAS_WHITE_CNT_REG 0x0034
+#define AWB_MEAS_MEAN_Y_G_REG 0x0038
+#define AWB_MEAS_MEAN_CB_B_REG 0x003c
+#define AWB_MEAS_MEAN_CR_R_REG 0x0040
+
+#define AWB_MEAS_CCOR_COEFF_NUM 9
+#define AWB_MEAS_CCOR_COEFF_REG(n) (0x0044 + (4 * (n)))
+
+#define AWB_MEAS_CCOR_OFFSET_R_REG 0x0068
+#define AWB_MEAS_CCOR_OFFSET_G_REG 0x006c
+#define AWB_MEAS_CCOR_OFFSET_B_REG 0x0070
+
+static int rppx1_wbmeas_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ switch (rpp_module_read(mod, AWB_MEAS_VERSION_REG)) {
+ case 1:
+ /* 8-bit. */
+ break;
+ case 2:
+ /* 20-bit. */
+ break;
+ case 3:
+ /* 24-bit. */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+rppx1_wbmeas_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_wbmeas_params *cfg = &block->wbmeas;
+ u32 awb_meas_props;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + AWB_MEAS_PROP_REG, 0);
+ return 0;
+ }
+
+ /* Program measurement window. */
+ write(priv, mod->base + AWB_MEAS_H_OFFS_REG, cfg->wnd.h_offs);
+ write(priv, mod->base + AWB_MEAS_V_OFFS_REG, cfg->wnd.v_offs);
+ write(priv, mod->base + AWB_MEAS_H_SIZE_REG, cfg->wnd.h_size);
+ write(priv, mod->base + AWB_MEAS_V_SIZE_REG, cfg->wnd.v_size);
+
+ /* Set number of frames to sample. */
+ write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames);
+
+ if (cfg->mode == RPPX1_WBMEAS_MODE_YCBCR) {
+ write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+ cfg->ref_cb_max_b);
+ write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+ cfg->ref_cr_max_r);
+ write(priv, mod->base + AWB_MEAS_MAX_Y_REG, cfg->max_y);
+ write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+ cfg->min_y_max_g);
+ write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG, cfg->max_csum);
+ write(priv, mod->base + AWB_MEAS_MIN_C_REG, cfg->min_c);
+
+ /*
+ * Program the color conversion matrix coefficients and the
+ * per-color channel offsets.
+ */
+ for (unsigned int i = 0; i < 3; i++) {
+ for (unsigned int j = 0; j < 3; j++) {
+ unsigned int index = i * 3 + j;
+
+ write(priv,
+ mod->base + AWB_MEAS_CCOR_COEFF_REG(index),
+ cfg->ccor_coeff[i][j]);
+ }
+ }
+
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG,
+ cfg->ccor_offs[0]);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG,
+ cfg->ccor_offs[1]);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG,
+ cfg->ccor_offs[2]);
+
+ awb_meas_props = cfg->ymax_cmp ? AWB_MEAS_PROP_YMAX : 0;
+ } else {
+ write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG,
+ cfg->ref_cb_max_b);
+ write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG,
+ cfg->ref_cr_max_r);
+ write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG,
+ cfg->min_y_max_g);
+
+ /* Bypass color conversion matrix and color offsets. */
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0x0000);
+ write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x1000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00000000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00000000);
+ write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00000000);
+
+ awb_meas_props = AWB_MEAS_PROP_MEAS_MODE_RGB;
+ }
+
+ write(priv, mod->base + AWB_MEAS_PROP_REG,
+ awb_meas_props | AWB_MEAS_PROP_AWB_MODE_ON);
+
+ return 0;
+}
+
+static int rppx1_wbmeas_fill_stats(struct rpp_module *mod,
+ union rppx1_stats_block *block)
+{
+ struct rppx1_wbmeas_stats *stats = &block->wbmeas;
+
+ /* Return measurements at native hardware precision. */
+ stats->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG);
+ stats->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG);
+ stats->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG);
+ stats->mean_cr_or_r = rpp_module_read(mod, AWB_MEAS_MEAN_CR_R_REG);
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_wbmeas_ops = {
+ .probe = rppx1_wbmeas_probe,
+ .fill_params = rppx1_wbmeas_fill_params,
+ .fill_stats = rppx1_wbmeas_fill_stats
+};
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
new file mode 100644
index 000000000000..cc6f977c8a03
--- /dev/null
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include "rpp_module.h"
+
+#define XYZ2LUV_VERSION_REG 0x0000
+#define XYZ2LUV_U_REF_REG 0x0004
+#define XYZ2LUV_V_REF_REG 0x0008
+#define XYZ2LUV_LUMA_OUT_FAC_REG 0x000c
+#define XYZ2LUV_CHROMA_OUT_FAC_REG 0x0010
+
+static int rppx1_xyz2luv_probe(struct rpp_module *mod)
+{
+ /* Version check. */
+ if (rpp_module_read(mod, XYZ2LUV_VERSION_REG) != 4)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct rpp_module_ops rppx1_xyz2luv_ops = {
+ .probe = rppx1_xyz2luv_probe,
+};
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
index d30a05782ab9..96a66aadf0cd 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
@@ -321,7 +321,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd,
/* Enable mux settings */
for (index = 0; index < ctrl->num_sets; index++) {
set = &ctrl->sets[index];
- cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, set->value);
+ cmdq_pkt_write_subsys(&cmd->pkt, set->subsys_id, set->reg, set->reg, set->value);
}
/* Config sub-frame information */
for (index = (num_comp - 1); index >= 0; index--) {
@@ -376,7 +376,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd,
/* Disable mux settings */
for (index = 0; index < ctrl->num_sets; index++) {
set = &ctrl->sets[index];
- cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, 0);
+ cmdq_pkt_write_subsys(&cmd->pkt, set->subsys_id, set->reg, set->reg, 0);
}
return 0;
@@ -628,7 +628,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp,
goto err_free_path;
}
cmdq_pkt_eoc(&cmd->pkt);
- cmdq_pkt_jump_rel(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]);
+ cmdq_pkt_jump_rel_temp(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]);
for (i = 0; i < num_comp; i++) {
s32 inner_id = MDP_COMP_NONE;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h
index 681906c16419..c6fc180950f2 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h
@@ -12,14 +12,14 @@
#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask) \
do { \
typeof(mask) (m) = (mask); \
- cmdq_pkt_write_mask(&((cmd)->pkt), id, (base) + (ofst), \
- (val), \
+ cmdq_pkt_write_mask_subsys(&((cmd)->pkt), (id), (base), \
+ (base) + (ofst), (val), \
(((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
(0xffffffff) : (m)); \
} while (0)
#define MM_REG_WRITE(cmd, id, base, ofst, val) \
- cmdq_pkt_write(&((cmd)->pkt), id, (base) + (ofst), (val))
+ cmdq_pkt_write_subsys(&((cmd)->pkt), (id), (base), (base) + (ofst), (val))
#define MM_REG_WAIT(cmd, evt) \
do { \
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index 8f4da4cf55d2..f412aadb5996 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -294,7 +294,11 @@ static int mdp_probe(struct platform_device *pdev)
goto err_destroy_clock_wq;
}
mdp->scp = platform_get_drvdata(mm_pdev);
- put_device(&mm_pdev->dev);
+ }
+
+ if (!mdp->scp) {
+ ret = -EPROBE_DEFER;
+ goto err_destroy_clock_wq;
}
mdp->rproc_handle = scp_get_rproc(mdp->scp);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
index adbacabdbebc..ba7a1cd998b9 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
@@ -974,10 +974,10 @@ static int vdec_vp9_slice_setup_pfc(struct vdec_vp9_slice_instance *instance,
return 0;
}
-static int vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *instance,
- struct vdec_vp9_slice_vsi *vsi,
- struct mtk_vcodec_mem *bs,
- struct vdec_lat_buf *lat_buf)
+static void vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *instance,
+ struct vdec_vp9_slice_vsi *vsi,
+ struct mtk_vcodec_mem *bs,
+ struct vdec_lat_buf *lat_buf)
{
int i;
@@ -1011,12 +1011,10 @@ static int vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *insta
vsi->row_info.buf = 0;
vsi->row_info.size = 0;
-
- return 0;
}
-static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *instance,
- struct vdec_vp9_slice_vsi *vsi)
+static void vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *instance,
+ struct vdec_vp9_slice_vsi *vsi)
{
struct vdec_vp9_slice_frame_ctx *frame_ctx;
struct vdec_vp9_slice_uncompressed_header *uh;
@@ -1032,8 +1030,6 @@ static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *inst
else
frame_ctx = vdec_vp9_slice_default_frame_ctx;
memcpy(instance->prob.va, frame_ctx, sizeof(*frame_ctx));
-
- return 0;
}
static void vdec_vp9_slice_setup_seg_buffer(struct vdec_vp9_slice_instance *instance,
@@ -1166,17 +1162,13 @@ static int vdec_vp9_slice_setup_lat(struct vdec_vp9_slice_instance *instance,
if (ret)
goto err;
- ret = vdec_vp9_slice_setup_lat_buffer(instance, vsi, bs, lat_buf);
- if (ret)
- goto err;
+ vdec_vp9_slice_setup_lat_buffer(instance, vsi, bs, lat_buf);
vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]);
/* setup prob/tile buffers for LAT */
- ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi);
- if (ret)
- goto err;
+ vdec_vp9_slice_setup_prob_buffer(instance, vsi);
ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs);
if (ret)
@@ -1808,10 +1800,7 @@ static int vdec_vp9_slice_setup_single(struct vdec_vp9_slice_instance *instance,
vdec_vp9_slice_setup_single_buffer(instance, pfc, vsi, bs, fb);
vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]);
-
- ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi);
- if (ret)
- goto err;
+ vdec_vp9_slice_setup_prob_buffer(instance, vsi);
ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs);
if (ret)
diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
index b0302dfc3278..7383341ec51d 100644
--- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
@@ -598,13 +598,8 @@ static int __maybe_unused xisc_runtime_suspend(struct device *dev)
static int __maybe_unused xisc_runtime_resume(struct device *dev)
{
struct isc_device *isc = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(isc->hclock);
- if (ret)
- return ret;
- return ret;
+ return clk_prepare_enable(isc->hclock);
}
static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index e8545761b5ff..0ae5f35e017c 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -318,6 +318,7 @@ static const struct mxc_isi_plat_data mxc_imx95_data = {
.model = MXC_ISI_IMX95,
.num_ports = 4,
.num_channels = 8,
+ .num_vc = 8,
.reg_offset = 0x10000,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
@@ -329,6 +330,7 @@ static const struct mxc_isi_plat_data mxc_imx8qm_data = {
.model = MXC_ISI_IMX8QM,
.num_ports = 5,
.num_channels = 8,
+ .num_vc = 4,
.reg_offset = 0x10000,
.ier_reg = &mxc_imx8_isi_ier_qm,
.set_thd = &mxc_imx8_isi_thd_v1,
@@ -340,6 +342,7 @@ static const struct mxc_isi_plat_data mxc_imx8qxp_data = {
.model = MXC_ISI_IMX8QXP,
.num_ports = 5,
.num_channels = 6,
+ .num_vc = 4,
.reg_offset = 0x10000,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
index 7547a6559d4c..58b47335afc7 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -170,6 +170,7 @@ struct mxc_isi_plat_data {
enum model model;
unsigned int num_ports;
unsigned int num_channels;
+ unsigned int num_vc; /* Number of VCs, 0 = no VC support */
unsigned int reg_offset;
const struct mxc_isi_ier_reg *ier_reg;
const struct mxc_isi_set_thd *set_thd;
@@ -185,7 +186,7 @@ struct mxc_isi_dma_buffer {
};
struct mxc_isi_input {
- unsigned int enable_count;
+ u64 enabled_streams;
};
struct mxc_isi_crossbar {
@@ -258,6 +259,14 @@ struct mxc_isi_pipe {
u8 acquired_res;
u8 chained_res;
bool chained;
+
+ unsigned int input;
+ /*
+ * Stream on the connected crossbar input, expressed as a bitmask. Zero
+ * when the pipeline is disabled, a single bit set when the pipeline is
+ * enabled (as each pipeline processes a single stream).
+ */
+ u64 input_stream;
};
struct mxc_isi_m2m {
@@ -378,6 +387,7 @@ void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe);
void mxc_isi_channel_config(struct mxc_isi_pipe *pipe,
enum mxc_isi_input_id input,
+ unsigned int vc,
const struct v4l2_area *in_size,
const struct v4l2_area *scale,
const struct v4l2_rect *crop,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
index c580c831972e..7bb1335f2110 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -106,8 +106,20 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
if (ret)
return ret;
- /* The memory input can be routed to the first pipeline only. */
- for_each_active_route(&state->routing, route) {
+ /*
+ * Validate routes against hardware constraints:
+ * - SOURCE stream must be 0 (pipes are hardcoded to stream 0)
+ * - Memory input can only route to the first pipeline
+ */
+ for_each_active_route(routing, route) {
+ if (route->source_stream != 0) {
+ dev_dbg(xbar->isi->dev,
+ "route to pipe %u must use source_stream=0, got %u\n",
+ route->source_pad - xbar->num_sinks,
+ route->source_stream);
+ return -ENXIO;
+ }
+
if (route->sink_pad == xbar->num_sinks - 1 &&
route->source_pad != xbar->num_sinks) {
dev_dbg(xbar->isi->dev,
@@ -145,10 +157,10 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar,
*/
for_each_active_route(&state->routing, route) {
if (route->source_pad != source_pad ||
- !(source_streams & BIT(route->source_stream)))
+ !(source_streams & BIT_ULL(route->source_stream)))
continue;
- sink_streams |= BIT(route->sink_stream);
+ sink_streams |= BIT_ULL(route->sink_stream);
sink_pad = route->sink_pad;
}
@@ -318,11 +330,32 @@ static int mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
return __mxc_isi_crossbar_set_routing(sd, state, routing);
}
+/*
+ * Check if a stream on a sink pad is in used by any of the ISI pipelines. The
+ * sink_streams argument is a bitmask that must have a single bit set (enforced
+ * by mxc_isi_crossbar_xlate_streams() translating the single stream mask of the
+ * pipeline to a single stream on the crossbar input side).
+ */
+static bool mxc_isi_crossbar_stream_in_use(const struct mxc_isi_crossbar *xbar,
+ unsigned int sink_pad, u64 sink_streams)
+{
+ for (unsigned int i = 0; i < xbar->isi->pdata->num_channels; ++i) {
+ const struct mxc_isi_pipe *pipe = &xbar->isi->pipes[i];
+
+ if (pipe->input == sink_pad &&
+ pipe->input_stream == sink_streams)
+ return true;
+ }
+
+ return false;
+}
+
static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
+ struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sinks];
struct v4l2_subdev *remote_sd;
struct mxc_isi_input *input;
u64 sink_streams;
@@ -339,29 +372,44 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
input = &xbar->inputs[sink_pad];
/*
- * TODO: Track per-stream enable counts to support multiplexed
- * streams.
+ * Check if any other pipe already receives the same input stream.
+ * If so, just record this pipe's usage and return.
*/
- if (!input->enable_count) {
+ if (mxc_isi_crossbar_stream_in_use(xbar, sink_pad, sink_streams)) {
+ pipe->input = sink_pad;
+ pipe->input_stream = sink_streams;
+ return 0;
+ }
+
+ /* Enable the gasket when the first stream is enabled for this input. */
+ if (!input->enabled_streams) {
ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd,
remote_pad, sink_pad);
if (ret)
return ret;
+ }
- ret = v4l2_subdev_enable_streams(remote_sd, remote_pad,
- sink_streams);
- if (ret) {
- dev_err(xbar->isi->dev,
- "failed to enable streams 0x%llx on '%s':%u: %d\n",
- sink_streams, remote_sd->name, remote_pad, ret);
- mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
- return ret;
- }
+ ret = v4l2_subdev_enable_streams(remote_sd, remote_pad, sink_streams);
+ if (ret) {
+ dev_err(xbar->isi->dev,
+ "failed to enable streams 0x%llx on '%s':%u: %d\n",
+ sink_streams, remote_sd->name, remote_pad, ret);
+ goto err_gasket_disable;
}
- input->enable_count++;
+ input->enabled_streams |= sink_streams;
+
+ /* Record the input and stream for this pipe. */
+ pipe->input = sink_pad;
+ pipe->input_stream = sink_streams;
return 0;
+
+err_gasket_disable:
+ if (!input->enabled_streams)
+ mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
+
+ return ret;
}
static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
@@ -369,6 +417,7 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
u32 pad, u64 streams_mask)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
+ struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sinks];
struct v4l2_subdev *remote_sd;
struct mxc_isi_input *input;
u64 sink_streams;
@@ -384,18 +433,27 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
input = &xbar->inputs[sink_pad];
- input->enable_count--;
+ /* Clear the input and stream for this pipe. */
+ pipe->input = UINT_MAX;
+ pipe->input_stream = 0;
- if (!input->enable_count) {
- ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
- sink_streams);
- if (ret)
- dev_err(xbar->isi->dev,
- "failed to disable streams 0x%llx on '%s':%u: %d\n",
- sink_streams, remote_sd->name, remote_pad, ret);
+ /*
+ * Check if any other pipe receives the same input stream. If so we
+ * can't disable it yet, so return immediately.
+ */
+ if (mxc_isi_crossbar_stream_in_use(xbar, sink_pad, sink_streams))
+ return 0;
+ ret = v4l2_subdev_disable_streams(remote_sd, remote_pad, sink_streams);
+ if (ret)
+ dev_err(xbar->isi->dev,
+ "failed to disable streams 0x%llx on '%s':%u: %d\n",
+ sink_streams, remote_sd->name, remote_pad, ret);
+
+ input->enabled_streams &= ~sink_streams;
+
+ if (!input->enabled_streams)
mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
- }
return ret;
}
@@ -404,6 +462,7 @@ static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = {
.enum_mbus_code = mxc_isi_crossbar_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = mxc_isi_crossbar_set_fmt,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
.set_routing = mxc_isi_crossbar_set_routing,
.enable_streams = mxc_isi_crossbar_enable_streams,
.disable_streams = mxc_isi_crossbar_disable_streams,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
index 16b20ea2d1db..6aa760ce3605 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
@@ -11,8 +11,6 @@
#include "imx8-isi-core.h"
#include "imx8-isi-regs.h"
-#define ISI_DOWNSCALE_THRESHOLD 0x4000
-
static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg)
{
return readl(pipe->regs + reg);
@@ -118,8 +116,7 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,
* output (input / scale_factor) rounds up to exactly the desired
* output.
*/
- return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec),
- ISI_DOWNSCALE_THRESHOLD);
+ return DIV_ROUND_UP(from * 0x1000, to * *dec);
}
static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,
@@ -308,6 +305,7 @@ static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe)
static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
enum mxc_isi_input_id input,
+ unsigned int vc,
bool bypass)
{
u32 val;
@@ -319,6 +317,10 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
CHNL_CTRL_SRC_TYPE_MASK | CHNL_CTRL_MIPI_VC_ID_MASK |
CHNL_CTRL_SRC_INPUT_MASK);
+ /* Clear the VC_ID_1 bit on platforms supporting more than 4 VCs. */
+ if (pipe->isi->pdata->num_vc > 4)
+ val &= ~CHNL_CTRL_VC_ID_1_MASK;
+
/*
* If no scaling or color space conversion is needed, bypass the
* channel.
@@ -345,7 +347,14 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
} else {
val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE);
val |= CHNL_CTRL_SRC_INPUT(input);
- val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */
+ val |= CHNL_CTRL_MIPI_VC_ID(vc); /* FIXME: For CSI-2 only */
+
+ /*
+ * On platforms with more than 4 VCs (i.MX95), the VC ID is
+ * split across VC_ID_0 (bits 7:6) and VC_ID_1 (bit 16).
+ */
+ if (pipe->isi->pdata->num_vc > 4)
+ val |= CHNL_CTRL_VC_ID_1(vc >> 2);
}
mxc_isi_write(pipe, CHNL_CTRL, val);
@@ -355,6 +364,7 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe,
void mxc_isi_channel_config(struct mxc_isi_pipe *pipe,
enum mxc_isi_input_id input,
+ unsigned int vc,
const struct v4l2_area *in_size,
const struct v4l2_area *scale,
const struct v4l2_rect *crop,
@@ -381,7 +391,7 @@ void mxc_isi_channel_config(struct mxc_isi_pipe *pipe,
mxc_isi_channel_set_panic_threshold(pipe);
/* Channel control */
- mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass);
+ mxc_isi_channel_set_control(pipe, input, vc, csc_bypass && scaler_bypass);
}
void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
index de398b232d74..b729eecad0fe 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
@@ -144,7 +144,7 @@ static void mxc_isi_m2m_device_run(void *priv)
.height = ctx->queues.cap.format.height,
};
- mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM,
+ mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM, 0,
&in_size, &scale, &crop,
ctx->queues.out.info->encoding,
ctx->queues.cap.info->encoding);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
index 2d0843c86534..934f7b356258 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
@@ -84,6 +84,12 @@ static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = {
| BIT(MXC_ISI_PIPE_PAD_SOURCE),
.encoding = MXC_ISI_ENC_RAW,
}, {
+ .mbus_code = MEDIA_BUS_FMT_Y16_1X16,
+ .output = MEDIA_BUS_FMT_Y16_1X16,
+ .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
+ | BIT(MXC_ISI_PIPE_PAD_SOURCE),
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
.mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
.output = MEDIA_BUS_FMT_SBGGR8_1X8,
.pads = BIT(MXC_ISI_PIPE_PAD_SINK)
@@ -179,6 +185,30 @@ static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = {
.pads = BIT(MXC_ISI_PIPE_PAD_SINK)
| BIT(MXC_ISI_PIPE_PAD_SOURCE),
.encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .output = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
+ | BIT(MXC_ISI_PIPE_PAD_SOURCE),
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .output = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
+ | BIT(MXC_ISI_PIPE_PAD_SOURCE),
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .output = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
+ | BIT(MXC_ISI_PIPE_PAD_SOURCE),
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .output = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
+ | BIT(MXC_ISI_PIPE_PAD_SOURCE),
+ .encoding = MXC_ISI_ENC_RAW,
},
/* JPEG */
{
@@ -232,6 +262,47 @@ static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd)
return container_of(sd, struct mxc_isi_pipe, sd);
}
+static int mxc_isi_get_vc(struct mxc_isi_pipe *pipe)
+{
+ struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
+ struct device *dev = pipe->isi->dev;
+ struct v4l2_mbus_frame_desc fd = { };
+ unsigned int source_pad = xbar->num_sinks + pipe->id;
+ unsigned int num_vcs;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_subdev_call(&xbar->sd, pad, get_frame_desc,
+ source_pad, &fd);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get source frame desc from pad %u\n",
+ source_pad);
+ return ret;
+ }
+
+ /* Find stream 0 in the frame descriptor. */
+ for (i = 0; i < fd.num_entries; i++) {
+ if (fd.entry[i].stream == 0)
+ break;
+ }
+
+ if (i == fd.num_entries) {
+ dev_err(dev, "Failed to find stream from source frame desc\n");
+ return -EPIPE;
+ }
+
+ num_vcs = pipe->isi->pdata->num_vc ? : 1;
+
+ /* Check virtual channel range. */
+ if (fd.entry[i].bus.csi2.vc >= num_vcs) {
+ dev_err(dev, "Virtual channel %u exceeds maximum %u\n",
+ fd.entry[i].bus.csi2.vc, num_vcs - 1);
+ return -EPIPE;
+ }
+
+ return fd.entry[i].bus.csi2.vc;
+}
+
int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
{
struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
@@ -246,6 +317,7 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
struct v4l2_rect crop;
u32 input;
int ret;
+ int vc;
/*
* Find the connected input by inspecting the crossbar switch routing
@@ -280,8 +352,12 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
v4l2_subdev_unlock_state(state);
+ vc = mxc_isi_get_vc(pipe);
+ if (vc < 0)
+ return vc;
+
/* Configure the ISI channel. */
- mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,
+ mxc_isi_channel_config(pipe, input, vc, &in_size, &scale, &crop,
sink_info->encoding, src_info->encoding);
mxc_isi_channel_enable(pipe);
@@ -770,6 +846,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)
pipe->acquired_res = 0;
pipe->chained_res = 0;
pipe->chained = false;
+ pipe->input = UINT_MAX;
sd = &pipe->sd;
v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h
index 1b65eccdf0da..e795f4daf3ff 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h
@@ -6,6 +6,7 @@
#ifndef __IMX8_ISI_REGS_H__
#define __IMX8_ISI_REGS_H__
+#include <linux/bitfield.h>
#include <linux/bits.h>
/* ISI Registers Define */
@@ -19,9 +20,14 @@
#define CHNL_CTRL_CHAIN_BUF_NO_CHAIN 0
#define CHNL_CTRL_CHAIN_BUF_2_CHAIN 1
#define CHNL_CTRL_SW_RST BIT(24)
-#define CHNL_CTRL_BLANK_PXL(n) ((n) << 16)
-#define CHNL_CTRL_BLANK_PXL_MASK GENMASK(23, 16)
-#define CHNL_CTRL_MIPI_VC_ID(n) ((n) << 6)
+/*
+ * CHNL_CTRL_BLANK_PXL: i.MX8{QM,QXP} only
+ * CHNL_CTRL_VC_ID_1, CHNL_CTRL_VC_ID_1_MASK: i.MX95 only
+ */
+#define CHNL_CTRL_BLANK_PXL(n) FIELD_PREP(GENMASK(23, 16), (n))
+#define CHNL_CTRL_VC_ID_1(n) FIELD_PREP(BIT(16), (n))
+#define CHNL_CTRL_VC_ID_1_MASK BIT(16)
+#define CHNL_CTRL_MIPI_VC_ID(n) FIELD_PREP(GENMASK(7, 6), (n))
#define CHNL_CTRL_MIPI_VC_ID_MASK GENMASK(7, 6)
#define CHNL_CTRL_SRC_TYPE(n) ((n) << 4)
#define CHNL_CTRL_SRC_TYPE_MASK BIT(4)
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
index fe4adfa3a1f0..f45c2aae59ce 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
@@ -151,7 +151,7 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
.fourcc = V4L2_PIX_FMT_XBGR32,
.type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
| MXC_ISI_VIDEO_M2M_CAP,
- .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,
+ .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8,
.isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888,
.mem_planes = 1,
.color_planes = 1,
@@ -166,6 +166,57 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
.color_planes = 1,
.depth = { 32 },
.encoding = MXC_ISI_ENC_RGB,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .fourcc = V4L2_PIX_FMT_BGRA32,
+ .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGBA8888,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 32 },
+ .encoding = MXC_ISI_ENC_RGB,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .fourcc = V4L2_PIX_FMT_RGBA32,
+ .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_ABGR8888,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 32 },
+ .encoding = MXC_ISI_ENC_RGB,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .fourcc = V4L2_PIX_FMT_BGRX32,
+ .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
+ | MXC_ISI_VIDEO_M2M_CAP,
+ .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGBX8,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGBX888,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 32 },
+ .encoding = MXC_ISI_ENC_RGB,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .fourcc = V4L2_PIX_FMT_RGBX32,
+ .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
+ | MXC_ISI_VIDEO_M2M_CAP,
+ .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_XBGR888,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 32 },
+ .encoding = MXC_ISI_ENC_RGB,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .fourcc = V4L2_PIX_FMT_ARGB2101010,
+ .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
+ | MXC_ISI_VIDEO_M2M_CAP,
+ .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_A2RGB10,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_A2RGB10,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 32 },
+ .encoding = MXC_ISI_ENC_RGB,
},
/*
* RAW formats
@@ -213,6 +264,15 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
.depth = { 16 },
.encoding = MXC_ISI_ENC_RAW,
}, {
+ .mbus_code = MEDIA_BUS_FMT_Y16_1X16,
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .type = MXC_ISI_VIDEO_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 16 },
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
.mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
.fourcc = V4L2_PIX_FMT_SBGGR8,
.type = MXC_ISI_VIDEO_CAP,
@@ -356,6 +416,42 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
.color_planes = 1,
.depth = { 16 },
.encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .type = MXC_ISI_VIDEO_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 16 },
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .type = MXC_ISI_VIDEO_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 16 },
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .type = MXC_ISI_VIDEO_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 16 },
+ .encoding = MXC_ISI_ENC_RAW,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .type = MXC_ISI_VIDEO_CAP,
+ .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
+ .mem_planes = 1,
+ .color_planes = 1,
+ .depth = { 16 },
+ .encoding = MXC_ISI_ENC_RAW,
},
/* JPEG */
{
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index aaf3caa42d33..20ccd7b1f11f 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -83,8 +83,8 @@
(0x270 + 0x200 * (m) + 0x4 * (n))
#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(m, n) \
(0x27c + 0x200 * (m) + 0x4 * (n))
-#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(c) \
- (1 << ((cid % 8) * 4))
+#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(cid) \
+ (1 << (((cid) % 8) * 4))
#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \
(0x2c0 + 0x200 * (m) + 0x4 * (n))
#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
index 246ad0abbac3..eb8de60c1177 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -492,39 +492,41 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst,
buffers->min_count = iris_vpu_buf_count(inst, buffer_type);
}
-void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
+static void iris_get_int_buf_tbl(struct iris_inst *inst, u32 plane,
+ const u32 **internal_buf_type, u32 *internal_buffer_count)
{
const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
- const u32 *internal_buf_type;
- u32 internal_buffer_count, i;
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
- for (i = 0; i < internal_buffer_count; i++)
- iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ *internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ *internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
} else {
- internal_buf_type = firmware_data->dec_op_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
- for (i = 0; i < internal_buffer_count; i++)
- iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ *internal_buf_type = firmware_data->dec_op_int_buf_tbl;
+ *internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
- for (i = 0; i < internal_buffer_count; i++)
- iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ *internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
+ *internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
} else {
- internal_buf_type = firmware_data->enc_op_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
- for (i = 0; i < internal_buffer_count; i++)
- iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ *internal_buf_type = firmware_data->enc_op_int_buf_tbl;
+ *internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
}
}
}
+void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+
+ iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &internal_buffer_count);
+
+ for (i = 0; i < internal_buffer_count; i++)
+ iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+}
+
static int iris_create_internal_buffer(struct iris_inst *inst,
enum iris_buffer_type buffer_type, u32 index)
{
@@ -559,29 +561,12 @@ static int iris_create_internal_buffer(struct iris_inst *inst,
int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
{
- const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
u32 internal_buffer_count, i, j;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
int ret;
- if (inst->domain == DECODER) {
- if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->dec_op_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
- }
- } else {
- if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->enc_op_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
- }
- }
+ iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &internal_buffer_count);
for (i = 0; i < internal_buffer_count; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
@@ -635,30 +620,13 @@ int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffe
int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
{
- const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
struct iris_buffer *buffer, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
int ret;
- if (inst->domain == DECODER) {
- if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->dec_op_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
- }
- } else {
- if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->enc_op_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
- }
- }
+ iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &internal_buffer_count);
for (i = 0; i < internal_buffer_count; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
@@ -680,7 +648,7 @@ int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
return 0;
}
-int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+void iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
{
struct iris_core *core = inst->core;
@@ -688,36 +656,16 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf
dma_free_attrs(core->dev, buffer->buffer_size, buffer->kvaddr,
buffer->device_addr, buffer->dma_attrs);
kfree(buffer);
-
- return 0;
}
static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force)
{
- const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
struct iris_buffer *buf, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
u32 i, len;
- int ret;
- if (inst->domain == DECODER) {
- if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
- len = firmware_data->dec_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->dec_op_int_buf_tbl;
- len = firmware_data->dec_op_int_buf_tbl_size;
- }
- } else {
- if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
- len = firmware_data->enc_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->enc_op_int_buf_tbl;
- len = firmware_data->enc_op_int_buf_tbl_size;
- }
- }
+ iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &len);
for (i = 0; i < len; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
@@ -730,9 +678,7 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool
if (!force && buf->attr & BUF_ATTR_QUEUED)
continue;
- ret = iris_destroy_internal_buffer(inst, buf);
- if (ret)
- return ret;
+ iris_destroy_internal_buffer(inst, buf);
}
}
@@ -742,11 +688,8 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool
else
buffers = &inst->buffers[BUF_ARP];
- list_for_each_entry_safe(buf, next, &buffers->list, list) {
- ret = iris_destroy_internal_buffer(inst, buf);
- if (ret)
- return ret;
- }
+ list_for_each_entry_safe(buf, next, &buffers->list, list)
+ iris_destroy_internal_buffer(inst, buf);
}
return 0;
@@ -788,18 +731,12 @@ static int iris_release_internal_buffers(struct iris_inst *inst,
static int iris_release_input_internal_buffers(struct iris_inst *inst)
{
- const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
int ret;
- if (inst->domain == DECODER) {
- internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
- } else {
- internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
- internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
- }
+ iris_get_int_buf_tbl(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ &internal_buf_type, &internal_buffer_count);
for (i = 0; i < internal_buffer_count; i++) {
ret = iris_release_internal_buffers(inst, internal_buf_type[i]);
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
index 75bb76776182..ab8e5d953101 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_buffer.h
@@ -112,7 +112,7 @@ void iris_get_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_create_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type);
-int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer);
+void iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer);
int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane);
int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst, enum iris_buffer_type buf_type);
diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c
index 52bf56e517f9..8c335dbfce16 100644
--- a/drivers/media/platform/qcom/iris/iris_core.c
+++ b/drivers/media/platform/qcom/iris/iris_core.c
@@ -12,18 +12,24 @@
void iris_core_deinit(struct iris_core *core)
{
- pm_runtime_resume_and_get(core->dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
mutex_lock(&core->lock);
if (core->state != IRIS_CORE_DEINIT) {
iris_fw_unload(core);
- iris_vpu_power_off(core);
+
+ if (!ret)
+ iris_vpu_power_off(core);
+
iris_hfi_queues_deinit(core);
core->state = IRIS_CORE_DEINIT;
}
mutex_unlock(&core->lock);
- pm_runtime_put_sync(core->dev);
+ if (!ret)
+ pm_runtime_put_sync(core->dev);
}
static int iris_wait_for_system_response(struct iris_core *core)
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index 10e33b8a73f6..bf17d310eac0 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -154,6 +154,8 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
return LAYER4_BITRATE_HEVC;
case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR:
return LAYER5_BITRATE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+ return REQUEST_SYNC_FRAME;
default:
return INST_FW_CAP_MAX;
}
@@ -297,6 +299,8 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR;
case LAYER5_BITRATE_HEVC:
return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR;
+ case REQUEST_SYNC_FRAME:
+ return V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
default:
return 0;
}
@@ -1293,7 +1297,7 @@ int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_
int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
- u32 gop_size = inst->fw_caps[GOP_SIZE].value;
+ u32 gop_size = inst->fw_caps[cap_id].value;
u32 b_frame = inst->fw_caps[B_FRAME].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
struct hfi_intra_period intra_period;
@@ -1477,6 +1481,43 @@ int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_typ
&bitrate, sizeof(u32));
}
+int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 hfi_val = 0;
+
+ if (inst->fw_caps[PREPEND_SPSPPS_TO_IDR].value)
+ hfi_val = HFI_SYNC_FRAME_REQUEST_WITH_PREFIX_SEQ_HDR;
+ else
+ hfi_val = HFI_SYNC_FRAME_REQUEST_WITHOUT_SEQ_HDR;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32_ENUM,
+ &hfi_val, sizeof(u32));
+}
+
+int iris_set_time_delta_based_rc(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 value = inst->fw_caps[cap_id].value;
+
+ /*
+ * Disable time-delta-based rate control (value = 0).
+ * This overrides the firmware's default (enabled), ensuring the
+ * firmware uses the configured bitrate target rather than calculating
+ * bitrate from frame timing.
+ */
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &value, sizeof(u32));
+}
+
int iris_set_properties(struct iris_inst *inst, u32 plane)
{
const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h
index 3c462ec9190b..5180d53d3c90 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.h
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.h
@@ -47,6 +47,8 @@ int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type c
int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_time_delta_based_rc(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_properties(struct iris_inst *inst, u32 plane);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index ea9654dd679e..21cdf1c39835 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -17,20 +17,109 @@
#define MAX_FIRMWARE_NAME_SIZE 128
-static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
+/* Detect Gen2 firmware by scanning the blob for:
+ * QC_IMAGE_VERSION_STRING=<version>
+ * and then checking:
+ * - version starts with "vfw", OR
+ * - version matches "video-firmware.N.M" with N >= 2
+ */
+
+static bool iris_detect_gen2_from_fwdata(const u8 *data, size_t size)
+{
+ static const char *marker = "QC_IMAGE_VERSION_STRING=";
+ const size_t mlen = strlen(marker);
+ static const char *vfw = "vfw";
+ const size_t vfwlen = strlen(vfw);
+ static const char *vf = "video-firmware.";
+ const size_t vflen = strlen(vf);
+
+ for (size_t i = 0; i + mlen < size; i++) {
+ const char *found;
+
+ if (memcmp(data + i, marker, mlen))
+ continue;
+
+ found = data + i + mlen;
+ size -= i + mlen;
+
+ /* vfw => Gen2 */
+ if (size > vfwlen && !memcmp(found, vfw, vfwlen))
+ return true;
+
+ if (size < vflen ||
+ memcmp(found, vf, vflen))
+ return false;
+
+ found += vflen;
+ size -= vflen;
+
+ /*
+ * video-firmware.1.x is Gen1.
+ * video-firmware.2.x and video-firmware.10.x are Gen2.
+ */
+ return size >= 2 &&
+ (*found >= '2' || (*found == '1' && found[1] != '.'));
+ }
+
+ return false;
+}
+
+static const struct firmware *iris_detect_firmware(struct iris_core *core,
+ const char **fw_name)
+{
+ const struct firmware *firmware;
+ bool has_both_gens;
+ int ret;
+
+ *fw_name = NULL;
+ if (core->iris_platform_data->firmware_desc_gen2)
+ core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen2;
+ else if (core->iris_platform_data->firmware_desc_gen1)
+ core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen1;
+ else
+ return ERR_PTR(-EINVAL);
+
+ has_both_gens = core->iris_platform_data->firmware_desc_gen2 &&
+ core->iris_platform_data->firmware_desc_gen1;
+
+ ret = of_property_read_string_index(dev_of_node(core->dev), "firmware-name", 0, fw_name);
+ if (ret) {
+ *fw_name = core->iris_firmware_desc->fwname;
+ ret = request_firmware(&firmware, *fw_name, core->dev);
+ if (ret && has_both_gens) {
+ core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen1;
+ *fw_name = core->iris_firmware_desc->fwname;
+ ret = request_firmware(&firmware, *fw_name, core->dev);
+ }
+
+ return ret ? ERR_PTR(ret) : firmware;
+ }
+
+ ret = request_firmware(&firmware, *fw_name, core->dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (has_both_gens &&
+ !iris_detect_gen2_from_fwdata((const u8 *)firmware->data, firmware->size)) {
+ dev_info(core->dev, "Gen1 FW detected in %s\n", *fw_name);
+ core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen1;
+ }
+
+ return firmware;
+}
+
+static int iris_load_fw_to_memory(struct iris_core *core)
{
const struct firmware *firmware = NULL;
struct device *dev = core->dev;
struct resource res;
phys_addr_t mem_phys;
+ const char *fw_name;
size_t res_size;
ssize_t fw_size;
void *mem_virt;
int ret;
- if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
- return -EINVAL;
-
ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
if (ret)
return ret;
@@ -38,9 +127,11 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
mem_phys = res.start;
res_size = resource_size(&res);
- ret = request_firmware(&firmware, fw_name, dev);
- if (ret)
- return ret;
+ firmware = iris_detect_firmware(core, &fw_name);
+ if (IS_ERR(firmware))
+ return PTR_ERR(firmware);
+
+ core->iris_firmware_data = core->iris_firmware_desc->firmware_data;
fw_size = qcom_mdt_get_size(firmware);
if (fw_size < 0 || res_size < (size_t)fw_size) {
@@ -67,18 +158,12 @@ err_release_fw:
int iris_fw_load(struct iris_core *core)
{
const struct tz_cp_config *cp_config;
- const char *fwpath = NULL;
int i, ret;
- ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
- &fwpath);
- if (ret)
- fwpath = core->iris_firmware_desc->fwname;
-
- ret = iris_load_fw_to_memory(core, fwpath);
+ ret = iris_load_fw_to_memory(core);
if (ret) {
- dev_err(core->dev, "firmware download failed\n");
- return -ENOMEM;
+ dev_err(core->dev, "firmware download failed %d\n", ret);
+ return ret;
}
ret = qcom_pas_auth_and_reset(IRIS_PAS_ID);
@@ -100,7 +185,7 @@ int iris_fw_load(struct iris_core *core)
}
}
- return ret;
+ return 0;
}
int iris_fw_unload(struct iris_core *core)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c
index ca1545d28b53..0f7e7d6b25f4 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c
@@ -171,7 +171,9 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
.max = (1 << 16) - 1,
.step_or_mask = 1,
.value = 30,
- .set = iris_set_u32
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_intra_period,
},
{
.cap_id = ENTROPY_MODE,
@@ -240,7 +242,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
.step_or_mask = 1,
.value = 0,
.hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH,
- .flags = CAP_FLAG_OUTPUT_PORT,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_ir_period_gen1,
},
{
@@ -282,16 +284,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
.flags = CAP_FLAG_OUTPUT_PORT,
},
{
- .cap_id = INTRA_PERIOD,
- .min = 0,
- .max = 1,
- .step_or_mask = 1,
- .value = 0,
- .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD,
- .flags = CAP_FLAG_OUTPUT_PORT,
- .set = iris_set_intra_period,
- },
- {
.cap_id = LAYER_ENABLE,
.min = 0,
.max = 1,
@@ -383,6 +375,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_bitrate_gen1,
},
+ {
+ .cap_id = REQUEST_SYNC_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_req_sync_frame,
+ },
};
static const u32 sm8250_vdec_input_config_param_default[] = {
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
index 7674b47ad6c4..f6e9cbfccd09 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -442,7 +442,7 @@ static int iris_hfi_gen1_session_unset_buffers(struct iris_inst *inst, struct ir
ret = iris_wait_for_session_response(inst, false);
if (!ret)
- ret = iris_destroy_internal_buffer(inst, buf);
+ iris_destroy_internal_buffer(inst, buf);
exit:
kfree(pkt);
@@ -741,6 +741,9 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
packet->shdr.hdr.size += sizeof(u32);
break;
}
+ case HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME: {
+ break;
+ }
default:
return -EINVAL;
}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
index 0e4dee192384..275f3fea3d2a 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -157,6 +157,7 @@
#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026
#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001
#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003
+#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME 0x2006004
#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009
#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a
#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
index acc0ed8adda1..3b8fbefb8b93 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
@@ -11,8 +11,832 @@
#include "iris_vpu_buffer.h"
#define VIDEO_ARCH_LX 1
+#define MILOS_BITRATE_MAX 100000000
#define BITRATE_MAX 245000000
+static const struct platform_inst_fw_cap inst_fw_cap_milos_dec[] = {
+ {
+ .cap_id = PROFILE_H264,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH),
+ .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = PROFILE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10),
+ .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = PROFILE_VP9,
+ .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2),
+ .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_H264,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1),
+ .value = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5),
+ .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_VP9,
+ .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_0,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0),
+ .value = V4L2_MPEG_VIDEO_VP9_LEVEL_5_0,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = TIER,
+ .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH),
+ .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .hfi_id = HFI_PROP_TIER,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = INPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROP_STAGE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = PIPE,
+ /* .max, .min and .value are set via platform data */
+ .step_or_mask = 1,
+ .hfi_id = HFI_PROP_PIPE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = POC,
+ .min = 0,
+ .max = 2,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE,
+ },
+ {
+ .cap_id = CODED_FRAMES,
+ .min = CODED_FRAMES_PROGRESSIVE,
+ .max = CODED_FRAMES_PROGRESSIVE,
+ .step_or_mask = 0,
+ .value = CODED_FRAMES_PROGRESSIVE,
+ .hfi_id = HFI_PROP_CODED_FRAMES,
+ },
+ {
+ .cap_id = BIT_DEPTH,
+ .min = BIT_DEPTH_8,
+ .max = BIT_DEPTH_10,
+ .step_or_mask = 1,
+ .value = BIT_DEPTH_8,
+ .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ },
+ {
+ .cap_id = RAP_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+};
+
+static const struct platform_inst_fw_cap inst_fw_cap_milos_enc[] = {
+ {
+ .cap_id = PROFILE_H264,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH),
+ .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_profile,
+ },
+ {
+ .cap_id = PROFILE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10),
+ .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_profile,
+ },
+ {
+ .cap_id = LEVEL_H264,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1),
+ .value = V4L2_MPEG_VIDEO_H264_LEVEL_5_0,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_level,
+ },
+ {
+ .cap_id = LEVEL_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5),
+ .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_level,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROP_STAGE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = HEADER_MODE,
+ .min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
+ BIT(V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME),
+ .value = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ .hfi_id = HFI_PROP_SEQ_HEADER_MODE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_header_mode_gen2,
+ },
+ {
+ .cap_id = PREPEND_SPSPPS_TO_IDR,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ },
+ {
+ .cap_id = BITRATE,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_TOTAL_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen2,
+ },
+ {
+ .cap_id = BITRATE_PEAK,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_TOTAL_PEAK_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_peak_bitrate,
+ },
+ {
+ .cap_id = BITRATE_MODE,
+ .min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ .max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
+ BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_CBR),
+ .value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ .hfi_id = HFI_PROP_RATE_CONTROL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_bitrate_mode_gen2,
+ },
+ {
+ .cap_id = FRAME_SKIP_MODE,
+ .min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ .max = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) |
+ BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT) |
+ BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT),
+ .value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ },
+ {
+ .cap_id = FRAME_RC_ENABLE,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 1,
+ },
+ {
+ .cap_id = GOP_SIZE,
+ .min = 0,
+ .max = INT_MAX,
+ .step_or_mask = 1,
+ .value = 2 * DEFAULT_FPS - 1,
+ .hfi_id = HFI_PROP_MAX_GOP_FRAMES,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = ENTROPY_MODE,
+ .min = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .max = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
+ BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC),
+ .value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+ .hfi_id = HFI_PROP_CABAC_SESSION,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_entropy_mode_gen2,
+ },
+ {
+ .cap_id = MIN_FRAME_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ .hfi_id = HFI_PROP_MIN_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_min_qp,
+ },
+ {
+ .cap_id = MIN_FRAME_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ .hfi_id = HFI_PROP_MIN_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_min_qp,
+ },
+ {
+ .cap_id = MAX_FRAME_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ .hfi_id = HFI_PROP_MAX_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_max_qp,
+ },
+ {
+ .cap_id = MAX_FRAME_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ .hfi_id = HFI_PROP_MAX_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_max_qp,
+ },
+ {
+ .cap_id = I_FRAME_MIN_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ },
+ {
+ .cap_id = I_FRAME_MIN_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ },
+ {
+ .cap_id = P_FRAME_MIN_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ },
+ {
+ .cap_id = P_FRAME_MIN_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ },
+ {
+ .cap_id = B_FRAME_MIN_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ },
+ {
+ .cap_id = B_FRAME_MIN_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MIN_QP_8BIT,
+ },
+ {
+ .cap_id = I_FRAME_MAX_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ },
+ {
+ .cap_id = I_FRAME_MAX_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ },
+ {
+ .cap_id = P_FRAME_MAX_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ },
+ {
+ .cap_id = P_FRAME_MAX_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ },
+ {
+ .cap_id = B_FRAME_MAX_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ },
+ {
+ .cap_id = B_FRAME_MAX_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = MAX_QP,
+ },
+ {
+ .cap_id = I_FRAME_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = DEFAULT_QP,
+ .hfi_id = HFI_PROP_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_frame_qp,
+ },
+ {
+ .cap_id = I_FRAME_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = DEFAULT_QP,
+ .hfi_id = HFI_PROP_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_frame_qp,
+ },
+ {
+ .cap_id = P_FRAME_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = DEFAULT_QP,
+ .hfi_id = HFI_PROP_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_frame_qp,
+ },
+ {
+ .cap_id = P_FRAME_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = DEFAULT_QP,
+ .hfi_id = HFI_PROP_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_frame_qp,
+ },
+ {
+ .cap_id = B_FRAME_QP_H264,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = DEFAULT_QP,
+ .hfi_id = HFI_PROP_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_frame_qp,
+ },
+ {
+ .cap_id = B_FRAME_QP_HEVC,
+ .min = MIN_QP_8BIT,
+ .max = MAX_QP,
+ .step_or_mask = 1,
+ .value = DEFAULT_QP,
+ .hfi_id = HFI_PROP_QP_PACKED,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_frame_qp,
+ },
+ {
+ .cap_id = INPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = OUTPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = ROTATION,
+ .min = 0,
+ .max = 270,
+ .step_or_mask = 90,
+ .value = 0,
+ .hfi_id = HFI_PROP_ROTATION,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_rotation,
+ },
+ {
+ .cap_id = HFLIP,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_FLIP,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_flip,
+ },
+ {
+ .cap_id = VFLIP,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_FLIP,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_flip,
+ },
+ {
+ .cap_id = IR_TYPE,
+ .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM),
+ .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ },
+ {
+ .cap_id = IR_PERIOD,
+ .min = 0,
+ .max = INT_MAX,
+ .step_or_mask = 1,
+ .value = 0,
+ .flags = CAP_FLAG_OUTPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_ir_period_gen2,
+ },
+ {
+ .cap_id = LTR_COUNT,
+ .min = 0,
+ .max = MAX_LTR_FRAME_COUNT_GEN2,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_LTR_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_ltr_count_gen2,
+ },
+ {
+ .cap_id = USE_LTR,
+ .min = 0,
+ .max = ((1 << MAX_LTR_FRAME_COUNT_GEN2) - 1),
+ .step_or_mask = 0,
+ .value = 0,
+ .hfi_id = HFI_PROP_LTR_USE,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_use_and_mark_ltr,
+ },
+ {
+ .cap_id = MARK_LTR,
+ .min = INVALID_DEFAULT_MARK_OR_USE_LTR,
+ .max = (MAX_LTR_FRAME_COUNT_GEN2 - 1),
+ .step_or_mask = 1,
+ .value = INVALID_DEFAULT_MARK_OR_USE_LTR,
+ .hfi_id = HFI_PROP_LTR_MARK,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_use_and_mark_ltr,
+ },
+ {
+ .cap_id = B_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_MAX_B_FRAMES,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = LAYER_ENABLE,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ },
+ {
+ .cap_id = LAYER_TYPE_H264,
+ .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B,
+ .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) |
+ BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P),
+ .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_layer_type,
+ },
+ {
+ .cap_id = LAYER_TYPE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B,
+ .max = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P),
+ .value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P,
+ .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_layer_type,
+ },
+ {
+ .cap_id = LAYER_COUNT_H264,
+ .min = 0,
+ .max = 5,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_LAYER_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_count_gen2,
+ },
+ {
+ .cap_id = LAYER_COUNT_HEVC,
+ .min = 0,
+ .max = 5,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_LAYER_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_count_gen2,
+ },
+ {
+ .cap_id = LAYER0_BITRATE_H264,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER1,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER1_BITRATE_H264,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER2,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER2_BITRATE_H264,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER3,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER3_BITRATE_H264,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER4,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER4_BITRATE_H264,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER5,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER5_BITRATE_H264,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER6,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER0_BITRATE_HEVC,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER1,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER1_BITRATE_HEVC,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER2,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER2_BITRATE_HEVC,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER3,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER3_BITRATE_HEVC,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER4,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER4_BITRATE_HEVC,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER5,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER5_BITRATE_HEVC,
+ .min = 1,
+ .max = MILOS_BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER6,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ }
+};
+
static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
{
.cap_id = PROFILE_H264,
@@ -417,6 +1241,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
.set = iris_set_bitrate_mode_gen2,
},
{
+ .cap_id = TIME_DELTA_BASED_RC,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_TIME_DELTA_BASED_RATE_CONTROL,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_time_delta_based_rc,
+ },
+ {
.cap_id = FRAME_SKIP_MODE,
.min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
.max = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
@@ -937,7 +1771,17 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
.set = iris_set_layer_bitrate,
- }
+ },
+ {
+ .cap_id = REQUEST_SYNC_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_REQUEST_SYNC_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_req_sync_frame,
+ },
};
static const u32 sm8550_vdec_input_config_params_default[] = {
@@ -1118,3 +1962,62 @@ const struct iris_firmware_data iris_hfi_gen2_data = {
.enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
.enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
};
+
+const struct iris_firmware_data iris_hfi_milos_data = {
+ .init_hfi_ops = iris_hfi_gen2_sys_ops_init,
+
+ .core_arch = VIDEO_ARCH_LX,
+
+ .inst_fw_caps_dec = inst_fw_cap_milos_dec,
+ .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_milos_dec),
+ .inst_fw_caps_enc = inst_fw_cap_milos_enc,
+ .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_milos_enc),
+
+ .dec_input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .dec_input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .dec_input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .dec_input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .dec_input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .dec_input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
+ .dec_output_config_params =
+ sm8550_vdec_output_config_params,
+ .dec_output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+
+ .enc_input_config_params =
+ sm8550_venc_input_config_params,
+ .enc_input_config_params_size =
+ ARRAY_SIZE(sm8550_venc_input_config_params),
+ .enc_output_config_params =
+ sm8550_venc_output_config_params,
+ .enc_output_config_params_size =
+ ARRAY_SIZE(sm8550_venc_output_config_params),
+
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc,
+ .dec_output_prop_avc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc),
+ .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc,
+ .dec_output_prop_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc),
+ .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
+ .dec_output_prop_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+
+ .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
+ .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
+ .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
+ .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
+};
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index ca2954f8bd3a..388a36ff2b07 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -603,10 +603,9 @@ static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane)
{
u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
- u32 tier = inst->fw_caps[TIER].value;
+ u32 tier_cap = (inst->codec == V4L2_PIX_FMT_AV1) ? TIER_AV1 : TIER;
+ u32 tier = inst->fw_caps[tier_cap].value;
- tier = (inst->codec == V4L2_PIX_FMT_AV1) ? inst->fw_caps[TIER_AV1].value :
- inst->fw_caps[TIER].value;
inst_hfi_gen2->src_subcr_params.tier = tier;
return iris_hfi_gen2_session_set_property(inst,
@@ -692,7 +691,6 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
{HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate },
{HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain },
{HFI_PROP_AV1_SUPER_BLOCK_ENABLED, iris_hfi_gen2_set_super_block },
- {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable },
};
if (inst->domain == DECODER) {
@@ -766,6 +764,7 @@ static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
break;
case V4L2_PIX_FMT_AV1:
codec = HFI_CODEC_DECODE_AV1;
+ break;
}
iris_hfi_gen2_packet_session_property(inst,
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
index 776b21cd11b2..f43aea10090d 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -67,6 +67,7 @@ enum hfi_rate_control {
};
#define HFI_PROP_RATE_CONTROL 0x0300012a
+#define HFI_PROP_TIME_DELTA_BASED_RATE_CONTROL 0x0300012b
#define HFI_PROP_QP_PACKED 0x0300012e
#define HFI_PROP_MIN_QP_PACKED 0x0300012f
#define HFI_PROP_MAX_QP_PACKED 0x03000130
@@ -90,6 +91,13 @@ enum hfi_layer_encoding_type {
#define HFI_PROP_BITRATE_LAYER4 0x0300013f
#define HFI_PROP_BITRATE_LAYER5 0x03000140
#define HFI_PROP_BITRATE_LAYER6 0x03000141
+
+enum hfi_syncframe_request_mode {
+ HFI_SYNC_FRAME_REQUEST_WITHOUT_SEQ_HDR = 0x00000001,
+ HFI_SYNC_FRAME_REQUEST_WITH_PREFIX_SEQ_HDR = 0x00000002,
+};
+
+#define HFI_PROP_REQUEST_SYNC_FRAME 0x03000145
#define HFI_PROP_MAX_GOP_FRAMES 0x03000146
#define HFI_PROP_MAX_B_FRAMES 0x03000147
#define HFI_PROP_QUALITY_MODE 0x03000148
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
index 25162ae71357..8c2644c7f6e8 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -447,7 +447,9 @@ static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst,
buf->attr &= ~BUF_ATTR_QUEUED;
- return iris_destroy_internal_buffer(inst, buf);
+ iris_destroy_internal_buffer(inst, buf);
+
+ return 0;
}
static int iris_hfi_gen2_handle_session_stop(struct iris_inst *inst,
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index c9256f2323dc..974809509146 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -52,7 +52,9 @@ enum pipe_type {
extern const struct iris_firmware_data iris_hfi_gen1_data;
extern const struct iris_firmware_data iris_hfi_gen2_data;
+extern const struct iris_firmware_data iris_hfi_milos_data;
+extern const struct iris_platform_data milos_data;
extern const struct iris_platform_data qcs8300_data;
extern const struct iris_platform_data sc7280_data;
extern const struct iris_platform_data sm8250_data;
@@ -165,7 +167,6 @@ enum platform_inst_fw_cap_type {
USE_LTR,
MARK_LTR,
B_FRAME,
- INTRA_PERIOD,
LAYER_ENABLE,
LAYER_TYPE_H264,
LAYER_TYPE_HEVC,
@@ -183,6 +184,8 @@ enum platform_inst_fw_cap_type {
LAYER3_BITRATE_HEVC,
LAYER4_BITRATE_HEVC,
LAYER5_BITRATE_HEVC,
+ REQUEST_SYNC_FRAME,
+ TIME_DELTA_BASED_RC,
INST_FW_CAP_MAX,
};
@@ -289,11 +292,7 @@ struct iris_firmware_desc {
};
struct iris_platform_data {
- /*
- * XXX: replace with gen1 / gen2 pointers once we have platforms
- * supporting both firmware kinds.
- */
- const struct iris_firmware_desc *firmware_desc;
+ const struct iris_firmware_desc *firmware_desc_gen1, *firmware_desc_gen2;
const struct vpu_ops *vpu_ops;
const struct icc_info *icc_tbl;
diff --git a/drivers/media/platform/qcom/iris/iris_platform_milos.h b/drivers/media/platform/qcom/iris/iris_platform_milos.h
new file mode 100644
index 000000000000..8a45a4aa9e86
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_milos.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __IRIS_PLATFORM_MILOS_H__
+#define __IRIS_PLATFORM_MILOS_H__
+
+#define MILOS_MAXIMUM_FPS 240
+
+static const struct icc_info iris_icc_info_milos[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 10000000 },
+};
+
+static const char * const milos_opp_pd_table[] = { "cx", "mx" };
+
+static struct platform_inst_caps platform_inst_cap_milos = {
+ .min_frame_width = 96,
+ .max_frame_width = 4096,
+ .min_frame_height = 96,
+ .max_frame_height = 4096,
+ .max_mbpf = (4096 * 2176) / 256,
+ .mb_cycles_vsp = 25,
+ .mb_cycles_vpp = 200,
+ .max_frame_rate = MILOS_MAXIMUM_FPS,
+ .max_operating_rate = MILOS_MAXIMUM_FPS,
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c
index 6e06a32822bb..bbdbf21961d7 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c
@@ -13,15 +13,28 @@
#include "iris_vpu_common.h"
#include "iris_instance.h"
+#include "iris_platform_milos.h"
#include "iris_platform_sc7280.h"
#include "iris_platform_sm8250.h"
+static const struct iris_firmware_desc iris_milos_desc = {
+ .firmware_data = &iris_hfi_milos_data,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .fwname = "qcom/vpu/vpu20_p2_gen2_s7.mbn",
+};
+
static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = {
.firmware_data = &iris_hfi_gen1_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
.fwname = "qcom/vpu/vpu20_p1.mbn",
};
+static const struct iris_firmware_desc iris_vpu20_p1_gen2_s6_desc = {
+ .firmware_data = &iris_hfi_gen2_data,
+ .get_vpu_buffer_size = iris_vpu33_buf_size,
+ .fwname = "qcom/vpu/vpu20_p1_gen2_s6.mbn",
+};
+
static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = {
.firmware_data = &iris_hfi_gen1_data,
.get_vpu_buffer_size = iris_vpu_buf_size,
@@ -64,8 +77,37 @@ static const struct tz_cp_config tz_cp_config_vpu2[] = {
},
};
+const struct iris_platform_data milos_data = {
+ .firmware_desc_gen1 = &iris_milos_desc,
+ .vpu_ops = &iris_vpu2_ops,
+ .icc_tbl = iris_icc_info_milos,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_milos),
+ .bw_tbl_dec = sm8250_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
+ .pmdomain_tbl = iris_pmdomain_table_vpu2,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2),
+ .opp_pd_tbl = milos_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(milos_opp_pd_table),
+ .clk_tbl = sm8250_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
+ .opp_clk_tbl = sm8250_opp_clk_table,
+ .clk_rst_tbl = iris_clk_reset_table_vpu2,
+ .clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2),
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu2_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec),
+ .inst_caps = &platform_inst_cap_milos,
+ .tz_cp_config_data = tz_cp_config_vpu2,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2),
+ .num_vpp_pipe = 2,
+ .max_session_count = 16,
+ .max_core_mbpf = ((4096 * 2176) / 256) * 2,
+ .max_core_mbps = ((3840 * 2176) / 256) * 30 + ((1920 * 1088) / 256) * 30,
+};
+
const struct iris_platform_data sc7280_data = {
- .firmware_desc = &iris_vpu20_p1_gen1_desc,
+ .firmware_desc_gen1 = &iris_vpu20_p1_gen1_desc,
+ .firmware_desc_gen2 = &iris_vpu20_p1_gen2_s6_desc,
.vpu_ops = &iris_vpu2_ops,
.icc_tbl = iris_icc_info_vpu2,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2),
@@ -94,7 +136,7 @@ const struct iris_platform_data sc7280_data = {
};
const struct iris_platform_data sm8250_data = {
- .firmware_desc = &iris_vpu20_p4_gen1_desc,
+ .firmware_desc_gen1 = &iris_vpu20_p4_gen1_desc,
.vpu_ops = &iris_vpu2_ops,
.icc_tbl = iris_icc_info_vpu2,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2),
diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c
index 2c63adbc5579..74626b35d9cb 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c
@@ -90,7 +90,7 @@ static const struct tz_cp_config tz_cp_config_vpu3[] = {
* - inst_caps to platform_inst_cap_qcs8300
*/
const struct iris_platform_data qcs8300_data = {
- .firmware_desc = &iris_vpu30_p4_s6_gen2_desc,
+ .firmware_desc_gen2 = &iris_vpu30_p4_s6_gen2_desc,
.vpu_ops = &iris_vpu3_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
@@ -119,7 +119,7 @@ const struct iris_platform_data qcs8300_data = {
};
const struct iris_platform_data sm8550_data = {
- .firmware_desc = &iris_vpu30_p4_gen2_desc,
+ .firmware_desc_gen2 = &iris_vpu30_p4_gen2_desc,
.vpu_ops = &iris_vpu3_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
@@ -154,7 +154,7 @@ const struct iris_platform_data sm8550_data = {
* - controller_rst_tbl to sm8650_controller_reset_table
*/
const struct iris_platform_data sm8650_data = {
- .firmware_desc = &iris_vpu33_p4_gen2_desc,
+ .firmware_desc_gen2 = &iris_vpu33_p4_gen2_desc,
.vpu_ops = &iris_vpu33_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
@@ -185,7 +185,7 @@ const struct iris_platform_data sm8650_data = {
};
const struct iris_platform_data sm8750_data = {
- .firmware_desc = &iris_vpu35_p4_gen2_desc,
+ .firmware_desc_gen2 = &iris_vpu35_p4_gen2_desc,
.vpu_ops = &iris_vpu35_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
@@ -220,7 +220,7 @@ const struct iris_platform_data sm8750_data = {
* - different num_vpp_pipe
*/
const struct iris_platform_data x1p42100_data = {
- .firmware_desc = &iris_vpu30_p1_gen2_desc,
+ .firmware_desc_gen2 = &iris_vpu30_p1_gen2_desc,
.vpu_ops = &iris_vpu3_ops,
.icc_tbl = iris_icc_info_vpu3x,
.icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index c2dcb50a2782..e4acf4a74f94 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -251,8 +251,6 @@ static int iris_probe(struct platform_device *pdev)
return core->irq;
core->iris_platform_data = of_device_get_match_data(core->dev);
- core->iris_firmware_desc = core->iris_platform_data->firmware_desc;
- core->iris_firmware_data = core->iris_firmware_desc->firmware_data;
core->ubwc_cfg = qcom_ubwc_config_get_data();
if (IS_ERR(core->ubwc_cfg))
@@ -271,8 +269,6 @@ static int iris_probe(struct platform_device *pdev)
if (ret)
return ret;
- iris_session_init_caps(core);
-
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
return ret;
@@ -361,6 +357,10 @@ static const struct dev_pm_ops iris_pm_ops = {
static const struct of_device_id iris_dt_match[] = {
{
+ .compatible = "qcom,milos-iris",
+ .data = &milos_data,
+ },
+ {
.compatible = "qcom,qcs8300-iris",
.data = &qcs8300_data,
},
diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c
index 773f6548370a..2c4d34c7bd77 100644
--- a/drivers/media/platform/qcom/iris/iris_resources.c
+++ b/drivers/media/platform/qcom/iris/iris_resources.c
@@ -61,9 +61,9 @@ int iris_unset_icc_bw(struct iris_core *core)
int iris_opp_set_rate(struct device *dev, unsigned long freq)
{
- struct dev_pm_opp *opp __free(put_opp);
+ struct dev_pm_opp *opp __free(put_opp) =
+ devfreq_recommended_opp(dev, &freq, 0);
- opp = devfreq_recommended_opp(dev, &freq, 0);
if (IS_ERR(opp))
return PTR_ERR(opp);
@@ -78,24 +78,21 @@ int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev)
if (ret)
return ret;
- ret = pm_runtime_get_sync(pd_dev);
- if (ret < 0)
- return ret;
-
- return ret;
+ return pm_runtime_resume_and_get(pd_dev);
}
int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev)
{
int ret;
+ int pm_ret;
ret = iris_opp_set_rate(core->dev, 0);
- if (ret)
- return ret;
- pm_runtime_put_sync(pd_dev);
+ pm_ret = pm_runtime_put_sync(pd_dev);
+ if (!ret)
+ ret = pm_ret;
- return 0;
+ return ret;
}
static struct clk *iris_get_clk_by_type(struct iris_core *core, enum platform_clk_type clk_type)
diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c
index d14472414750..5552725c614e 100644
--- a/drivers/media/platform/qcom/iris/iris_state.c
+++ b/drivers/media/platform/qcom/iris/iris_state.c
@@ -60,9 +60,9 @@ int iris_inst_change_state(struct iris_inst *inst,
return -EINVAL;
change_state:
- inst->state = request_state;
dev_dbg(inst->core->dev, "state changed from %x to %x\n",
inst->state, request_state);
+ inst->state = request_state;
return 0;
}
@@ -269,7 +269,7 @@ bool iris_allow_cmd(struct iris_inst *inst, u32 cmd)
return true;
} else if (cmd == V4L2_DEC_CMD_STOP || cmd == V4L2_ENC_CMD_STOP) {
if (vb2_is_streaming(src_q))
- if (inst->sub_state != IRIS_INST_SUB_DRAIN)
+ if (!(inst->sub_state & IRIS_INST_SUB_DRAIN))
return true;
}
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index 14d63dc76c9b..fcbc60016bee 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -9,6 +9,7 @@
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
+#include "iris_ctrls.h"
#include "iris_vidc.h"
#include "iris_instance.h"
#include "iris_vdec.h"
@@ -196,6 +197,8 @@ int iris_open(struct file *filp)
goto fail_m2m_release;
}
+ iris_session_init_caps(core);
+
if (inst->domain == DECODER)
ret = iris_vdec_inst_init(inst);
else if (inst->domain == ENCODER)
@@ -449,14 +452,21 @@ static int iris_enum_frameintervals(struct file *filp, void *fh,
static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap)
{
+ struct iris_core *core = video_drvdata(filp);
struct iris_inst *inst = iris_get_inst(filp);
+ char *info;
strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver));
- if (inst->domain == DECODER)
+ if (inst->domain == DECODER) {
strscpy(cap->card, "Iris Decoder", sizeof(cap->card));
- else
+ info = "dec";
+ } else {
strscpy(cap->card, "Iris Encoder", sizeof(cap->card));
+ info = "enc";
+ }
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "plat:%s:%s", dev_name(core->dev), info);
return 0;
}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
index fb6f1016415e..faebb5472866 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -580,7 +580,7 @@ static u32 hfi_buffer_line_av1d(u32 frame_width, u32 frame_height,
ALIGN(size_av1d_qp(frame_width, frame_height), DMA_ALIGNMENT);
opbwr8 = size_av1d_lb_opb_wr1_nv12_ubwc(frame_width, frame_height);
opbwr10 = size_av1d_lb_opb_wr1_tp10_ubwc(frame_width, frame_height);
- opbwrbufsize = opbwr8 >= opbwr10 ? opbwr8 : opbwr10;
+ opbwrbufsize = max(opbwr8, opbwr10);
size = ALIGN((size + opbwrbufsize), DMA_ALIGNMENT);
if (is_opb) {
vpss_lb_size = size_vpss_lb(frame_width, frame_height);
@@ -700,7 +700,7 @@ static u32 hfi_buffer_ibc_av1d(u32 frame_width, u32 frame_height)
ibc8 = size_av1d_ibc_nv12_ubwc(frame_width, frame_height);
ibc10 = size_av1d_ibc_tp10_ubwc(frame_width, frame_height);
- size = ibc8 >= ibc10 ? ibc8 : ibc10;
+ size = max(ibc8, ibc10);
return ALIGN(size, DMA_ALIGNMENT);
}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
index ab41da1f47c8..e4847c107709 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -237,7 +237,7 @@ void iris_vpu_power_off(struct iris_core *core)
iris_unset_icc_bw(core);
if (!iris_vpu_watchdog(core, core->intr_status))
- disable_irq_nosync(core->irq);
+ disable_irq(core->irq);
}
int iris_vpu_power_on_controller(struct iris_core *core)
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index f44059f19505..e96d458c52fa 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -616,7 +616,7 @@ struct hfi_capability {
struct hfi_capabilities {
u32 num_capabilities;
- struct hfi_capability data[];
+ struct hfi_capability data[] __counted_by(num_capabilities);
};
#define HFI_DEBUG_MSG_LOW 0x01
@@ -802,7 +802,7 @@ struct hfi_profile_level {
struct hfi_profile_level_supported {
u32 profile_count;
- struct hfi_profile_level profile_level[];
+ struct hfi_profile_level profile_level[] __counted_by(profile_count);
};
struct hfi_quality_vs_speed {
@@ -1085,7 +1085,7 @@ struct hfi_resource_ocmem_requirement {
struct hfi_resource_ocmem_requirement_info {
u32 num_entries;
- struct hfi_resource_ocmem_requirement requirements[];
+ struct hfi_resource_ocmem_requirement requirements[] __counted_by(num_entries);
};
struct hfi_property_sys_image_version_info_type {
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index 47b99d5b5af7..5c2025c5acc7 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -690,7 +690,7 @@ struct hfi_done_handler {
u32 pkt;
u32 pkt_sz;
u32 pkt_sz2;
- void (*done)(struct venus_core *, struct venus_inst *, void *);
+ void (*done)(struct venus_core *core, struct venus_inst *inst, void *packet);
bool is_sys_pkt;
};
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index b1657443f23f..d07845f37c55 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -85,7 +85,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
type++;
}
- return sizeof(*mode);
+ return mode->num_entries * sizeof(u32) + sizeof(*mode);
}
static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
@@ -146,7 +146,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_caps, caps_arr, num_caps);
- return sizeof(*caps);
+ return num_caps * sizeof(*cap) + sizeof(u32);
}
static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
@@ -171,7 +171,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
u32 entries = fmt->format_entries;
unsigned int i = 0;
u32 num_planes = 0;
- u32 size;
+ u32 size = 2 * sizeof(u32);
while (entries) {
num_planes = pinfo->num_planes;
@@ -186,6 +186,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
if (pinfo->num_planes > MAX_PLANES)
break;
+ size += sizeof(*constr) * num_planes + 2 * sizeof(u32);
pinfo = (void *)pinfo + sizeof(*constr) * num_planes +
2 * sizeof(u32);
entries--;
@@ -193,8 +194,6 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_raw_fmts, rawfmts, i);
- size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32))
- + 2 * sizeof(u32);
return size;
}
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index be1cbd5cfe84..e88e66be4f6d 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -781,7 +781,6 @@ static int decide_core(struct venus_inst *inst)
unsigned long max_freq = ULONG_MAX;
struct device *dev = core->dev;
struct dev_pm_opp *opp;
- int ret = 0;
if (legacy_binding) {
if (inst->session_type == VIDC_SESSION_TYPE_DEC)
@@ -829,11 +828,7 @@ static int decide_core(struct venus_inst *inst)
}
done:
- ret = hfi_session_set_property(inst, ptype, &cu);
- if (ret)
- return ret;
-
- return ret;
+ return hfi_session_set_property(inst, ptype, &cu);
}
static int acquire_core(struct venus_inst *inst)
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 7305cc4a04cb..6635f5782175 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -2273,11 +2273,7 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv,
return ret;
}
- ret = rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4));
- if (ret)
- return ret;
-
- return ret;
+ return rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4));
}
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
index 242f6a23851f..bacc15c250fe 100644
--- a/drivers/media/platform/renesas/rcar-isp/Kconfig
+++ b/drivers/media/platform/renesas/rcar-isp/Kconfig
@@ -5,10 +5,12 @@ config VIDEO_RCAR_ISP
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV && OF
depends on ARCH_RENESAS || COMPILE_TEST
+ depends on VIDEO_RENESAS_VSP1
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select RESET_CONTROLLER
select V4L2_FWNODE
+ select VIDEO_DCT_RPPX1
help
Support for Renesas R-Car Image Signal Processor (ISP).
Enable this to support the Renesas R-Car Image Signal
diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile
index b542118c831e..c0c80303682c 100644
--- a/drivers/media/platform/renesas/rcar-isp/Makefile
+++ b/drivers/media/platform/renesas/rcar-isp/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-rcar-isp-objs = csisp.o
+rcar-isp-objs = csisp.o core.o core-io.o
obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
diff --git a/drivers/media/platform/renesas/rcar-isp/core-io.c b/drivers/media/platform/renesas/rcar-isp/core-io.c
new file mode 100644
index 000000000000..820af506f896
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/core-io.c
@@ -0,0 +1,997 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
+#include <media/v4l2-mc.h>
+
+#include <linux/media/dreamchip/rppx1-config.h>
+
+#include "risp-core.h"
+
+#define risp_io_err(d, fmt, arg...) dev_err((d)->core->dev, fmt, ##arg)
+
+static struct risp_buffer *risp_io_vb2buf(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct risp_buffer, vb);
+}
+
+static int risp_io_open(struct file *file)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ int ret;
+
+ ret = mutex_lock_interruptible(&io->lock);
+ if (ret)
+ return ret;
+
+ file->private_data = io;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ goto err_unlock;
+
+ ret = v4l2_pipeline_pm_get(&io->vdev.entity);
+ if (ret < 0)
+ goto err_open;
+
+ mutex_unlock(&io->lock);
+
+ return 0;
+err_open:
+ v4l2_fh_release(file);
+err_unlock:
+ mutex_unlock(&io->lock);
+
+ return ret;
+}
+
+static int risp_io_release(struct file *file)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&io->lock);
+
+ ret = _vb2_fop_release(file, NULL);
+
+ v4l2_pipeline_pm_put(&io->vdev.entity);
+
+ mutex_unlock(&io->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations risp_io_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = risp_io_open,
+ .release = risp_io_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .read = vb2_fop_read,
+};
+
+/* -----------------------------------------------------------------------------
+ * Common queue
+ */
+
+static int risp_io_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+ const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
+
+ if (*nplanes) {
+ if (*nplanes > pix->num_planes)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < pix->num_planes; i++)
+ if (sizes[i] < pix->plane_fmt[i].sizeimage)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *nplanes = pix->num_planes;
+ for (unsigned int i = 0; i < pix->num_planes; i++)
+ sizes[i] = pix->plane_fmt[i].sizeimage;
+ } else {
+ if (*nplanes) {
+ if (sizes[0] < io->format.fmt.meta.buffersize)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *nplanes = 1;
+ sizes[0] = io->format.fmt.meta.buffersize;
+ }
+
+ /* Initialize buffer queue */
+ INIT_LIST_HEAD(&io->buffers);
+
+ return 0;
+};
+
+static int risp_io_buffer_prepare_set(struct rcar_isp_core_io *io,
+ struct vb2_buffer *vb, unsigned int plane,
+ unsigned long size)
+{
+ if (vb2_plane_size(vb, plane) < size) {
+ risp_io_err(io, "Buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, plane), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, plane, size);
+
+ return 0;
+}
+
+static int risp_io_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type)) {
+ const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp;
+ int ret = 0;
+
+ for (unsigned int i = 0; i < pix->num_planes; i++) {
+ ret = risp_io_buffer_prepare_set(io, vb, i,
+ pix->plane_fmt[i].sizeimage);
+ if (ret)
+ break;
+ }
+
+ return ret;
+ }
+
+ return risp_io_buffer_prepare_set(io, vb, 0,
+ io->format.fmt.meta.buffersize);
+}
+
+static void risp_io_buffer_queue(struct vb2_buffer *vb)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+
+ guard(mutex)(&io->core->io_lock);
+
+ list_add_tail(&buf->list, &io->buffers);
+
+ if (risp_core_job_prepare(io->core))
+ risp_io_err(io, "Failed to prepare job\n");
+}
+
+static void risp_io_return_buffers(struct rcar_isp_core_io *io,
+ enum vb2_buffer_state state)
+{
+ struct risp_buffer *buf, *node;
+
+ lockdep_assert_held(&io->core->io_lock);
+
+ list_for_each_entry_safe(buf, node, &io->buffers, list) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->list);
+ }
+}
+
+static int risp_io_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+ int ret;
+
+ scoped_guard(mutex, &io->core->io_lock) {
+ if (io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width !=
+ io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width ||
+ io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height !=
+ io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height) {
+ risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
+ return -EPIPE;
+ }
+
+ io->streaming = true;
+ }
+
+ ret = risp_core_start_streaming(io->core);
+ if (ret) {
+ guard(mutex)(&io->core->io_lock);
+
+ risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void risp_io_stop_streaming(struct vb2_queue *vq)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+
+ scoped_guard(mutex, &io->core->io_lock) {
+ io->streaming = false;
+ risp_core_stop_streaming(io->core);
+ risp_io_return_buffers(io, VB2_BUF_STATE_ERROR);
+ }
+
+ /*
+ * Wait for buffers part of the jobs not yet processed. Note that this
+ * might complete buffers out of order.
+ */
+ vb2_wait_for_all_buffers(&io->queue);
+}
+
+/* -----------------------------------------------------------------------------
+ * Common V4L2 IOCTLs
+ */
+
+static int risp_io_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Input Exposure
+ */
+
+static int risp_io_input_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vq);
+ struct rcar_isp_core *core = io->core;
+ struct device *bus_master;
+ int ret;
+
+ ret = risp_io_queue_setup(vq, nbuffers, nplanes, sizes, alloc_devs);
+ if (ret)
+ return ret;
+
+ bus_master = vsp1_isp_get_bus_master(core->vspx.dev);
+ if (IS_ERR_OR_NULL(bus_master)) {
+ risp_io_err(io, "Missing reference to bus-master device\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate buffers using the bus_master device associated with the
+ * VSPX associated to this ISP instance.
+ */
+ alloc_devs[0] = bus_master;
+
+ return 0;
+};
+
+static const struct vb2_ops risp_io_input_qops = {
+ .queue_setup = risp_io_input_queue_setup,
+ .buf_prepare = risp_io_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_pix_format_mplane risp_io_input_default_format = {
+ .width = 1920,
+ .height = 1080,
+ .field = V4L2_FIELD_NONE,
+ .pixelformat = V4L2_PIX_FMT_SGRBG8,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .num_planes = 1,
+ .plane_fmt = {
+ [0] = {
+ .sizeimage = 1920 * 1080,
+ .bytesperline = 1920,
+ },
+ },
+};
+
+static const struct risp_io_input_format {
+ unsigned int fourcc;
+ unsigned int bpp;
+} risp_io_input_formats[] = {
+ { .fourcc = V4L2_PIX_FMT_SBGGR8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SGBRG8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SGRBG8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SRGGB8, .bpp = 1 },
+ { .fourcc = V4L2_PIX_FMT_SBGGR10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGBRG10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGRBG10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SRGGB10, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SBGGR12, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGBRG12, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SGRBG12, .bpp = 2 },
+ { .fourcc = V4L2_PIX_FMT_SRGGB12, .bpp = 2 },
+};
+
+static void risp_io_input_try_format(struct rcar_isp_core_io *io,
+ struct v4l2_pix_format_mplane *pix)
+{
+ unsigned int bpp = 0;
+
+ v4l_bound_align_image(&pix->width, 128, 5120, 2,
+ &pix->height, 128, 4096, 2, 0);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
+ if (risp_io_input_formats[i].fourcc == pix->pixelformat) {
+ bpp = risp_io_input_formats[i].bpp;
+ break;
+ }
+ }
+
+ if (!bpp) {
+ pix->pixelformat = risp_io_input_formats[0].fourcc;
+ bpp = risp_io_input_formats[0].bpp;
+ }
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_RAW;
+
+ pix->num_planes = 1;
+ pix->plane_fmt[0].bytesperline = pix->width * bpp;
+ pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
+}
+
+static int risp_io_input_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(risp_io_input_formats))
+ return -EINVAL;
+
+ f->pixelformat = risp_io_input_formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int risp_io_input_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ f->fmt.pix_mp = io->format.fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_input_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ risp_io_input_try_format(io, &f->fmt.pix_mp);
+
+ io->format.fmt.pix_mp = f->fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_input_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ risp_io_input_try_format(io, &f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int risp_io_input_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ bool found = false;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) {
+ if (risp_io_input_formats[i].fourcc == fsize->pixel_format) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fsize->stepwise.min_width = 128;
+ fsize->stepwise.max_width = 5120;
+ fsize->stepwise.step_width = 2;
+
+ fsize->stepwise.min_height = 128;
+ fsize->stepwise.max_height = 4096;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops risp_io_input_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_vid_out = risp_io_input_enum_fmt,
+ .vidioc_g_fmt_vid_out_mplane = risp_io_input_g_fmt,
+ .vidioc_s_fmt_vid_out_mplane = risp_io_input_s_fmt,
+ .vidioc_try_fmt_vid_out_mplane = risp_io_input_try_fmt,
+ .vidioc_enum_framesizes = risp_io_input_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Parameters
+ *
+ */
+
+static int risp_io_params_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct rcar_isp_core *core = io->core;
+ size_t size;
+ int ret;
+
+ memset(&buf->vsp_buffer, 0, sizeof(buf->vsp_buffer));
+
+ size = RISP_IO_PARAMS_BUF_SIZE;
+ ret = vsp1_isp_alloc_buffer(core->vspx.dev, size, &buf->vsp_buffer);
+ if (ret)
+ return -EINVAL;
+
+ memset(buf->vsp_buffer.cpu_addr, 0, RISP_IO_PARAMS_BUF_SIZE);
+
+ return 0;
+}
+
+static void risp_io_params_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct rcar_isp_core *core = io->core;
+
+ vsp1_isp_free_buffer(core->vspx.dev, &buf->vsp_buffer);
+}
+
+struct risp_conf_dma_write_desc {
+ u32 *buf;
+ u32 base;
+ unsigned int count;
+};
+
+static int risp_conf_dma_prepare(void *priv, u32 offset, u32 value)
+{
+ struct risp_conf_dma_write_desc *desc = priv;
+
+ /* Bounds check, 8 bytes = address (4)+ value (4). */
+ if ((desc->count + 1) * 8 > RISP_IO_PARAMS_BUF_SIZE)
+ return -ENOMEM;
+
+ (*desc->buf++) = desc->base | offset;
+ (*desc->buf++) = value;
+
+ desc->count++;
+
+ return 0;
+}
+
+static int risp_io_params_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct risp_buffer *buf = risp_io_vb2buf(vbuf);
+ struct risp_conf_dma_write_desc desc;
+ u32 *cpu_addr;
+ int ret;
+
+ /* Prepare params. */
+ cpu_addr = (u32 *)buf->vsp_buffer.cpu_addr;
+
+ desc.buf = cpu_addr + 2;
+ desc.base = io->core->rppaddr;
+ desc.count = 0;
+
+ /* Fill params body. */
+ ret = rppx1_params(io->core->rpp, vb, io->format.fmt.meta.buffersize,
+ risp_conf_dma_prepare, &desc);
+ if (ret)
+ return ret;
+
+ /* Fill params header. */
+ cpu_addr[0] = desc.count;
+ cpu_addr[1] = 0x0;
+
+ return 0;
+}
+
+static const struct vb2_ops risp_io_params_qops = {
+ .queue_setup = risp_io_queue_setup,
+ .buf_init = risp_io_params_buf_init,
+ .buf_cleanup = risp_io_params_buf_cleanup,
+ .buf_prepare = risp_io_params_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_meta_format risp_io_params_default_format = {
+ .dataformat = V4L2_META_FMT_RPPX1_PARAMS,
+ .buffersize = v4l2_isp_buffer_size(RPPX1_PARAMS_MAX_SIZE),
+};
+
+static int risp_io_params_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_META_OUTPUT || f->index)
+ return -EINVAL;
+
+ f->pixelformat = io->format.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int risp_io_params_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != V4L2_BUF_TYPE_META_OUTPUT)
+ return -EINVAL;
+
+ *meta = io->format.fmt.meta;
+
+ return 0;
+}
+
+static int risp_io_params_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ return risp_io_params_g_fmt(file, priv, f);
+}
+
+static const struct v4l2_ioctl_ops risp_io_params_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_meta_out = risp_io_params_enum_fmt,
+ .vidioc_g_fmt_meta_out = risp_io_params_g_fmt,
+ .vidioc_s_fmt_meta_out = risp_io_params_s_fmt,
+ .vidioc_try_fmt_meta_out = risp_io_params_g_fmt,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Statistics
+ */
+
+static const struct vb2_ops risp_io_stats_qops = {
+ .queue_setup = risp_io_queue_setup,
+ .buf_prepare = risp_io_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_meta_format risp_io_stats_default_format = {
+ .dataformat = V4L2_META_FMT_RPPX1_STATS,
+ .buffersize = v4l2_isp_buffer_size(RPPX1_STATS_MAX_SIZE),
+};
+
+static int risp_io_stats_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_META_CAPTURE || f->index)
+ return -EINVAL;
+
+ f->pixelformat = io->format.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int risp_io_stats_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != V4L2_BUF_TYPE_META_CAPTURE)
+ return -EINVAL;
+
+ *meta = io->format.fmt.meta;
+
+ return 0;
+}
+
+static int risp_io_stats_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ return risp_io_stats_g_fmt(file, priv, f);
+}
+
+static const struct v4l2_ioctl_ops risp_io_stats_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_meta_cap = risp_io_stats_enum_fmt,
+ .vidioc_g_fmt_meta_cap = risp_io_stats_g_fmt,
+ .vidioc_s_fmt_meta_cap = risp_io_stats_s_fmt,
+ .vidioc_try_fmt_meta_cap = risp_io_stats_g_fmt,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video capture
+ */
+
+static const struct vb2_ops risp_io_capture_qops = {
+ .queue_setup = risp_io_queue_setup,
+ .buf_prepare = risp_io_buffer_prepare,
+ .buf_queue = risp_io_buffer_queue,
+ .start_streaming = risp_io_start_streaming,
+ .stop_streaming = risp_io_stop_streaming,
+};
+
+static const struct v4l2_pix_format_mplane risp_io_capture_default_format = {
+ .width = 1920,
+ .height = 1080,
+ .pixelformat = V4L2_PIX_FMT_XBGR32,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ .num_planes = 1,
+ .plane_fmt = {
+ [0] = {
+ .bytesperline = ALIGN(1920 * 4, 256),
+ .sizeimage = ALIGN(1920 * 4, 256) * 1080,
+ },
+ },
+};
+
+static void risp_io_capture_try_format(struct rcar_isp_core_io *io,
+ struct v4l2_pix_format_mplane *pix)
+{
+ v4l_bound_align_image(&pix->width, 128, 5120, 2,
+ &pix->height, 128, 4096, 2, 0);
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ pix->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ pix->xfer_func = V4L2_XFER_FUNC_SRGB;
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV16M:
+ pix->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ pix->num_planes = 2;
+ pix->plane_fmt[0].bytesperline = ALIGN(pix->width, 256);
+ pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
+ pix->plane_fmt[1].bytesperline = ALIGN(pix->width, 256);
+ pix->plane_fmt[1].sizeimage = pix->plane_fmt[1].bytesperline * pix->height;
+ break;
+ default:
+ pix->pixelformat = V4L2_PIX_FMT_XBGR32;
+ pix->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ pix->num_planes = 1;
+ pix->plane_fmt[0].bytesperline = ALIGN(pix->width * 4, 256);
+ pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height;
+ break;
+ }
+}
+
+static int risp_io_capture_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (f->index) {
+ case 0:
+ f->pixelformat = V4L2_PIX_FMT_NV16M;
+ break;
+ case 1:
+ f->pixelformat = V4L2_PIX_FMT_XBGR32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int risp_io_capture_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ f->fmt.pix_mp = io->format.fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_capture_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ if (vb2_is_busy(&io->queue))
+ return -EBUSY;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ risp_io_capture_try_format(io, &f->fmt.pix_mp);
+
+ io->format.fmt.pix_mp = f->fmt.pix_mp;
+
+ return 0;
+}
+
+static int risp_io_capture_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rcar_isp_core_io *io = video_drvdata(file);
+
+ risp_io_capture_try_format(io, &f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int risp_io_capture_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ switch (fsize->pixel_format) {
+ case V4L2_PIX_FMT_NV16M:
+ case V4L2_PIX_FMT_XBGR32:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+
+ fsize->stepwise.min_width = 128;
+ fsize->stepwise.max_width = 5120;
+ fsize->stepwise.step_width = 2;
+
+ fsize->stepwise.min_height = 128;
+ fsize->stepwise.max_height = 4096;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops risp_io_capture_ioctl_ops = {
+ .vidioc_querycap = risp_io_querycap,
+
+ .vidioc_enum_fmt_vid_cap = risp_io_capture_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = risp_io_capture_g_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = risp_io_capture_s_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = risp_io_capture_try_fmt,
+ .vidioc_enum_framesizes = risp_io_capture_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * Create and remove IO video devices
+ */
+
+int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
+ struct rcar_isp_core_io *io, unsigned int pad)
+{
+ struct video_device *vdev = &io->vdev;
+ struct vb2_queue *q = &io->queue;
+ int ret;
+
+ switch (pad) {
+ case RISP_CORE_INPUT1:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s input1",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_TX;
+ vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ vdev->ioctl_ops = &risp_io_input_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->ops = &risp_io_input_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SOURCE;
+ io->format.fmt.pix_mp = risp_io_input_default_format;
+ break;
+
+ case RISP_CORE_PARAMS:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s params",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_TX;
+ vdev->device_caps = V4L2_CAP_META_OUTPUT;
+ vdev->ioctl_ops = &risp_io_params_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->ops = &risp_io_params_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SOURCE;
+ io->format.fmt.meta = risp_io_params_default_format;
+ break;
+
+ case RISP_CORE_STATS:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s stats",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE;
+ vdev->ioctl_ops = &risp_io_stats_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->ops = &risp_io_stats_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SINK;
+ io->format.fmt.meta = risp_io_stats_default_format;
+ break;
+
+ case RISP_CORE_OUTPUT1:
+ snprintf(vdev->name, sizeof(vdev->name), "%s %s output1",
+ KBUILD_MODNAME, dev_name(dev));
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+ vdev->ioctl_ops = &risp_io_capture_ioctl_ops;
+
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->ops = &risp_io_capture_qops;
+
+ io->pad.flags = MEDIA_PAD_FL_SINK;
+ io->format.fmt.pix_mp = risp_io_capture_default_format;
+ break;
+ }
+
+ io->core = core;
+
+ mutex_init(&io->lock);
+ INIT_LIST_HEAD(&io->buffers);
+
+ /* Create media graph pad. */
+ ret = media_entity_pads_init(&io->vdev.entity, 1, &io->pad);
+ if (ret)
+ return ret;
+
+ /* Create queue */
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->lock = &io->lock;
+ q->drv_priv = io;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct risp_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->dev = dev;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ risp_io_err(io, "Failed to initialize VB2 queue\n");
+ return ret;
+ }
+
+ /* Create video device */
+ vdev->v4l2_dev = &core->v4l2_dev;
+ vdev->queue = &io->queue;
+
+ vdev->release = video_device_release_empty;
+ vdev->lock = &io->lock;
+ vdev->fops = &risp_io_fops;
+
+ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ risp_io_err(io, "Failed to register video device\n");
+ return ret;
+ }
+
+ video_set_drvdata(&io->vdev, io);
+
+ v4l2_info(&core->v4l2_dev, "Device registered as %s\n",
+ video_device_node_name(vdev));
+
+ switch (pad) {
+ case RISP_CORE_INPUT1:
+ case RISP_CORE_PARAMS:
+ ret = media_create_pad_link(&io->vdev.entity, 0,
+ &core->subdev.entity, pad,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ break;
+ case RISP_CORE_STATS:
+ case RISP_CORE_OUTPUT1:
+ ret = media_create_pad_link(&core->subdev.entity, pad,
+ &io->vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ break;
+ }
+
+ return ret;
+}
+
+void risp_core_io_destroy(struct rcar_isp_core_io *io)
+{
+ if (!video_is_registered(&io->vdev))
+ return;
+
+ vb2_video_unregister_device(&io->vdev);
+}
diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c
new file mode 100644
index 000000000000..b5861d0cd0e8
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/core.c
@@ -0,0 +1,916 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/vsp1.h>
+
+#include "risp-core.h"
+
+#define ISP_CS_STREAMER_MODE_REG 0x7000
+#define ISP_CS_STREAMER_MODE_STREAMER_EN 0xf
+
+#define ISP_CS_STREAMER_VBLANK_REG 0x7004
+#define ISP_CS_STREAMER_HBLANK_REG 0x7008
+
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG 0x7100
+#define ISP_CS_STREAMER_CONFIG_DMA_REG_ADDRESS_UPPER_8BIT_MASK GENMASK(31, 24)
+#define ISP_CS_STREAMER_CONFIG_DMA_ENABLE0 BIT(0)
+
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG 0x2100
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 BIT(31)
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_CONFIG_DATA_START_REG_ADDRESS_MASK GENMASK(15, 0)
+
+#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG 0x2104
+
+#define ISP_CORE_ISPCORE_INT_STATUS 0x80000
+#define ISP_CORE_ISPCORE_INT_ENABLE 0x80004
+#define ISPCORE_DMA_IMAGE_FRAME_MODE(i, f) (0x84000 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(i, f) (0x84004 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(i, f) (0x84008 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(i, f) (0x8400c + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(i, f) (0x84010 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(i, f) (0x84014 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP2(i, f) (0x84018 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP3(i, f) (0x8401c + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(i, f) (0x84020 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(i, f) (0x84024 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP2(i, f) (0x84028 + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP3(i, f) (0x8402c + 0x1000 * (i) + 0x100 * (f))
+#define ISPCORE_DMA_IMAGE_FRAME_AXI_ID(i, f) (0x84030 + 0x1000 * (i) + 0x100 * (f))
+
+#define ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(i) (0x84400 + 0x1000 * (i))
+#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_MASK GENMASK(31, 16)
+#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT 16
+
+#define ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(i) (0x84800 + 0x1000 * (i))
+
+static void risp_cs_write(struct rcar_isp_core *core, u32 offset, u32 value)
+{
+ iowrite32(value, core->csbase + offset);
+}
+
+static u32 risp_cs_read(struct rcar_isp_core *core, u32 offset)
+{
+ return ioread32(core->csbase + offset);
+}
+
+static void risp_core_write(struct rcar_isp_core *core, u32 offset, u32 value)
+{
+ iowrite32(value, core->base + offset);
+}
+
+static u32 risp_core_read(struct rcar_isp_core *core, u32 offset)
+{
+ return ioread32(core->base + offset);
+}
+
+static void risp_core_job_run_params(struct rcar_isp_core *core,
+ struct vsp1_isp_job_desc *vspx_job,
+ struct risp_buffer *buf)
+{
+ u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
+ bool have_config = !!params_buf[0];
+ u32 ctrl0, ctrl1, ctrl2;
+
+ /*
+ * If we have a configuration but not asked the VSPX to program it,
+ * use MMIO to write the configuration. This might be needed to work
+ * around limitations of the VSPX ConfigDMA, see comment in
+ * risp_core_job_prepare().
+ */
+ if (have_config && !vspx_job->config.pairs) {
+ for (unsigned int i = 0; i < params_buf[0]; i++)
+ risp_core_write(core, params_buf[2 + i * 2] & 0xffff,
+ params_buf[3 + i * 2]);
+
+ /* Disable ConfigDMA. */
+ have_config = false;
+ }
+
+ ctrl0 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG) &
+ ~ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
+ ctrl1 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG) &
+ ~(ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 | 0xffff);
+ ctrl2 = 0;
+
+ if (have_config) {
+ ctrl0 |= ISP_CS_STREAMER_CONFIG_DMA_ENABLE0;
+ ctrl1 |= ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 |
+ (params_buf[2] & 0xffff);
+ ctrl2 = params_buf[3];
+ }
+
+ risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG, ctrl0);
+ risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG, ctrl1);
+ risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG, ctrl2);
+}
+
+static void risp_core_job_run_output(struct rcar_isp_core *core,
+ struct risp_buffer *buf)
+{
+ const struct v4l2_format *fmt = &core->io[RISP_CORE_OUTPUT1].format;
+ dma_addr_t mem;
+ u32 reg;
+
+ for (unsigned int frame = 0; frame < 4; frame++) {
+ reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(0, frame);
+ mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ risp_core_write(core, reg, mem);
+
+ /* Only NV16 uses 2 planes. */
+ if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV16M)
+ continue;
+
+ reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(0, frame);
+ mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
+ risp_core_write(core, reg, mem);
+ }
+}
+
+static void risp_core_job_run(struct rcar_isp_core *core)
+{
+ struct rcar_isp_job *job;
+
+ lockdep_assert_held(&core->lock);
+
+ /* ISP not yet started, nothing to do. */
+ if (!core->streaming)
+ return;
+
+ /* If we have active buffers in the ISP core, nothing to do. */
+ if (core->vspx.job)
+ return;
+
+ job = list_first_entry_or_null(&core->risp_jobs,
+ struct rcar_isp_job,
+ job_queue);
+ if (!job)
+ return;
+
+ list_del(&job->job_queue);
+
+ core->vspx.job = job;
+
+ /* Program the ISP register before kicking the VSPX. */
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf = job->buffers[i];
+
+ switch (i) {
+ case RISP_CORE_PARAMS:
+ risp_core_job_run_params(core, &job->vspx_job, buf);
+ break;
+ case RISP_CORE_OUTPUT1:
+ risp_core_job_run_output(core, buf);
+ break;
+ }
+ }
+
+ if (vsp1_isp_job_run(core->vspx.dev, &job->vspx_job)) {
+ /*
+ * Release all buffers in this job if running on the VSPX
+ * failed. Userspace should recover from this, no new jobs are
+ * scheduled.
+ */
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf = job->buffers[i];
+
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
+ core->vspx.job = NULL;
+ kfree(job);
+
+ dev_err(core->dev, "Failed to run job");
+ }
+}
+
+static int risp_core_pixfmt_to_vspx(u32 pixfmt)
+{
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ return V4L2_PIX_FMT_GREY;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ return V4L2_PIX_FMT_Y10;
+ case V4L2_PIX_FMT_SBGGR12:
+ case V4L2_PIX_FMT_SGBRG12:
+ case V4L2_PIX_FMT_SGRBG12:
+ case V4L2_PIX_FMT_SRGGB12:
+ return V4L2_PIX_FMT_Y12;
+ default:
+ return -EINVAL;
+ }
+}
+
+int risp_core_job_prepare(struct rcar_isp_core *core)
+{
+ struct vsp1_isp_job_desc *vspx_job;
+ int vspx_pixfmt = -EINVAL;
+ struct rcar_isp_job *job;
+ int ret;
+
+ lockdep_assert_held(&core->io_lock);
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ if (list_empty(&core->io[i].buffers))
+ return 0;
+ }
+
+ /* Memory is released when the job is consumed. */
+ job = kzalloc(sizeof(*job), GFP_KERNEL);
+ if (!job)
+ return -ENOMEM;
+
+ vspx_job = &job->vspx_job;
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf;
+
+ /*
+ * Extract buffer from the IO queue and save a reference in
+ * the job description. Buffers will be completed when the
+ * corresponding frame will be completed by the ISP.
+ */
+ buf = list_first_entry_or_null(&core->io[i].buffers,
+ struct risp_buffer, list);
+ /*
+ * This should not happen as we have checked there is buffers,
+ * with the lock held, but check the return value anyhow.
+ */
+ if (WARN_ON(!buf)) {
+ ret = -EINVAL;
+ goto error_return_buffers;
+ }
+
+ switch (i) {
+ case RISP_CORE_INPUT1: {
+ u32 isp_pixfmt = core->io[i].format.fmt.pix_mp.pixelformat;
+
+ vspx_pixfmt = risp_core_pixfmt_to_vspx(isp_pixfmt);
+
+ vspx_job->img.fmt = core->io[i].format.fmt.pix_mp;
+ vspx_job->img.fmt.pixelformat = vspx_pixfmt;
+ vspx_job->img.mem =
+ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf,
+ 0);
+ break;
+ }
+ case RISP_CORE_PARAMS: {
+ u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr;
+ u32 pairs = params_buf[0];
+
+ /*
+ * Check config pairs not larger then buffer.
+ *
+ * Remove 8 byte header and each pair is 16 bytes.
+ */
+ if (pairs > (RISP_IO_PARAMS_BUF_SIZE - 8) / 16) {
+ ret = -EINVAL;
+ goto error_return_buffers;
+ }
+
+ /*
+ * Work around undocumented behavior of the ConfigDMA
+ * interface by using MMIO if 16 or less pairs are to
+ * be programmed.
+ *
+ * Programming 15 or less pairs corrupts the image data
+ * following the config buffer, programming exactly 16
+ * pairs freeze the whole VSPX.
+ */
+ if (pairs <= 16) {
+ vspx_job->config.pairs = 0;
+ } else {
+ vspx_job->config.pairs = pairs;
+ vspx_job->config.mem = buf->vsp_buffer.dma_addr;
+ }
+ break;
+ }
+ }
+
+ list_del(&buf->list);
+ job->buffers[i] = buf;
+ }
+
+ if (vspx_pixfmt < 0) {
+ ret = -EINVAL;
+ goto error_return_buffers;
+ }
+
+ ret = vsp1_isp_job_prepare(core->vspx.dev, vspx_job);
+ if (ret)
+ goto error_return_buffers;
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ list_add_tail(&job->job_queue, &core->risp_jobs);
+ risp_core_job_run(core);
+ }
+
+ return 0;
+
+error_return_buffers:
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ if (!job->buffers[i])
+ continue;
+
+ vb2_buffer_done(&job->buffers[i]->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+ kfree(job);
+ return ret;
+}
+
+static int risp_core_config_output(struct rcar_isp_core *core,
+ unsigned int index,
+ const struct v4l2_pix_format_mplane *pix)
+{
+ /* For all frame capture slots. */
+ for (unsigned int frame = 0; frame < 4; frame++) {
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV16M:
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
+ 1);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
+ 0 << 24 | 0 << 16 | 4 << 8 | 16 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
+ 0 << 24 | 0 << 16 | 7 << 8 | 7 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
+ 0 << 28 | 0 << 24 |
+ 0 << 20 | 0 << 16 |
+ 3 << 12 | 0 << 8 |
+ 3 << 4 | 0 << 0);
+
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
+ pix->plane_fmt[0].bytesperline);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(index, frame),
+ pix->plane_fmt[1].bytesperline);
+ break;
+ case V4L2_PIX_FMT_XBGR32:
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame),
+ 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame),
+ 0 << 24 | 0 << 16 | 0 << 8 | 0 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame),
+ 0 << 24 | 0 << 16 | 0 << 8 | 23 << 0);
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame),
+ 0 << 28 | 0 << 24 |
+ 0 << 20 | 0 << 16 |
+ 0 << 12 | 0 << 8 |
+ 3 << 4 | 2 << 0);
+
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame),
+ pix->plane_fmt[0].bytesperline);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ risp_core_write(core,
+ ISPCORE_DMA_IMAGE_FRAME_AXI_ID(index, frame),
+ 0);
+ }
+
+ /* Set image out flush EOF. */
+ risp_core_write(core, ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(index),
+ pix->plane_fmt[0].bytesperline <<
+ ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT);
+
+ /* Enable DMA and set burst length. */
+ risp_core_write(core, ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(index),
+ BIT(31) | 7);
+
+ return 0;
+}
+
+static u32 risp_core_pix2bus(const struct rcar_isp_core_io *io)
+{
+ switch (io->format.fmt.pix_mp.pixelformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ return MEDIA_BUS_FMT_SBGGR8_1X8;
+ case V4L2_PIX_FMT_SGBRG8:
+ return MEDIA_BUS_FMT_SGBRG8_1X8;
+ case V4L2_PIX_FMT_SGRBG8:
+ return MEDIA_BUS_FMT_SGRBG8_1X8;
+ case V4L2_PIX_FMT_SRGGB8:
+ return MEDIA_BUS_FMT_SRGGB8_1X8;
+ case V4L2_PIX_FMT_SBGGR10:
+ return MEDIA_BUS_FMT_SBGGR10_1X10;
+ case V4L2_PIX_FMT_SGBRG10:
+ return MEDIA_BUS_FMT_SGBRG10_1X10;
+ case V4L2_PIX_FMT_SGRBG10:
+ return MEDIA_BUS_FMT_SGRBG10_1X10;
+ case V4L2_PIX_FMT_SRGGB10:
+ return MEDIA_BUS_FMT_SRGGB10_1X10;
+ case V4L2_PIX_FMT_SBGGR12:
+ return MEDIA_BUS_FMT_SBGGR12_1X12;
+ case V4L2_PIX_FMT_SGBRG12:
+ return MEDIA_BUS_FMT_SGBRG12_1X12;
+ case V4L2_PIX_FMT_SGRBG12:
+ return MEDIA_BUS_FMT_SGRBG12_1X12;
+ case V4L2_PIX_FMT_SRGGB12:
+ return MEDIA_BUS_FMT_SRGGB12_1X12;
+ case V4L2_PIX_FMT_XBGR32:
+ return MEDIA_BUS_FMT_RGB888_1X24;
+ case V4L2_PIX_FMT_NV16M:
+ return MEDIA_BUS_FMT_YUYV12_1X24;
+ default:
+ return 0;
+ }
+}
+
+static void risp_core_try_next_job(struct rcar_isp_core *core)
+{
+ lockdep_assert_held(&core->lock);
+
+ struct rcar_isp_job *job = core->vspx.job;
+
+ /* If the ISP or the VSPX is not done with the job, wait. */
+ if (!job || !job->done_isp || !job->done_vspx)
+ return;
+
+ core->vspx.job = NULL;
+ kfree(job);
+
+ core->sequence++;
+
+ /* Kickoff processing of next frame (if any). */
+ risp_core_job_run(core);
+}
+
+static void risp_core_vspx_frame_end(void *data)
+{
+ struct rcar_isp_core *core = data;
+
+ guard(spinlock_irqsave)(&core->lock);
+
+ /*
+ * In tear-down the ISP may report a frame end event but we have already
+ * freed the job. It is safe to ignore the end of frame event.
+ */
+ if (!core->vspx.job)
+ return;
+
+ core->vspx.job->done_vspx = true;
+ risp_core_try_next_job(core);
+}
+
+static int risp_core_power_on(struct rcar_isp_core *core)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = reset_control_deassert(core->csrstc);
+ if (ret)
+ goto err_pm;
+
+ ret = clk_prepare_enable(core->clk);
+ if (ret)
+ goto err_csrstc;
+
+ return 0;
+
+err_csrstc:
+ reset_control_assert(core->csrstc);
+err_pm:
+ pm_runtime_put(core->dev);
+
+ return ret;
+}
+
+static void risp_core_power_off(struct rcar_isp_core *core)
+{
+ clk_disable_unprepare(core->clk);
+
+ reset_control_assert(core->csrstc);
+
+ pm_runtime_put(core->dev);
+}
+
+int risp_core_start_streaming(struct rcar_isp_core *core)
+{
+ struct vsp1_vspx_frame_end vspx_fe = {
+ .vspx_frame_end = risp_core_vspx_frame_end,
+ .frame_end_data = core,
+ };
+
+ struct v4l2_mbus_framefmt inputfmt = {
+ .width = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width,
+ .height = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height,
+ .code = risp_core_pix2bus(&core->io[RISP_CORE_INPUT1]),
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+ };
+
+ struct v4l2_mbus_framefmt hvout = {
+ .width = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width,
+ .height = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height,
+ .code = risp_core_pix2bus(&core->io[RISP_CORE_OUTPUT1]),
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization =
+ core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.pixelformat ==
+ V4L2_PIX_FMT_XBGR32 ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+ int ret;
+
+ scoped_guard(mutex, &core->io_lock) {
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ if (!core->io[i].streaming)
+ return 0;
+ }
+
+ /*
+ * The state core->streaming is protected by core->lock, which
+ * is not held yet. It is however safe to read it here since
+ * core->io_lock is held both in risp_core_stop_streaming() and
+ * here, the only two places the variable is modified.
+ *
+ * With this small implied dependency on the two locks for write
+ * access, the interrupt handler can safely depend sole on the
+ * spinlock core->lock for read access to core->streaming.
+ *
+ * The gain is an interrupt handler which can hold the spinlock
+ * and a start/stop procedure which can reset the ISP using the
+ * reset_control_reset() API, The later which can not be called
+ * from a context that may sleep.
+ *
+ * All other locations core->streaming is read and _all_
+ * locations where it is written core->lock is held.
+ */
+ if (core->streaming)
+ return 0;
+
+ ret = risp_core_power_on(core);
+ if (ret)
+ return ret;
+
+ /* Reset and wait for ISP core to initialize itself. */
+ reset_control_reset(core->rstc);
+ usleep_range(2000, 4000);
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 1);
+
+ /* Configure output DMA */
+ risp_core_config_output(core, 0,
+ &core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp);
+
+ risp_cs_write(core, ISP_CS_STREAMER_VBLANK_REG, inputfmt.width * 25);
+ risp_cs_write(core, ISP_CS_STREAMER_HBLANK_REG, 64);
+
+ /* Enable ISP Streaming bridge. */
+ risp_cs_write(core, ISP_CS_STREAMER_MODE_REG,
+ ISP_CS_STREAMER_MODE_STREAMER_EN);
+
+ /* Start RPP ISP */
+ ret = rppx1_start(core->rpp, &inputfmt, &hvout, NULL);
+ if (ret) {
+ risp_core_power_off(core);
+ return ret;
+ }
+
+ core->vspx.job = NULL;
+ core->sequence = 0;
+ core->streaming = true;
+ }
+
+ /* Start VSPX */
+ vsp1_isp_start_streaming(core->vspx.dev, &vspx_fe);
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ risp_core_job_run(core);
+ }
+ }
+
+ return 0;
+}
+
+void risp_core_stop_streaming(struct rcar_isp_core *core)
+{
+ struct rcar_isp_job *job, *tmp;
+
+ /*
+ * This function releases buffers and jobs: make sure the queues mutex
+ * is held.
+ */
+ lockdep_assert_held(&core->io_lock);
+
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ /* Stop is called by each vdev, only act on the first call. */
+ if (!core->streaming)
+ return;
+
+ /* Stop queueing jobs to VSPX. */
+ core->streaming = false;
+ }
+
+ /* Wait for active VSPX job to finish. */
+ for (unsigned int retry = 0; retry <= 10; retry++) {
+ if (!core->vspx.job)
+ break;
+
+ usleep_range(2000, 4000);
+ }
+
+ if (core->vspx.job)
+ dev_err(core->dev, "Failed to complete running job");
+
+ /* Free all buffers and switch off the hardware. */
+ scoped_guard(spinlock_irqsave, &core->lock) {
+ /* Free all jobs and buffers. */
+ list_for_each_entry_safe(job, tmp, &core->risp_jobs, job_queue) {
+ vsp1_isp_job_release(core->vspx.dev, &job->vspx_job);
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf = job->buffers[i];
+
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ list_del(&job->job_queue);
+ kfree(job);
+ }
+
+ rppx1_stop(core->rpp);
+ risp_cs_write(core, ISP_CS_STREAMER_MODE_REG, 0);
+ risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 0);
+ }
+
+ vsp1_isp_stop_streaming(core->vspx.dev);
+
+ risp_core_power_off(core);
+}
+
+static irqreturn_t risp_core_irq(int irq, void *data)
+{
+ struct rcar_isp_core *core = data;
+ struct rcar_isp_job *job;
+ u32 status;
+
+ status = risp_core_read(core, ISP_CORE_ISPCORE_INT_STATUS);
+ if (!(status & BIT(0)))
+ return IRQ_NONE;
+
+ if (!rppx1_interrupt(core->rpp, &status))
+ return IRQ_HANDLED;
+
+ guard(spinlock_irqsave)(&core->lock);
+
+ job = core->vspx.job;
+ if (!job)
+ return IRQ_HANDLED;
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ struct risp_buffer *buf;
+
+ buf = job->buffers[i];
+
+ switch (i) {
+ case RISP_CORE_STATS:
+ rppx1_stats_fill_isr(core->rpp, status,
+ vb2_plane_vaddr(&buf->vb.vb2_buf, 0));
+ fallthrough;
+ case RISP_CORE_OUTPUT1:
+ case RISP_CORE_INPUT1:
+ buf->vb.sequence = core->sequence;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ fallthrough;
+ case RISP_CORE_PARAMS:
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+
+ core->vspx.job->done_isp = true;
+ risp_core_try_next_job(core);
+
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_subdev_ops risp_core_subdev_ops = {
+};
+
+static int risp_core_create_subdev(struct rcar_isp_core *core)
+{
+ struct v4l2_subdev *subdev = &core->subdev;
+ int ret;
+
+ subdev->owner = THIS_MODULE;
+ subdev->dev = core->dev;
+ v4l2_subdev_init(subdev, &risp_core_subdev_ops);
+ v4l2_set_subdevdata(subdev, core->dev);
+ snprintf(subdev->name, sizeof(subdev->name), "%s %s core",
+ KBUILD_MODNAME, dev_name(core->dev));
+ subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ subdev->entity.function = MEDIA_ENT_F_VID_MUX;
+
+ core->pads[RISP_CORE_INPUT1].flags = MEDIA_PAD_FL_SINK;
+ core->pads[RISP_CORE_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ core->pads[RISP_CORE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[RISP_CORE_OUTPUT1].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&subdev->entity, RISP_CORE_NUM_PADS,
+ core->pads);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd)
+{
+ int ret;
+
+ core->v4l2_dev.mdev = sd->v4l2_dev->mdev;
+
+ /* Register ISP Core subdevice. */
+ ret = v4l2_device_register_subdev(&core->v4l2_dev, &core->subdev);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) {
+ ret = risp_core_io_create(core->dev, core, &core->io[i], i);
+ if (ret) {
+ /* It is safe to destroy io node that is not created. */
+ for (unsigned int n = 0; n < RISP_CORE_NUM_PADS; n++)
+ risp_core_io_destroy(&core->io[n]);
+
+ v4l2_device_unregister_subdev(&core->subdev);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int risp_core_probe_resources(struct rcar_isp_core *core,
+ struct platform_device *pdev)
+{
+ struct platform_device *vspx;
+ struct device_node *of_vspx;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core");
+ if (!res)
+ return -ENODEV;
+
+ core->rppaddr = res->start;
+ core->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(core->base))
+ return PTR_ERR(core->base);
+
+ ret = platform_get_irq_byname(pdev, "core");
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, ret, risp_core_irq, IRQF_SHARED,
+ KBUILD_MODNAME, core);
+ if (ret)
+ return ret;
+
+ core->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(core->clk))
+ return -ENODEV;
+
+ core->rstc = devm_reset_control_get(&pdev->dev, "core");
+ if (IS_ERR(core->rstc))
+ return -ENODEV;
+
+ of_vspx = of_parse_phandle(pdev->dev.of_node, "renesas,vspx", 0);
+ if (!of_vspx)
+ return -ENODEV;
+
+ vspx = of_find_device_by_node(of_vspx);
+ of_node_put(of_vspx);
+ if (!vspx)
+ return -ENODEV;
+
+ /* Attach to VSP-X */
+ core->vspx.dev = &vspx->dev;
+
+ ret = vsp1_isp_init(&vspx->dev);
+ if (ret < 0)
+ goto err_put_vspx;
+
+ /* Attach to the RPP library
+ *
+ * 1. Start and wait for the ISP to startup.
+ * 2. Attach the RPP library and talk with the RPP ISP.
+ * 3. Turn off ISP.
+ * 4. Fail if the RPP is unhappy with the hardware.
+ */
+ ret = clk_prepare_enable(core->clk);
+ if (ret)
+ goto err_put_vspx;
+
+ usleep_range(2000, 4000);
+
+ core->rpp = rppx1_create(core->base, &pdev->dev);
+
+ clk_disable_unprepare(core->clk);
+
+ if (!core->rpp) {
+ ret = -ENODEV;
+ goto err_put_vspx;
+ }
+
+ return 0;
+
+err_put_vspx:
+ put_device(&vspx->dev);
+ return ret;
+}
+
+int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
+ void __iomem *csbase, struct reset_control *csrstc)
+{
+ int ret;
+
+ core->dev = &pdev->dev;
+ core->csrstc = csrstc;
+ core->csbase = csbase;
+
+ ret = risp_core_probe_resources(core, pdev);
+ if (ret) {
+ core->base = NULL;
+ return ret;
+ }
+
+ ret = v4l2_device_register(core->dev, &core->v4l2_dev);
+ if (ret)
+ goto err_destroy_rpp;
+
+ ret = risp_core_create_subdev(core);
+ if (ret)
+ goto err_unregister_v4l2;
+
+ mutex_init(&core->io_lock);
+ spin_lock_init(&core->lock);
+ INIT_LIST_HEAD(&core->risp_jobs);
+
+ return 0;
+
+err_unregister_v4l2:
+ v4l2_device_unregister(&core->v4l2_dev);
+err_destroy_rpp:
+ rppx1_destroy(core->rpp);
+ put_device(core->vspx.dev);
+ return ret;
+}
+
+void risp_core_remove(struct rcar_isp_core *core)
+{
+ /* If we did not probe the ISP core, nothing to do. */
+ if (!core->base)
+ return;
+
+ dev_info(core->dev, "Remove ISP Core\n");
+
+ for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++)
+ risp_core_io_destroy(&core->io[i]);
+
+ v4l2_device_unregister(&core->v4l2_dev);
+
+ mutex_destroy(&core->io_lock);
+ rppx1_destroy(core->rpp);
+ put_device(core->vspx.dev);
+}
diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
index 8fb2cc3b5650..53ce47020d17 100644
--- a/drivers/media/platform/renesas/rcar-isp/csisp.c
+++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
@@ -11,14 +11,13 @@
*/
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/reset.h>
#include <media/mipi-csi2.h>
-#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+
+#include "risp-core.h"
#define ISPINPUTSEL0_REG 0x0008
#define ISPINPUTSEL0_SEL_CSI0 BIT(31)
@@ -158,6 +157,7 @@ struct rcar_isp {
struct device *dev;
void __iomem *csbase;
struct reset_control *rstc;
+ struct rcar_isp_core core;
enum rcar_isp_input csi_input;
@@ -452,6 +452,21 @@ static int risp_parse_dt(struct rcar_isp *isp)
}
/* -----------------------------------------------------------------------------
+ * ISP Core connection
+ */
+
+static int risp_cs_registered(struct v4l2_subdev *sd)
+{
+ struct rcar_isp *isp = sd_to_isp(sd);
+
+ return risp_core_registered(&isp->core, sd);
+}
+
+static const struct v4l2_subdev_internal_ops risp_cs_internal_ops = {
+ .registered = risp_cs_registered,
+};
+
+/* -----------------------------------------------------------------------------
* Platform Device Driver
*/
@@ -477,7 +492,7 @@ static int risp_probe_resources(struct rcar_isp *isp,
if (IS_ERR(isp->csbase))
return PTR_ERR(isp->csbase);
- isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
+ isp->rstc = devm_reset_control_get_shared(&pdev->dev, NULL);
return PTR_ERR_OR_ZERO(isp->rstc);
}
@@ -541,14 +556,31 @@ static int risp_probe(struct platform_device *pdev)
if (ret)
goto error_notifier;
+ ret = risp_core_probe(&isp->core, pdev, isp->csbase, isp->rstc);
+ switch (ret) {
+ case 0:
+ /* The device have an ISP core. */
+ isp->subdev.internal_ops = &risp_cs_internal_ops;
+ break;
+ case -ENODEV:
+ /* The device don't have an ISP core, that is OK. */
+ ret = 0;
+ break;
+ default:
+ /* Something went wrong registering the ISP core. */
+ goto error_subdev;
+ }
+
ret = v4l2_async_register_subdev(&isp->subdev);
if (ret < 0)
- goto error_subdev;
+ goto error_core;
dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
return 0;
+error_core:
+ risp_core_remove(&isp->core);
error_subdev:
v4l2_subdev_cleanup(&isp->subdev);
error_notifier:
@@ -564,6 +596,8 @@ static void risp_remove(struct platform_device *pdev)
{
struct rcar_isp *isp = platform_get_drvdata(pdev);
+ risp_core_remove(&isp->core);
+
v4l2_async_nf_unregister(&isp->notifier);
v4l2_async_nf_cleanup(&isp->notifier);
diff --git a/drivers/media/platform/renesas/rcar-isp/risp-core.h b/drivers/media/platform/renesas/rcar-isp/risp-core.h
new file mode 100644
index 000000000000..627a762d6b19
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/risp-core.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Renesas Electronics Corp.
+ * Copyright (C) 2026 Ideas on Board Oy
+ * Copyright (C) 2026 Ragnatech AB
+ */
+
+#ifndef __RCAR_ISP__
+#define __RCAR_ISP__
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <media/rppx1.h>
+#include <media/vsp1.h>
+
+/* Max 2048 address + value pairs in one VSPX buffer, increase if needed. */
+#define RISP_IO_PARAMS_BUF_SIZE 16384
+
+struct rcar_isp_core;
+
+enum risp_core_pads {
+ RISP_CORE_INPUT1,
+ RISP_CORE_PARAMS,
+ RISP_CORE_STATS,
+ RISP_CORE_OUTPUT1,
+ RISP_CORE_NUM_PADS,
+};
+
+/**
+ * struct risp_buffer - Describe an IO buffer
+ * @vb: The VB2 buffer
+ * @list: List of buffers queued to the IO queue
+ * @vsp_buffer: Buffer mapped from VSP-X, only used for params IO
+ */
+struct risp_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ struct vsp1_isp_buffer_desc vsp_buffer;
+};
+
+/**
+ * struct rcar_isp_core_io - Information for a IO video devices
+ * @core: Backlink to the common ISP core structure
+ *
+ * @lock: Protects @vdev, @pad and @queue + open/close fops
+ * @vdev: V4L2 video device associated with this IO port
+ * @pad: Media pad for @vdev
+ * @queue: VB2 buffers queue for $@vdev
+ *
+ * @streaming: Flag to indicate if device is streaming, or not
+ * @buffers: List of buffers queued to the device
+ *
+ * @format: The active V4L2 format
+ */
+struct rcar_isp_core_io {
+ struct rcar_isp_core *core;
+
+ struct mutex lock; /* See KDoc block. */
+ struct video_device vdev;
+ struct media_pad pad;
+ struct vb2_queue queue;
+
+ bool streaming;
+ struct list_head buffers;
+
+ struct v4l2_format format;
+};
+
+/**
+ * struct rcar_isp_job - R-Car ISP job description
+ *
+ * Both done_isp and done_vspx shall be set before the job can be considered
+ * completely done.
+ *
+ * @buffers: IO buffers that form a job
+ * @vspx_job: VSPX job description
+ * @job_queue: list handle
+ * @done_isp: Flag to indicate the ISP is done with the job
+ * @done_vspx: Flag to indicate the VSPX is done with the job
+ */
+struct rcar_isp_job {
+ struct risp_buffer *buffers[RISP_CORE_NUM_PADS];
+ struct vsp1_isp_job_desc vspx_job;
+ struct list_head job_queue;
+ bool done_isp;
+ bool done_vspx;
+};
+
+/**
+ * struct rcar_isp_vspx - R-Car ISP job description
+ *
+ * @dev: Device reference to VSPX
+ * @job: Job currently being processed by VSPX
+ */
+struct rcar_isp_vspx {
+ struct device *dev;
+ struct rcar_isp_job *job;
+};
+
+/**
+ * struct rcar_isp_core - ISP Core
+ * @dev: (OF) device
+ * @rppaddr: Hardware address of the RPP ISP (from OF)
+ * @clk: The clock for the ISP CORE
+ * @rstc: The reset for the ISP Core
+ * @csrstc: The reset for the ISP Channel Selector
+ *
+ * @base: MMIO base of the ISP CORE
+ * @csbase: MMIO base of the ISP CS
+ *
+ * @subdev: V4L2 subdevice to represent the ISP CORE
+ * @pads: Media pad for @subdev
+ *
+ * @v4l2_dev: V4L2 device
+ * @rpp: Handle to the RPP ISP connected to the ISP CORE
+ *
+ * @io_lock: Protect io[*].streaming and io[*].buffers
+ * @io: Array of IO ports to the ISP CORE
+ *
+ * @lock: Protects @vspx, @risp_jobs, @sequence and @streaming
+ * @vspx: Handle to the resources used by VSPX connected to the ISP CORE
+ * @risp_jobs: Queue of VSPX transfer jobs
+ * @sequence: V4L2 buffers sequence number
+ * @streaming: Tracks if the device is streaming
+ */
+struct rcar_isp_core {
+ struct device *dev;
+
+ u32 rppaddr;
+ struct clk *clk;
+
+ struct reset_control *rstc;
+ struct reset_control *csrstc;
+
+ void __iomem *base;
+ void __iomem *csbase;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RISP_CORE_NUM_PADS];
+
+ struct v4l2_device v4l2_dev;
+ struct rppx1 *rpp;
+
+ struct mutex io_lock; /* See KDoc block. */
+ struct rcar_isp_core_io io[RISP_CORE_NUM_PADS];
+
+ spinlock_t lock; /* See KDoc block. */
+ struct rcar_isp_vspx vspx;
+ struct list_head risp_jobs;
+ unsigned int sequence;
+ bool streaming;
+};
+
+int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev,
+ void __iomem *csbase, struct reset_control *csrstc);
+void risp_core_remove(struct rcar_isp_core *core);
+int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd);
+
+int risp_core_job_prepare(struct rcar_isp_core *core);
+
+int risp_core_start_streaming(struct rcar_isp_core *core);
+void risp_core_stop_streaming(struct rcar_isp_core *core);
+
+int risp_core_io_create(struct device *dev, struct rcar_isp_core *core,
+ struct rcar_isp_core_io *io, unsigned int pad);
+void risp_core_io_destroy(struct rcar_isp_core_io *io);
+
+#endif
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index 5bf334e173d2..b426bc7898bf 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -116,11 +116,9 @@ struct rzg2l_cru_info {
* @scratch_phys: physical address of the scratch buffer
*
* @qlock: protects @queue_buf, @buf_list, @sequence
- * @state
* @queue_buf: Keeps track of buffers given to HW slot
* @buf_list: list of queued buffers
* @sequence: V4L2 buffers sequence number
- * @state: keeps track of operation state
*
* @format: active V4L2 pixel format
*/
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 5185a547461d..91eda5034248 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -851,6 +851,11 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height);
+ if (info->has_stride) {
+ pix->bytesperline = ALIGN(pix->bytesperline, RZG2L_CRU_STRIDE_ALIGN);
+ pix->sizeimage = pix->bytesperline * pix->height;
+ }
+
dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
pix->width, pix->height, pix->bytesperline, pix->sizeimage);
}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index 325be30836d7..360a42502947 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
@@ -155,9 +155,7 @@ static int brx_set_format(struct v4l2_subdev *subdev,
/* Propagate the format code to all pads. */
if (fmt->pad == BRX_PAD_SINK(0)) {
- unsigned int i;
-
- for (i = 0; i <= brx->entity.source_pad; ++i) {
+ for (unsigned int i = 0; i <= brx->entity.source_pad; ++i) {
format = v4l2_subdev_state_get_format(state, i);
format->code = fmt->format.code;
}
@@ -271,7 +269,6 @@ static void brx_configure_stream(struct vsp1_entity *entity,
struct vsp1_brx *brx = to_brx(&entity->subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
- unsigned int i;
format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);
@@ -315,7 +312,7 @@ static void brx_configure_stream(struct vsp1_entity *entity,
VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
VI6_BRU_ROP_AROP(VI6_ROP_NOP));
- for (i = 0; i < brx->entity.source_pad; ++i) {
+ for (unsigned int i = 0; i < brx->entity.source_pad; ++i) {
bool premultiplied = false;
u32 ctrl = 0;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
index a6e4bcab5101..4b7d07d730da 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
@@ -43,14 +43,13 @@ static inline void vsp1_clu_write(struct vsp1_clu *clu,
static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl)
{
struct vsp1_dl_body *dlb;
- unsigned int i;
dlb = vsp1_dl_body_get(clu->pool);
if (!dlb)
return -ENOMEM;
vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0);
- for (i = 0; i < CLU_SIZE; ++i)
+ for (unsigned int i = 0; i < CLU_SIZE; ++i)
vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
scoped_guard(spinlock_irq, &clu->lock) {
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index 3dc74fed91dc..6430f2ec8b32 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -257,7 +257,6 @@ vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
{
struct vsp1_dl_body_pool *pool;
size_t dlb_size;
- unsigned int i;
pool = kzalloc_obj(*pool);
if (!pool)
@@ -291,7 +290,7 @@ vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
spin_lock_init(&pool->lock);
INIT_LIST_HEAD(&pool->free);
- for (i = 0; i < num_bodies; ++i) {
+ for (unsigned int i = 0; i < num_bodies; ++i) {
struct vsp1_dl_body *dlb = &pool->bodies[i];
dlb->pool = pool;
@@ -426,7 +425,6 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type,
unsigned int num_cmds)
{
struct vsp1_dl_cmd_pool *pool;
- unsigned int i;
size_t cmd_size;
pool = kzalloc_obj(*pool);
@@ -457,7 +455,7 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type,
return NULL;
}
- for (i = 0; i < num_cmds; ++i) {
+ for (unsigned int i = 0; i < num_cmds; ++i) {
struct vsp1_dl_ext_cmd *cmd = &pool->cmds[i];
size_t cmd_offset = i * cmd_size;
/* data_offset must be 16 byte aligned for DMA. */
@@ -1046,7 +1044,6 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
/* Hardware Setup */
void vsp1_dlm_setup(struct vsp1_device *vsp1)
{
- unsigned int i;
u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT)
| VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0
| VI6_DL_CTRL_DLE;
@@ -1054,7 +1051,7 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1)
| VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT;
if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) {
- for (i = 0; i < vsp1->info->wpf_count; ++i)
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i)
vsp1_write(vsp1, VI6_DL_EXT_CTRL(i), ext_dl);
}
@@ -1092,7 +1089,6 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
{
struct vsp1_dl_manager *dlm;
size_t header_size;
- unsigned int i;
dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);
if (!dlm)
@@ -1128,7 +1124,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
if (!dlm->pool)
return NULL;
- for (i = 0; i < prealloc; ++i) {
+ for (unsigned int i = 0; i < prealloc; ++i) {
struct vsp1_dl_list *dl;
dl = vsp1_dl_list_alloc(dlm);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index f6fbd3475329..9cd5c025d2be 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -419,13 +419,12 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
struct vsp1_entity *uif;
bool use_uif = false;
struct vsp1_brx *brx;
- unsigned int i;
int ret;
/* Count the number of enabled inputs and sort them by Z-order. */
pipe->num_inputs = 0;
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *rpf = vsp1->rpf[i];
unsigned int j;
@@ -457,7 +456,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
brx = to_brx(&pipe->brx->subdev);
/* Setup the RPF input pipeline for every enabled input. */
- for (i = 0; i < pipe->brx->source_pad; ++i) {
+ for (unsigned int i = 0; i < pipe->brx->source_pad; ++i) {
struct vsp1_rwpf *rpf = inputs[i];
if (!rpf) {
@@ -732,7 +731,6 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index)
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe;
struct vsp1_pipeline *pipe;
- unsigned int i;
int ret;
if (pipe_index >= vsp1->info->lif_count)
@@ -748,7 +746,7 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index)
if (ret == -ETIMEDOUT)
dev_err(vsp1->dev, "DRM pipeline stop timeout\n");
- for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
struct vsp1_rwpf *rpf = pipe->inputs[i];
if (!rpf)
@@ -964,8 +962,6 @@ EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg);
int vsp1_drm_init(struct vsp1_device *vsp1)
{
- unsigned int i;
-
vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL);
if (!vsp1->drm)
return -ENOMEM;
@@ -973,7 +969,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
mutex_init(&vsp1->drm->lock);
/* Create one DRM pipeline per LIF. */
- for (i = 0; i < vsp1->info->lif_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) {
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i];
struct vsp1_pipeline *pipe = &drm_pipe->pipe;
@@ -1010,7 +1006,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
}
/* Disable all RPFs initially. */
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *input = vsp1->rpf[i];
INIT_LIST_HEAD(&input->entity.list_pipe);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 627b5046fa80..762a9e776a94 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -51,10 +51,9 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
VI6_WPF_IRQ_STA_UND;
struct vsp1_device *vsp1 = data;
irqreturn_t ret = IRQ_NONE;
- unsigned int i;
u32 status;
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
if (wpf == NULL)
@@ -103,7 +102,6 @@ static int vsp1_create_sink_links(struct vsp1_device *vsp1,
{
struct media_entity *entity = &sink->subdev.entity;
struct vsp1_entity *source;
- unsigned int pad;
int ret;
list_for_each_entry(source, &vsp1->entities, list_dev) {
@@ -123,7 +121,7 @@ static int vsp1_create_sink_links(struct vsp1_device *vsp1,
source->index == sink->index
? MEDIA_LNK_FL_ENABLED : 0;
- for (pad = 0; pad < entity->num_pads; ++pad) {
+ for (unsigned int pad = 0; pad < entity->num_pads; ++pad) {
if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
continue;
@@ -144,7 +142,6 @@ static int vsp1_create_sink_links(struct vsp1_device *vsp1,
static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
{
struct vsp1_entity *entity;
- unsigned int i;
int ret;
list_for_each_entry(entity, &vsp1->entities, list_dev) {
@@ -177,7 +174,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
return ret;
}
- for (i = 0; i < vsp1->info->lif_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) {
if (!vsp1->lif[i])
continue;
@@ -189,7 +186,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
return ret;
}
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *rpf = vsp1->rpf[i];
ret = media_create_pad_link(&rpf->video->video.entity, 0,
@@ -201,7 +198,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
return ret;
}
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
/*
* Connect the video device to the WPF. All connections are
* immutable.
@@ -253,7 +250,6 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
struct media_device *mdev = &vsp1->media_dev;
struct v4l2_device *vdev = &vsp1->v4l2_dev;
struct vsp1_entity *entity;
- unsigned int i;
int ret;
mdev->dev = vsp1->dev;
@@ -365,7 +361,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
* enabled skip the LIFs, even when present.
*/
if (!vsp1->info->uapi) {
- for (i = 0; i < vsp1->info->lif_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) {
struct vsp1_lif *lif;
lif = vsp1_lif_create(vsp1, i);
@@ -389,7 +385,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *rpf;
rpf = vsp1_rpf_create(vsp1, i);
@@ -423,7 +419,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->info->uds_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->uds_count; ++i) {
struct vsp1_uds *uds;
uds = vsp1_uds_create(vsp1, i);
@@ -436,7 +432,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&uds->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->info->uif_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->uif_count; ++i) {
struct vsp1_uif *uif;
uif = vsp1_uif_create(vsp1, i);
@@ -449,7 +445,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&uif->entity.list_dev, &vsp1->entities);
}
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf;
wpf = vsp1_wpf_create(vsp1, i);
@@ -543,11 +539,10 @@ int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
static int vsp1_device_init(struct vsp1_device *vsp1)
{
- unsigned int i;
int ret;
/* Reset any channel that might be running. */
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
ret = vsp1_reset_wpf(vsp1, i);
if (ret < 0)
return ret;
@@ -556,13 +551,13 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
(8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
- for (i = 0; i < vsp1->info->rpf_count; ++i)
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i)
vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
- for (i = 0; i < vsp1->info->uds_count; ++i)
+ for (unsigned int i = 0; i < vsp1->info->uds_count; ++i)
vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
- for (i = 0; i < vsp1->info->uif_count; ++i)
+ for (unsigned int i = 0; i < vsp1->info->uif_count; ++i)
vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
@@ -587,11 +582,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
static void vsp1_mask_all_interrupts(struct vsp1_device *vsp1)
{
- unsigned int i;
-
- for (i = 0; i < vsp1->info->lif_count; ++i)
+ for (unsigned int i = 0; i < vsp1->info->lif_count; ++i)
vsp1_write(vsp1, VI6_DISP_IRQ_ENB(i), 0);
- for (i = 0; i < vsp1->info->wpf_count; ++i)
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i)
vsp1_write(vsp1, VI6_WPF_IRQ_ENB(i), 0);
}
@@ -891,7 +884,6 @@ static const struct vsp1_device_info rzg2l_vsp2_device_info = {
static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1)
{
const struct vsp1_device_info *info;
- unsigned int i;
u32 model;
u32 soc;
@@ -909,7 +901,7 @@ static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1)
model = vsp1->version & VI6_IP_VERSION_MODEL_MASK;
soc = vsp1->version & VI6_IP_VERSION_SOC_MASK;
- for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
info = &vsp1_device_infos[i];
if (model == info->version && (!info->soc || soc == info->soc))
@@ -947,7 +939,7 @@ static int vsp1_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- vsp1->rstc = devm_reset_control_get_shared(&pdev->dev, NULL);
+ vsp1->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
if (IS_ERR(vsp1->rstc))
return dev_err_probe(&pdev->dev, PTR_ERR(vsp1->rstc),
"failed to get reset control\n");
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index 2ae2a573f0de..26b21559878d 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
@@ -372,10 +372,8 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
static int vsp1_entity_init_state(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state)
{
- unsigned int pad;
-
/* Initialize all pad formats with default values. */
- for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
+ for (unsigned int pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
struct v4l2_subdev_format format = {
.pad = pad,
.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
index 0ef512e3a94b..d3eaa7c2d595 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
@@ -42,7 +42,6 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity)
{
struct vsp1_hgo *hgo = to_hgo(&entity->subdev);
struct vsp1_histogram_buffer *buf;
- unsigned int i;
size_t size;
u32 *data;
@@ -56,7 +55,7 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity)
*data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN);
*data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM);
- for (i = 0; i < 256; ++i) {
+ for (unsigned int i = 0; i < 256; ++i) {
vsp1_write(hgo->histo.entity.vsp1,
VI6_HGO_EXT_HIST_ADDR, i);
*data++ = vsp1_hgo_read(hgo, VI6_HGO_EXT_HIST_DATA);
@@ -67,7 +66,7 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity)
*data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN);
*data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM);
- for (i = 0; i < 64; ++i)
+ for (unsigned int i = 0; i < 64; ++i)
*data++ = vsp1_hgo_read(hgo, VI6_HGO_G_HISTO(i));
size = (2 + 64) * sizeof(u32);
@@ -80,7 +79,7 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity)
*data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM);
*data++ = vsp1_hgo_read(hgo, VI6_HGO_B_SUM);
- for (i = 0; i < 64; ++i) {
+ for (unsigned int i = 0; i < 64; ++i) {
data[i] = vsp1_hgo_read(hgo, VI6_HGO_R_HISTO(i));
data[i+64] = vsp1_hgo_read(hgo, VI6_HGO_G_HISTO(i));
data[i+128] = vsp1_hgo_read(hgo, VI6_HGO_B_HISTO(i));
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
index 78b5a9201c70..8c04bdec8510 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
@@ -42,8 +42,6 @@ void vsp1_hgt_frame_end(struct vsp1_entity *entity)
{
struct vsp1_hgt *hgt = to_hgt(&entity->subdev);
struct vsp1_histogram_buffer *buf;
- unsigned int m;
- unsigned int n;
u32 *data;
buf = vsp1_histogram_buffer_get(&hgt->histo);
@@ -55,9 +53,10 @@ void vsp1_hgt_frame_end(struct vsp1_entity *entity)
*data++ = vsp1_hgt_read(hgt, VI6_HGT_MAXMIN);
*data++ = vsp1_hgt_read(hgt, VI6_HGT_SUM);
- for (m = 0; m < 6; ++m)
- for (n = 0; n < 32; ++n)
+ for (unsigned int m = 0; m < 6; ++m) {
+ for (unsigned int n = 0; n < 32; ++n)
*data++ = vsp1_hgt_read(hgt, VI6_HGT_HISTO(m, n));
+ }
vsp1_histogram_buffer_complete(&hgt->histo, buf, HGT_DATA_SIZE);
}
@@ -71,7 +70,6 @@ void vsp1_hgt_frame_end(struct vsp1_entity *entity)
static int hgt_hue_areas_try_ctrl(struct v4l2_ctrl *ctrl)
{
const u8 *values = ctrl->p_new.p_u8;
- unsigned int i;
/*
* The hardware has constraints on the hue area boundaries beyond the
@@ -83,7 +81,7 @@ static int hgt_hue_areas_try_ctrl(struct v4l2_ctrl *ctrl)
*
* Start by verifying the common part...
*/
- for (i = 1; i < (HGT_NUM_HUE_AREAS * 2) - 1; ++i) {
+ for (unsigned int i = 1; i < (HGT_NUM_HUE_AREAS * 2) - 1; ++i) {
if (values[i] > values[i+1])
return -EINVAL;
}
@@ -138,7 +136,6 @@ static void hgt_configure_stream(struct vsp1_entity *entity,
unsigned int vratio;
u8 lower;
u8 upper;
- unsigned int i;
crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK);
@@ -153,7 +150,7 @@ static void hgt_configure_stream(struct vsp1_entity *entity,
(crop->height << VI6_HGT_SIZE_VSIZE_SHIFT));
scoped_guard(mutex, hgt->ctrls.lock) {
- for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) {
+ for (unsigned int i = 0; i < HGT_NUM_HUE_AREAS; ++i) {
lower = hgt->hue_areas[i*2 + 0];
upper = hgt->hue_areas[i*2 + 1];
vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i),
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
index a22c31e17cb7..6433b5515ef9 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
@@ -40,13 +40,12 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut,
static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl)
{
struct vsp1_dl_body *dlb;
- unsigned int i;
dlb = vsp1_dl_body_get(lut->pool);
if (!dlb)
return -ENOMEM;
- for (i = 0; i < LUT_SIZE; ++i)
+ for (unsigned int i = 0; i < LUT_SIZE; ++i)
vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i,
ctrl->p_new.p_u32[i]);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index 32bb02ce0366..c6a624e193b3 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
@@ -391,16 +391,15 @@ void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func,
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
{
struct vsp1_entity *entity;
- unsigned int i;
if (pipe->brx) {
struct vsp1_brx *brx = to_brx(&pipe->brx->subdev);
- for (i = 0; i < ARRAY_SIZE(brx->inputs); ++i)
+ for (unsigned int i = 0; i < ARRAY_SIZE(brx->inputs); ++i)
brx->inputs[i].rpf = NULL;
}
- for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i)
+ for (unsigned int i = 0; i < ARRAY_SIZE(pipe->inputs); ++i)
pipe->inputs[i] = NULL;
pipe->output = NULL;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c
index 5a1d284213ad..b85351986fae 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_video.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c
@@ -104,14 +104,13 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
const struct vsp1_format_info *info;
unsigned int width = pix->width;
unsigned int height = pix->height;
- unsigned int i;
/*
* Backward compatibility: replace deprecated RGB formats by their XRGB
* equivalent. This selects the format older userspace applications want
* while still exposing the new format.
*/
- for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) {
if (xrgb_formats[i][0] == pix->pixelformat) {
pix->pixelformat = xrgb_formats[i][1];
break;
@@ -161,7 +160,7 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
* the datasheet, strides not aligned to a multiple of 128 bytes result
* in image corruption.
*/
- for (i = 0; i < min(info->planes, 2U); ++i) {
+ for (unsigned int i = 0; i < min(info->planes, 2U); ++i) {
unsigned int hsub = i > 0 ? info->hsub : 1;
unsigned int vsub = i > 0 ? info->vsub : 1;
unsigned int align = 128;
@@ -209,7 +208,6 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *next = NULL;
struct vsp1_vb2_buffer *done;
- unsigned int i;
scoped_guard(spinlock_irqsave, &video->irqlock) {
if (list_empty(&video->irqqueue))
@@ -227,7 +225,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
done->buf.sequence = pipe->sequence;
done->buf.vb2_buf.timestamp = ktime_get_ns();
- for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
+ for (unsigned int i = 0; i < done->buf.vb2_buf.num_planes; ++i)
vb2_set_plane_payload(&done->buf.vb2_buf, i,
vb2_plane_size(&done->buf.vb2_buf, i));
vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE);
@@ -267,7 +265,6 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
struct vsp1_entity *entity;
struct vsp1_dl_body *dlb;
struct vsp1_dl_list *dl;
- unsigned int partition;
dl = vsp1_dl_list_get(pipe->output->dlm);
@@ -289,7 +286,7 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
vsp1_video_pipeline_run_partition(pipe, dl, 0);
/* Process consecutive partitions as necessary. */
- for (partition = 1; partition < pipe->partitions; ++partition) {
+ for (unsigned int partition = 1; partition < pipe->partitions; ++partition) {
struct vsp1_dl_list *dl_next;
dl_next = vsp1_dl_list_get(pipe->output->dlm);
@@ -321,7 +318,6 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
enum vsp1_pipeline_state state;
unsigned long flags;
- unsigned int i;
/* M2M Pipelines should never call here with an incomplete frame. */
WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED));
@@ -329,7 +325,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
spin_lock_irqsave(&pipe->irqlock, flags);
/* Complete buffers on all video nodes. */
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) {
if (!pipe->inputs[i])
continue;
@@ -449,7 +445,6 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
struct media_graph graph;
struct media_entity *entity = &video->video.entity;
struct media_device *mdev = entity->graph_obj.mdev;
- unsigned int i;
int ret;
/* Walk the graph to locate the entities and video nodes. */
@@ -517,7 +512,7 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
* Follow links downstream for each input and make sure the graph
* contains no loop and that all branches end at the output WPF.
*/
- for (i = 0; i < video->vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < video->vsp1->info->rpf_count; ++i) {
if (!pipe->inputs[i])
continue;
@@ -606,13 +601,12 @@ vsp1_video_queue_setup(struct vb2_queue *vq,
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
const struct v4l2_pix_format_mplane *format = &video->rwpf->format;
- unsigned int i;
if (*nplanes) {
if (*nplanes != format->num_planes)
return -EINVAL;
- for (i = 0; i < *nplanes; i++)
+ for (unsigned int i = 0; i < *nplanes; i++)
if (sizes[i] < format->plane_fmt[i].sizeimage)
return -EINVAL;
return 0;
@@ -620,7 +614,7 @@ vsp1_video_queue_setup(struct vb2_queue *vq,
*nplanes = format->num_planes;
- for (i = 0; i < format->num_planes; ++i)
+ for (unsigned int i = 0; i < format->num_planes; ++i)
sizes[i] = format->plane_fmt[i].sizeimage;
return 0;
@@ -682,7 +676,6 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
const struct v4l2_mbus_framefmt *format;
struct vsp1_entity *entity;
unsigned int div_size;
- unsigned int i;
/*
* Partitions are computed on the size before rotation, use the format
@@ -716,7 +709,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
if (!pipe->part_table)
return -ENOMEM;
- for (i = 0; i < pipe->partitions; ++i)
+ for (unsigned int i = 0; i < pipe->partitions; ++i)
vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i],
div_size, i);
@@ -1116,7 +1109,6 @@ static const struct media_entity_operations vsp1_video_media_ops = {
void vsp1_video_suspend(struct vsp1_device *vsp1)
{
- unsigned int i;
int ret;
/*
@@ -1124,7 +1116,7 @@ void vsp1_video_suspend(struct vsp1_device *vsp1)
* pipelines twice, first to set them all to the stopping state, and
* then to wait for the stop to complete.
*/
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
@@ -1141,7 +1133,7 @@ void vsp1_video_suspend(struct vsp1_device *vsp1)
}
}
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
@@ -1162,10 +1154,8 @@ void vsp1_video_suspend(struct vsp1_device *vsp1)
void vsp1_video_resume(struct vsp1_device *vsp1)
{
- unsigned int i;
-
/* Resume all running pipelines. */
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
index 0ec707d2913f..821887815eb2 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
@@ -232,7 +232,6 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
const struct v4l2_mbus_framefmt *source_format;
const struct v4l2_mbus_framefmt *sink_format;
unsigned int index = wpf->entity.index;
- unsigned int i;
u32 outfmt = 0;
u32 srcrpf = 0;
int ret;
@@ -314,7 +313,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
* inputs as sub-layers and select the virtual RPF as the master
* layer. For VSPX configure the enabled sources as masters.
*/
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *input = pipe->inputs[i];
if (!input)
@@ -399,7 +398,6 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
unsigned int left;
unsigned int offset;
unsigned int flip;
- unsigned int i;
/*
* Cropping. The partition algorithm can split the image into multiple
@@ -448,7 +446,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
else
offset = left;
- for (i = 0; i < format->num_planes; ++i) {
+ for (unsigned int i = 0; i < format->num_planes; ++i) {
unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
@@ -490,7 +488,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
* Compute the output coordinate. The partition
* horizontal (left) offset becomes a vertical offset.
*/
- for (i = 0; i < format->num_planes; ++i) {
+ for (unsigned int i = 0; i < format->num_planes; ++i) {
unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
mem.addr[i] += hoffset / hsub
diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c
index ca1c268303dd..c4a71306278b 100644
--- a/drivers/media/platform/rockchip/rga/rga3-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga3-hw.c
@@ -450,10 +450,14 @@ static void *rga3_adjust_and_map_format(struct rga_ctx *ctx,
other_format = is_output ? &ctx->out.pix : &ctx->in.pix;
other_format_info = v4l2_format_info(other_format->pixelformat);
- if ((v4l2_is_format_rgb(format_info) &&
- v4l2_is_format_yuv(other_format_info)) ||
- (v4l2_is_format_yuv(format_info) &&
- v4l2_is_format_rgb(other_format_info))) {
+ /*
+ * Only apply the quantization restrictions when we need to
+ * convert between RGB and YUV. Otherwise there is no point
+ * to limit the quantization for operations like scaling or
+ * rotations.
+ */
+ if (v4l2_is_format_yuv(format_info) &&
+ v4l2_is_format_rgb(other_format_info)) {
/*
* The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions
* Additionally BT709 and BT2020 only support limited range YUV.
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
index f89602075121..9c4a6093af32 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
@@ -209,6 +209,9 @@ static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx,
int i, j;
int dPoc;
+ if ((unsigned int)rps_data->delta_idx_minus1 + 1 > idx)
+ return;
+
ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */
delta_rps = (1 - 2 * rps_data->delta_rps_sign) *
(rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
index 87abf93dfd5e..ff3942f91c5d 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
@@ -258,9 +258,9 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
for (i = 0; i <= pps->num_tile_rows_minus1; i++)
WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i));
} else {
- WRITE_PPS(((sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
+ WRITE_PPS(DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_y) - 1,
COLUMN_WIDTH(0));
- WRITE_PPS(((sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
+ WRITE_PPS(DIV_ROUND_UP(sps->pic_height_in_luma_samples, ctb_size_y) - 1,
ROW_HEIGHT(0));
}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
index fe6414a17551..d07c74679552 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
@@ -261,8 +261,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
memset(row_height, 0, sizeof(row_height));
max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size);
- pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width;
- pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width;
+ pic_in_cts_width = DIV_ROUND_UP(width, max_cu_width);
+ pic_in_cts_height = DIV_ROUND_UP(height, max_cu_width);
if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) {
if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) {
@@ -275,8 +275,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
column_width, row_height);
}
} else {
- column_width[0] = (width + max_cu_width - 1) / max_cu_width;
- row_height[0] = (height + max_cu_width - 1) / max_cu_width;
+ column_width[0] = DIV_ROUND_UP(width, max_cu_width);
+ row_height[0] = DIV_ROUND_UP(height, max_cu_width);
}
for (i = 0; i < 20; i++) {
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
index 3575338a531a..3462d995d4cf 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
@@ -287,8 +287,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
memset(row_height, 0, sizeof(row_height));
max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size);
- pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width;
- pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width;
+ pic_in_cts_width = DIV_ROUND_UP(width, max_cu_width);
+ pic_in_cts_height = DIV_ROUND_UP(height, max_cu_width);
if (tiles_enabled) {
if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) {
@@ -301,8 +301,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
column_width, row_height);
}
} else {
- column_width[0] = (width + max_cu_width - 1) / max_cu_width;
- row_height[0] = (height + max_cu_width - 1) / max_cu_width;
+ column_width[0] = DIV_ROUND_UP(width, max_cu_width);
+ row_height[0] = DIV_ROUND_UP(height, max_cu_width);
}
for (i = 0; i < 20; i++)
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 1d1e9bfef8e9..061281f903f3 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -278,12 +278,12 @@ static const struct rkvdec_ctrl_desc vdpu38x_hevc_ctrl_descs[] = {
{
.cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS,
.cfg.ops = &rkvdec_ctrl_ops,
- .cfg.dims = { 65 },
+ .cfg.dims = { 64 },
},
{
.cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS,
.cfg.ops = &rkvdec_ctrl_ops,
- .cfg.dims = { 65 },
+ .cfg.dims = { 32 },
},
};
@@ -1818,8 +1818,8 @@ static int rkvdec_probe(struct platform_device *pdev)
vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return -ENXIO;
+ if (irq < 0)
+ return irq;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
rkvdec_irq_handler, IRQF_ONESHOT,
diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c
index 221e3c447f36..bb06847f3a63 100644
--- a/drivers/media/platform/samsung/s3c-camif/camif-core.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c
@@ -12,7 +12,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -302,7 +301,6 @@ static int camif_media_dev_init(struct camif_dev *camif)
struct media_device *md = &camif->media_dev;
struct v4l2_device *v4l2_dev = &camif->v4l2_dev;
unsigned int ip_rev = camif->variant->ip_revision;
- int ret;
memset(md, 0, sizeof(*md));
snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF",
@@ -317,11 +315,7 @@ static int camif_media_dev_init(struct camif_dev *camif)
media_device_init(md);
- ret = v4l2_device_register(camif->dev, v4l2_dev);
- if (ret < 0)
- return ret;
-
- return ret;
+ return v4l2_device_register(camif->dev, v4l2_dev);
}
static void camif_clk_put(struct camif_dev *camif)
@@ -412,7 +406,7 @@ static int s3c_camif_probe(struct platform_device *pdev)
camif->dev = dev;
- if (!pdata || !pdata->gpio_get || !pdata->gpio_put) {
+ if (!pdata) {
dev_err(dev, "wrong platform data\n");
return -EINVAL;
}
@@ -429,9 +423,7 @@ static int s3c_camif_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- ret = pdata->gpio_get();
- if (ret < 0)
- return ret;
+ /* FIXME: get GPIOs here */
ret = s3c_camif_create_subdev(camif);
if (ret < 0)
@@ -504,14 +496,12 @@ err_disable:
err_clk:
s3c_camif_unregister_subdev(camif);
err_sd:
- pdata->gpio_put();
return ret;
}
static void s3c_camif_remove(struct platform_device *pdev)
{
struct camif_dev *camif = platform_get_drvdata(pdev);
- struct s3c_camif_plat_data *pdata = &camif->pdata;
media_device_unregister(&camif->media_dev);
media_device_cleanup(&camif->media_dev);
@@ -521,7 +511,6 @@ static void s3c_camif_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
camif_clk_put(camif);
s3c_camif_unregister_subdev(camif);
- pdata->gpio_put();
}
static int s3c_camif_runtime_resume(struct device *dev)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index eeb0199864dd..aa79fe60ff0c 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -2024,8 +2024,10 @@ static int dcmi_probe(struct platform_device *pdev)
mdma_chan = dma_request_chan(&pdev->dev, "mdma_tx");
if (IS_ERR(mdma_chan)) {
ret = PTR_ERR(mdma_chan);
- if (ret != -ENODEV)
- return dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n");
+ if (ret != -ENODEV) {
+ dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n");
+ goto err_release_chan;
+ }
mdma_chan = NULL;
}
@@ -2050,6 +2052,7 @@ static int dcmi_probe(struct platform_device *pdev)
dcmi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
if (!dcmi->sram_pool) {
dev_info(&pdev->dev, "No SRAM pool, can't use MDMA chaining\n");
+ ret = -ENOMEM;
goto err_dma_slave_config;
}
@@ -2061,6 +2064,7 @@ static int dcmi_probe(struct platform_device *pdev)
&dcmi->sram_dma_buf);
if (!dcmi->sram_buf) {
dev_err(dcmi->dev, "Failed to allocate from SRAM\n");
+ ret = -ENOMEM;
goto err_dma_slave_config;
}
@@ -2206,12 +2210,13 @@ err_device_unregister:
err_media_device_cleanup:
media_device_cleanup(&dcmi->mdev);
err_mdma_slave_config:
- if (dcmi->mdma_chan)
+ if (mdma_chan)
gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size);
err_dma_slave_config:
- dma_release_channel(dcmi->dma_chan);
- if (dcmi->mdma_chan)
+ if (mdma_chan)
dma_release_channel(mdma_chan);
+err_release_chan:
+ dma_release_channel(chan);
return ret;
}
@@ -2273,9 +2278,7 @@ static int dcmi_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
/* clock enable */
- pm_runtime_force_resume(dev);
-
- return 0;
+ return pm_runtime_force_resume(dev);
}
static const struct dev_pm_ops dcmi_pm_ops = {
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 49398d077764..516334541b2c 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -649,9 +649,7 @@ static int dcmipp_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
/* clock enable */
- pm_runtime_force_resume(dev);
-
- return 0;
+ return pm_runtime_force_resume(dev);
}
static const struct dev_pm_ops dcmipp_pm_ops = {
diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig
index d0cb05481bd8..1a020b2bbb4f 100644
--- a/drivers/media/platform/ti/Kconfig
+++ b/drivers/media/platform/ti/Kconfig
@@ -50,6 +50,7 @@ config VIDEO_TI_VIP
select VIDEO_TI_VPDMA
select VIDEO_TI_SC
select VIDEO_TI_CSC
+ select V4L2_FWNODE
help
Driver support for VIP module on certain TI SoC's
VIP = Video Input Port.
diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
index e56a95f53ea9..ccb688f4d7af 100644
--- a/drivers/media/platform/ti/vpe/vip.c
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -815,6 +815,22 @@ static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num)
vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id);
}
+/*
+ * Quiesce recovery work and per-list IRQs before releasing stream resources.
+ * disable_work_sync() prevents the overflow handler from requeueing recovery
+ * work. Mask and synchronize IRQs afterwards because a running worker may
+ * have re-enabled them before exiting.
+ */
+static void vip_quiesce_stream(struct vip_stream *stream)
+{
+ struct vip_dev *dev = stream->port->dev;
+
+ disable_work_sync(&stream->recovery_work);
+ disable_irqs(dev, dev->slice_id, stream->list_num);
+ clear_irqs(dev, dev->slice_id, stream->list_num);
+ synchronize_irq(dev->irq);
+}
+
static void populate_desc_list(struct vip_stream *stream)
{
struct vip_port *port = stream->port;
@@ -2429,6 +2445,7 @@ static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
goto err;
stream->num_recovery = 0;
+ enable_work(&stream->recovery_work);
clear_irqs(dev, dev->slice_id, stream->list_num);
enable_irqs(dev, dev->slice_id, stream->list_num);
@@ -2453,13 +2470,17 @@ static void vip_stop_streaming(struct vb2_queue *vq)
struct vip_dev *dev = port->dev;
int ret;
+ /*
+ * A running recovery worker may re-enable the parser, so quiesce it
+ * and its IRQ handler before stopping the parser or releasing the
+ * descriptor list.
+ */
+ vip_quiesce_stream(stream);
+
vip_parser_stop_imm(port, true);
vip_enable_parser(port, false);
unset_fmt_params(stream);
- disable_irqs(dev, dev->slice_id, stream->list_num);
- clear_irqs(dev, dev->slice_id, stream->list_num);
-
if (port->subdev) {
ret = v4l2_subdev_call(port->subdev, video, s_stream, 0);
if (ret)
@@ -3075,6 +3096,8 @@ static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
goto do_free_hwlist;
INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work);
+ /* Start disabled; vip_start_streaming() enables it before IRQs. */
+ disable_work(&stream->recovery_work);
INIT_LIST_HEAD(&stream->vidq);
@@ -3140,6 +3163,13 @@ static void free_stream(struct vip_stream *stream)
return;
dev = stream->port->dev;
+ /*
+ * Quiesce the IRQ handler and recovery worker, then drop the stream
+ * from cap_streams[], before releasing stream-owned resources.
+ */
+ vip_quiesce_stream(stream);
+ stream->port->cap_streams[stream->stream_id] = NULL;
+
/* Free up the Drop queue */
list_for_each_safe(pos, q, &stream->dropq) {
buf = list_entry(pos,
@@ -3151,7 +3181,6 @@ static void free_stream(struct vip_stream *stream)
video_unregister_device(stream->vfd);
vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
- stream->port->cap_streams[stream->stream_id] = NULL;
kfree(stream);
}
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 2e81877f640f..ad71c0402ef3 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -771,8 +771,10 @@ static int hantro_register_entity(struct media_device *mdev,
return ret;
ret = media_device_register_entity(mdev, entity);
- if (ret)
+ if (ret) {
+ media_entity_cleanup(entity);
return ret;
+ }
return 0;
}
@@ -860,12 +862,13 @@ err_rm_links0:
err_rel_entity2:
media_device_unregister_entity(&func->sink);
-
+ media_entity_cleanup(&func->sink);
err_rel_entity1:
media_device_unregister_entity(&func->proc);
-
+ media_entity_cleanup(&func->proc);
err_rel_entity0:
media_device_unregister_entity(&func->vdev.entity);
+ media_entity_cleanup(&func->vdev.entity);
return ret;
}
@@ -878,6 +881,9 @@ static void hantro_detach_func(struct hantro_func *func)
media_device_unregister_entity(&func->sink);
media_device_unregister_entity(&func->proc);
media_device_unregister_entity(&func->vdev.entity);
+ media_entity_cleanup(&func->sink);
+ media_entity_cleanup(&func->proc);
+ media_entity_cleanup(&func->vdev.entity);
}
static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid)