summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/cec/core/cec-pin-error-inj.c59
-rw-r--r--drivers/media/cec/core/cec-pin-priv.h8
-rw-r--r--drivers/media/cec/core/cec-pin.c31
-rw-r--r--drivers/media/cec/platform/cec-gpio/cec-gpio.c58
-rw-r--r--drivers/media/cec/usb/rainshadow/rainshadow-cec.c3
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c6
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c8
-rw-r--r--drivers/media/i2c/Kconfig41
-rw-r--r--drivers/media/i2c/adv7180.c16
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c17
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c10
-rw-r--r--drivers/media/i2c/adv7604.c4
-rw-r--r--drivers/media/i2c/ds90ub913.c14
-rw-r--r--drivers/media/i2c/ds90ub953.c19
-rw-r--r--drivers/media/i2c/ds90ub960.c8
-rw-r--r--drivers/media/i2c/dw9714.c62
-rw-r--r--drivers/media/i2c/hi556.c73
-rw-r--r--drivers/media/i2c/imx214.c263
-rw-r--r--drivers/media/i2c/imx290.c1
-rw-r--r--drivers/media/i2c/imx415.c2
-rw-r--r--drivers/media/i2c/lt6911uxe.c2
-rw-r--r--drivers/media/i2c/max9286.c8
-rw-r--r--drivers/media/i2c/max96714.c7
-rw-r--r--drivers/media/i2c/max96717.c11
-rw-r--r--drivers/media/i2c/mt9m114.c171
-rw-r--r--drivers/media/i2c/ov2659.c3
-rw-r--r--drivers/media/i2c/ov2740.c18
-rw-r--r--drivers/media/i2c/ov5670.c9
-rw-r--r--drivers/media/i2c/ov5693.c7
-rw-r--r--drivers/media/i2c/ov7251.c7
-rw-r--r--drivers/media/i2c/ov8865.c3
-rw-r--r--drivers/media/i2c/saa7115.c12
-rw-r--r--drivers/media/i2c/tc358743.c138
-rw-r--r--drivers/media/i2c/tda1997x.c4
-rw-r--r--drivers/media/i2c/vd55g1.c32
-rw-r--r--drivers/media/pci/cx18/cx18-av-vbi.c12
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h12
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c2
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c2
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c13
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c82
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.h2
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c12
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h2
-rw-r--r--drivers/media/pci/intel/ivsc/mei_ace.c4
-rw-r--r--drivers/media/pci/intel/ivsc/mei_csi.c4
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c9
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c20
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c28
-rw-r--r--drivers/media/pci/saa7164/saa7164.h2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-core.c2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-gpio.c22
-rw-r--r--drivers/media/platform/amphion/vdec.c294
-rw-r--r--drivers/media/platform/amphion/vpu.h7
-rw-r--r--drivers/media/platform/amphion/vpu_color.c73
-rw-r--r--drivers/media/platform/amphion/vpu_dbg.c15
-rw-r--r--drivers/media/platform/amphion/vpu_defs.h12
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.c123
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.h12
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c5
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.c4
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.h1
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c11
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c131
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c47
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h1
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c68
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c135
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h6
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c18
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c169
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c5
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h1
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c39
-rw-r--r--drivers/media/platform/qcom/camss/camss.c107
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c35
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h3
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c35
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c48
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c37
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c143
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c56
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h6
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h28
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c198
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h126
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8250.c15
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c18
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c116
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.h11
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c36
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c397
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h46
-rw-r--r--drivers/media/platform/qcom/venus/core.c18
-rw-r--r--drivers/media/platform/qcom/venus/core.h2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c83
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c5
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c58
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c8
-rw-r--r--drivers/media/platform/qcom/venus/venc.c8
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Kconfig1
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be.c207
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c4
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c336
-rw-r--r--drivers/media/platform/renesas/rcar-fcp.c36
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c694
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c77
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c492
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h16
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c6
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h14
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c45
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c108
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c42
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c25
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c22
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.c633
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.h16
-rw-r--r--drivers/media/platform/rockchip/Kconfig1
-rw-r--r--drivers/media/platform/rockchip/Makefile1
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h2
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c150
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h99
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Kconfig16
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Makefile3
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c1212
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h223
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c1072
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c1251
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.h144
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c27
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c34
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c2
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c32
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.h3
-rw-r--r--drivers/media/platform/verisilicon/hantro.h2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_regs.h2
-rw-r--r--drivers/media/platform/verisilicon/hantro_h264.c6
-rw-r--r--drivers/media/platform/verisilicon/hantro_postproc.c6
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu_hw.c9
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c7
-rw-r--r--drivers/media/rc/ir-spi.c40
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.c3
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-gen.c8
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c4
-rw-r--r--drivers/media/usb/gspca/vicam.c10
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c30
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c4
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c43
-rw-r--r--drivers/media/usb/stk1160/stk1160.h7
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c4
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c131
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c74
-rw-r--r--drivers/media/usb/uvc/uvc_metadata.c122
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c199
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c366
-rw-r--r--drivers/media/usb/uvc/uvc_video.c82
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h46
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c40
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c20
-rw-r--r--drivers/media/v4l2-core/v4l2-i2c.c3
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c10
-rw-r--r--drivers/media/v4l2-core/v4l2-jpeg.c80
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c31
181 files changed, 9250 insertions, 3406 deletions
diff --git a/drivers/media/cec/core/cec-pin-error-inj.c b/drivers/media/cec/core/cec-pin-error-inj.c
index 6e61a04b8168..d9e613c7ce3f 100644
--- a/drivers/media/cec/core/cec-pin-error-inj.c
+++ b/drivers/media/cec/core/cec-pin-error-inj.c
@@ -91,16 +91,22 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
if (!strcmp(token, "clear")) {
memset(pin->error_inj, 0, sizeof(pin->error_inj));
pin->rx_toggle = pin->tx_toggle = false;
+ pin->rx_no_low_drive = false;
pin->tx_ignore_nack_until_eom = false;
pin->tx_custom_pulse = false;
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_falling_edge = false;
+ pin->tx_glitch_rising_edge = false;
return true;
}
if (!strcmp(token, "rx-clear")) {
for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
pin->rx_toggle = false;
+ pin->rx_no_low_drive = false;
return true;
}
if (!strcmp(token, "tx-clear")) {
@@ -111,6 +117,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
pin->tx_custom_pulse = false;
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_falling_edge = false;
+ pin->tx_glitch_rising_edge = false;
+ return true;
+ }
+ if (!strcmp(token, "rx-no-low-drive")) {
+ pin->rx_no_low_drive = true;
return true;
}
if (!strcmp(token, "tx-ignore-nack-until-eom")) {
@@ -122,6 +136,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
cec_pin_start_timer(pin);
return true;
}
+ if (!strcmp(token, "tx-glitch-falling-edge")) {
+ pin->tx_glitch_falling_edge = true;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-rising-edge")) {
+ pin->tx_glitch_rising_edge = true;
+ return true;
+ }
if (!p)
return false;
@@ -139,7 +161,23 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
return false;
- pin->tx_custom_high_usecs = usecs;
+ pin->tx_glitch_high_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-low-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 100)
+ return false;
+ pin->tx_glitch_low_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-high-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 100)
+ return false;
+ pin->tx_glitch_high_usecs = usecs;
return true;
}
@@ -273,6 +311,9 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n");
seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n");
seq_puts(sf, "#\n");
+ seq_puts(sf, "# RX error injection settings:\n");
+ seq_puts(sf, "# rx-no-low-drive do not generate low-drive pulses\n");
+ seq_puts(sf, "#\n");
seq_puts(sf, "# RX error injection:\n");
seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n");
seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n");
@@ -285,6 +326,10 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
+ seq_puts(sf, "# tx-glitch-low-usecs <usecs> define the 'low' time for the glitch pulse\n");
+ seq_puts(sf, "# tx-glitch-high-usecs <usecs> define the 'high' time for the glitch pulse\n");
+ seq_puts(sf, "# tx-glitch-falling-edge send the glitch pulse after every falling edge\n");
+ seq_puts(sf, "# tx-glitch-rising-edge send the glitch pulse after every rising edge\n");
seq_puts(sf, "#\n");
seq_puts(sf, "# TX error injection:\n");
seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
@@ -332,8 +377,14 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
}
}
+ if (pin->rx_no_low_drive)
+ seq_puts(sf, "rx-no-low-drive\n");
if (pin->tx_ignore_nack_until_eom)
seq_puts(sf, "tx-ignore-nack-until-eom\n");
+ if (pin->tx_glitch_falling_edge)
+ seq_puts(sf, "tx-glitch-falling-edge\n");
+ if (pin->tx_glitch_rising_edge)
+ seq_puts(sf, "tx-glitch-rising-edge\n");
if (pin->tx_custom_pulse)
seq_puts(sf, "tx-custom-pulse\n");
if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
@@ -342,5 +393,11 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
seq_printf(sf, "tx-custom-high-usecs %u\n",
pin->tx_custom_high_usecs);
+ if (pin->tx_glitch_low_usecs != CEC_TIM_GLITCH_DEFAULT)
+ seq_printf(sf, "tx-glitch-low-usecs %u\n",
+ pin->tx_glitch_low_usecs);
+ if (pin->tx_glitch_high_usecs != CEC_TIM_GLITCH_DEFAULT)
+ seq_printf(sf, "tx-glitch-high-usecs %u\n",
+ pin->tx_glitch_high_usecs);
return 0;
}
diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h
index 156a9f81be94..e7801be9adb9 100644
--- a/drivers/media/cec/core/cec-pin-priv.h
+++ b/drivers/media/cec/core/cec-pin-priv.h
@@ -164,6 +164,9 @@ enum cec_pin_state {
/* The default for the low/high time of the custom pulse */
#define CEC_TIM_CUSTOM_DEFAULT 1000
+/* The default for the low/high time of the glitch pulse */
+#define CEC_TIM_GLITCH_DEFAULT 1
+
#define CEC_NUM_PIN_EVENTS 128
#define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0)
#define CEC_PIN_EVENT_FL_DROPPED (1 << 1)
@@ -225,12 +228,17 @@ struct cec_pin {
u32 timer_max_overrun;
u32 timer_sum_overrun;
+ bool rx_no_low_drive;
u32 tx_custom_low_usecs;
u32 tx_custom_high_usecs;
+ u32 tx_glitch_low_usecs;
+ u32 tx_glitch_high_usecs;
bool tx_ignore_nack_until_eom;
bool tx_custom_pulse;
bool tx_generated_poll;
bool tx_post_eom;
+ bool tx_glitch_falling_edge;
+ bool tx_glitch_rising_edge;
u8 tx_extra_bytes;
u32 tx_low_drive_cnt;
#ifdef CONFIG_CEC_PIN_ERROR_INJ
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c
index 59ac12113f3a..4d7155281daa 100644
--- a/drivers/media/cec/core/cec-pin.c
+++ b/drivers/media/cec/core/cec-pin.c
@@ -142,15 +142,42 @@ static bool cec_pin_read(struct cec_pin *pin)
return v;
}
+static void cec_pin_insert_glitch(struct cec_pin *pin, bool rising_edge)
+{
+ /*
+ * Insert a short glitch after the falling or rising edge to
+ * simulate reflections on the CEC line. This can be used to
+ * test deglitch filters, which should be present in CEC devices
+ * to deal with noise on the line.
+ */
+ if (!pin->tx_glitch_high_usecs || !pin->tx_glitch_low_usecs)
+ return;
+ if (rising_edge) {
+ udelay(pin->tx_glitch_high_usecs);
+ call_void_pin_op(pin, low);
+ udelay(pin->tx_glitch_low_usecs);
+ call_void_pin_op(pin, high);
+ } else {
+ udelay(pin->tx_glitch_low_usecs);
+ call_void_pin_op(pin, high);
+ udelay(pin->tx_glitch_high_usecs);
+ call_void_pin_op(pin, low);
+ }
+}
+
static void cec_pin_low(struct cec_pin *pin)
{
call_void_pin_op(pin, low);
+ if (pin->tx_glitch_falling_edge && pin->adap->cec_pin_is_high)
+ cec_pin_insert_glitch(pin, false);
cec_pin_update(pin, false, false);
}
static bool cec_pin_high(struct cec_pin *pin)
{
call_void_pin_op(pin, high);
+ if (pin->tx_glitch_rising_edge && !pin->adap->cec_pin_is_high)
+ cec_pin_insert_glitch(pin, true);
return cec_pin_read(pin);
}
@@ -770,7 +797,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
* Go to low drive state when the total bit time is
* too short.
*/
- if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN) {
+ if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN && !pin->rx_no_low_drive) {
if (!pin->rx_data_bit_too_short_cnt++) {
pin->rx_data_bit_too_short_ts = ktime_to_ns(pin->ts);
pin->rx_data_bit_too_short_delta = delta;
@@ -1350,6 +1377,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
init_waitqueue_head(&pin->kthread_waitq);
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name,
caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN,
diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c
index 50cdc557c943..3c27789d8657 100644
--- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c
+++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c
@@ -61,49 +61,51 @@ static void cec_gpio_low(struct cec_adapter *adap)
gpiod_set_value(cec->cec_gpio, 0);
}
-static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv)
+static irqreturn_t cec_gpio_5v_irq_handler_thread(int irq, void *priv)
{
struct cec_gpio *cec = priv;
+ int val = gpiod_get_value_cansleep(cec->v5_gpio);
+ bool is_high = val > 0;
- cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
+ if (val < 0 || is_high == cec->v5_is_high)
+ return IRQ_HANDLED;
+
+ cec->v5_is_high = is_high;
+ cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
return IRQ_HANDLED;
}
-static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv)
+static irqreturn_t cec_gpio_5v_irq_handler(int irq, void *priv)
{
struct cec_gpio *cec = priv;
- int val = gpiod_get_value(cec->v5_gpio);
- bool is_high = val > 0;
- if (val < 0 || is_high == cec->v5_is_high)
- return IRQ_HANDLED;
cec->v5_ts = ktime_get();
- cec->v5_is_high = is_high;
return IRQ_WAKE_THREAD;
}
-static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv)
+static irqreturn_t cec_gpio_hpd_irq_handler_thread(int irq, void *priv)
{
struct cec_gpio *cec = priv;
+ int val = gpiod_get_value_cansleep(cec->hpd_gpio);
+ bool is_high = val > 0;
- cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
+ if (val < 0 || is_high == cec->hpd_is_high)
+ return IRQ_HANDLED;
+
+ cec->hpd_is_high = is_high;
+ cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
return IRQ_HANDLED;
}
-static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv)
+static irqreturn_t cec_gpio_hpd_irq_handler(int irq, void *priv)
{
struct cec_gpio *cec = priv;
- int val = gpiod_get_value(cec->hpd_gpio);
- bool is_high = val > 0;
- if (val < 0 || is_high == cec->hpd_is_high)
- return IRQ_HANDLED;
cec->hpd_ts = ktime_get();
- cec->hpd_is_high = is_high;
return IRQ_WAKE_THREAD;
}
-static irqreturn_t cec_gpio_irq_handler(int irq, void *priv)
+static irqreturn_t cec_gpio_cec_irq_handler(int irq, void *priv)
{
struct cec_gpio *cec = priv;
int val = gpiod_get_value(cec->cec_gpio);
@@ -113,7 +115,7 @@ static irqreturn_t cec_gpio_irq_handler(int irq, void *priv)
return IRQ_HANDLED;
}
-static bool cec_gpio_enable_irq(struct cec_adapter *adap)
+static bool cec_gpio_cec_enable_irq(struct cec_adapter *adap)
{
struct cec_gpio *cec = cec_get_drvdata(adap);
@@ -121,7 +123,7 @@ static bool cec_gpio_enable_irq(struct cec_adapter *adap)
return true;
}
-static void cec_gpio_disable_irq(struct cec_adapter *adap)
+static void cec_gpio_cec_disable_irq(struct cec_adapter *adap)
{
struct cec_gpio *cec = cec_get_drvdata(adap);
@@ -148,7 +150,7 @@ static int cec_gpio_read_hpd(struct cec_adapter *adap)
if (!cec->hpd_gpio)
return -ENOTTY;
- return gpiod_get_value(cec->hpd_gpio);
+ return gpiod_get_value_cansleep(cec->hpd_gpio);
}
static int cec_gpio_read_5v(struct cec_adapter *adap)
@@ -157,15 +159,15 @@ static int cec_gpio_read_5v(struct cec_adapter *adap)
if (!cec->v5_gpio)
return -ENOTTY;
- return gpiod_get_value(cec->v5_gpio);
+ return gpiod_get_value_cansleep(cec->v5_gpio);
}
static const struct cec_pin_ops cec_gpio_pin_ops = {
.read = cec_gpio_read,
.low = cec_gpio_low,
.high = cec_gpio_high,
- .enable_irq = cec_gpio_enable_irq,
- .disable_irq = cec_gpio_disable_irq,
+ .enable_irq = cec_gpio_cec_enable_irq,
+ .disable_irq = cec_gpio_cec_disable_irq,
.status = cec_gpio_status,
.read_hpd = cec_gpio_read_hpd,
.read_5v = cec_gpio_read_5v,
@@ -209,7 +211,7 @@ static int cec_gpio_probe(struct platform_device *pdev)
if (IS_ERR(cec->adap))
return PTR_ERR(cec->adap);
- ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler,
+ ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_cec_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
cec->adap->name, cec);
if (ret)
@@ -218,8 +220,8 @@ static int cec_gpio_probe(struct platform_device *pdev)
if (cec->hpd_gpio) {
cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio);
ret = devm_request_threaded_irq(dev, cec->hpd_irq,
- cec_hpd_gpio_irq_handler,
- cec_hpd_gpio_irq_handler_thread,
+ cec_gpio_hpd_irq_handler,
+ cec_gpio_hpd_irq_handler_thread,
IRQF_ONESHOT |
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"hpd-gpio", cec);
@@ -230,8 +232,8 @@ static int cec_gpio_probe(struct platform_device *pdev)
if (cec->v5_gpio) {
cec->v5_irq = gpiod_to_irq(cec->v5_gpio);
ret = devm_request_threaded_irq(dev, cec->v5_irq,
- cec_5v_gpio_irq_handler,
- cec_5v_gpio_irq_handler_thread,
+ cec_gpio_5v_irq_handler,
+ cec_gpio_5v_irq_handler_thread,
IRQF_ONESHOT |
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"v5-gpio", cec);
diff --git a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
index ee870ea1a886..6f8d6797c614 100644
--- a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
+++ b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
@@ -171,11 +171,12 @@ static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
{
struct rain *rain = serio_get_drvdata(serio);
+ spin_lock(&rain->buf_lock);
if (rain->buf_len == DATA_SIZE) {
+ spin_unlock(&rain->buf_lock);
dev_warn_once(rain->dev, "buffer overflow\n");
return IRQ_HANDLED;
}
- spin_lock(&rain->buf_lock);
rain->buf_len++;
rain->buf[rain->buf_wr_idx] = data;
rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
diff --git a/drivers/media/common/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c
index 1f1eaa807811..21edf870d927 100644
--- a/drivers/media/common/b2c2/flexcop-i2c.c
+++ b/drivers/media/common/b2c2/flexcop-i2c.c
@@ -209,7 +209,7 @@ static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm flexcop_algo = {
+static const struct i2c_algorithm flexcop_algo = {
.master_xfer = flexcop_master_xfer,
.functionality = flexcop_i2c_func,
};
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
index c3d8ced6c3ba..a31a8a6a4946 100644
--- a/drivers/media/dvb-frontends/cxd2820r_core.c
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -433,7 +433,7 @@ static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
return cxd2820r_gpio(&priv->fe, gpio);
}
-static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+static int cxd2820r_gpio_set(struct gpio_chip *chip, unsigned int nr, int val)
{
struct cxd2820r_priv *priv = gpiochip_get_data(chip);
struct i2c_client *client = priv->client[0];
@@ -446,7 +446,7 @@ static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
(void) cxd2820r_gpio(&priv->fe, gpio);
- return;
+ return 0;
}
static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr)
@@ -651,7 +651,7 @@ static int cxd2820r_probe(struct i2c_client *client)
priv->gpio_chip.parent = &client->dev;
priv->gpio_chip.owner = THIS_MODULE;
priv->gpio_chip.direction_output = cxd2820r_gpio_direction_output;
- priv->gpio_chip.set = cxd2820r_gpio_set;
+ priv->gpio_chip.set_rv = cxd2820r_gpio_set;
priv->gpio_chip.get = cxd2820r_gpio_get;
priv->gpio_chip.base = -1; /* Dynamic allocation */
priv->gpio_chip.ngpio = GPIO_COUNT;
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index b40daf242046..7d3a994b7cc4 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -2193,6 +2193,8 @@ static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_ms
struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
u8 n_overflow = 1;
u16 i = 1000;
+ if (msg[0].len < 3)
+ return -EOPNOTSUPP;
u16 serpar_num = msg[0].buf[0];
while (n_overflow == 1 && i) {
@@ -2212,6 +2214,8 @@ static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg
struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
u8 n_overflow = 1, n_empty = 1;
u16 i = 1000;
+ if (msg[0].len < 1 || msg[1].len < 2)
+ return -EOPNOTSUPP;
u16 serpar_num = msg[0].buf[0];
u16 read_word;
@@ -2256,8 +2260,12 @@ static int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap,
u16 word;
if (num == 1) { /* write */
+ if (msg[0].len < 3)
+ return -EOPNOTSUPP;
dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2])));
} else {
+ if (msg[1].len < 2)
+ return -EOPNOTSUPP;
word = dib7000p_read_word(state, apb_address);
msg[1].buf[0] = (word >> 8) & 0xff;
msg[1].buf[1] = (word) & 0xff;
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index e68202954a8f..6237fe804a5c 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -141,6 +141,7 @@ config VIDEO_IMX214
depends on GPIOLIB
select REGMAP_I2C
select V4L2_CCI_I2C
+ select VIDEO_CCS_PLL
help
This is a Video4Linux2 sensor driver for the Sony
IMX214 camera.
@@ -765,24 +766,25 @@ config VIDEO_THP7312
endmenu
-menu "Lens drivers"
- visible if MEDIA_CAMERA_SUPPORT
+menuconfig VIDEO_CAMERA_LENS
+ bool "Lens drivers"
+ depends on MEDIA_CAMERA_SUPPORT && I2C
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ default y
+
+if VIDEO_CAMERA_LENS
config VIDEO_AD5820
tristate "AD5820 lens voice coil support"
- depends on GPIOLIB && I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select V4L2_ASYNC
+ depends on GPIOLIB
help
This is a driver for the AD5820 camera lens voice coil.
It is used for example in Nokia N900 (RX-51).
config VIDEO_AK7375
tristate "AK7375 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
help
This is a driver for the AK7375 camera lens voice coil.
AK7375 is a 12 bit DAC with 120mA output current sink
@@ -791,10 +793,7 @@ config VIDEO_AK7375
config VIDEO_DW9714
tristate "DW9714 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
+ depends on GPIOLIB
help
This is a driver for the DW9714 camera lens voice coil.
DW9714 is a 10 bit DAC with 120mA output current sink
@@ -803,10 +802,6 @@ config VIDEO_DW9714
config VIDEO_DW9719
tristate "DW9719 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
select V4L2_CCI_I2C
help
This is a driver for the DW9719 camera lens voice coil.
@@ -815,10 +810,6 @@ config VIDEO_DW9719
config VIDEO_DW9768
tristate "DW9768 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_FWNODE
help
This is a driver for the DW9768 camera lens voice coil.
DW9768 is a 10 bit DAC with 100mA output current sink
@@ -827,17 +818,13 @@ config VIDEO_DW9768
config VIDEO_DW9807_VCM
tristate "DW9807 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
help
This is a driver for the DW9807 camera lens voice coil.
DW9807 is a 10 bit DAC with 100mA output current sink
capability. This is designed for linear control of
voice coil motors, controlled via I2C serial interface.
-endmenu
+endif
menu "Flash devices"
visible if MEDIA_CAMERA_SUPPORT
@@ -1684,7 +1671,7 @@ config VIDEO_MAX96714
config VIDEO_MAX96717
tristate "Maxim MAX96717 GMSL2 Serializer support"
- depends on OF && I2C && VIDEO_DEV && COMMON_CLK
+ depends on I2C && VIDEO_DEV && COMMON_CLK
select I2C_MUX
select MEDIA_CONTROLLER
select GPIOLIB
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 6e50b14f888f..5d90b8ab9b6d 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -868,21 +868,6 @@ static int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames)
return 0;
}
-static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect)
-{
- struct adv7180_state *state = to_state(sd);
-
- if (state->curr_norm & V4L2_STD_525_60) {
- aspect->numerator = 11;
- aspect->denominator = 10;
- } else {
- aspect->numerator = 54;
- aspect->denominator = 59;
- }
-
- return 0;
-}
-
static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
*norm = V4L2_STD_ALL;
@@ -929,7 +914,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
- .g_pixelaspect = adv7180_g_pixelaspect,
.g_tvnorms = adv7180_g_tvnorms,
.s_stream = adv7180_s_stream,
};
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index 5edb3295dc58..678199196b84 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -161,22 +161,6 @@ int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input)
return sdp_write(state, ADV748X_SDP_INSEL, input);
}
-static int adv748x_afe_g_pixelaspect(struct v4l2_subdev *sd,
- struct v4l2_fract *aspect)
-{
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
-
- if (afe->curr_norm & V4L2_STD_525_60) {
- aspect->numerator = 11;
- aspect->denominator = 10;
- } else {
- aspect->numerator = 54;
- aspect->denominator = 59;
- }
-
- return 0;
-}
-
/* -----------------------------------------------------------------------------
* v4l2_subdev_video_ops
*/
@@ -307,7 +291,6 @@ static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = {
.g_tvnorms = adv748x_afe_g_tvnorms,
.g_input_status = adv748x_afe_g_input_status,
.s_stream = adv748x_afe_s_stream,
- .g_pixelaspect = adv748x_afe_g_pixelaspect,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index a4db9bae5f79..b154dea29ba2 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -382,19 +382,9 @@ done:
return ret;
}
-static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd,
- struct v4l2_fract *aspect)
-{
- aspect->numerator = 1;
- aspect->denominator = 1;
-
- return 0;
-}
-
static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
.g_input_status = adv748x_hdmi_g_input_status,
.s_stream = adv748x_hdmi_s_stream,
- .g_pixelaspect = adv748x_hdmi_g_pixelaspect,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index e271782b7b70..afed38596362 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -2448,8 +2448,8 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
}
cec_s_phys_addr(state->cec_adap, parent_pa, false);
- /* enable hotplug after 100 ms */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
+ /* enable hotplug after 143 ms */
+ schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7);
return 0;
}
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index 6d3f8617ef13..bc74499b0a96 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -203,9 +203,9 @@ static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset,
0));
}
-static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+static int ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
- ub913_gpio_direction_out(gc, offset, value);
+ return ub913_gpio_direction_out(gc, offset, value);
}
static int ub913_gpio_of_xlate(struct gpio_chip *gc,
@@ -235,7 +235,7 @@ static int ub913_gpiochip_probe(struct ub913_data *priv)
gc->ngpio = UB913_NUM_GPIOS;
gc->get_direction = ub913_gpio_get_direction;
gc->direction_output = ub913_gpio_direction_out;
- gc->set = ub913_gpio_set;
+ gc->set_rv = ub913_gpio_set;
gc->of_xlate = ub913_gpio_of_xlate;
gc->of_gpio_n_cells = 2;
@@ -337,14 +337,6 @@ static int _ub913_set_routing(struct v4l2_subdev *sd,
unsigned int i;
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
-
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -EINVAL;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index 59bd92388845..a865bfc89500 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -317,14 +317,13 @@ static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset)
return !!(v & UB953_REG_GPIO_PIN_STS_GPIO_STS(offset));
}
-static void ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+static int ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct ub953_data *priv = gpiochip_get_data(gc);
- regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA,
- UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset),
- value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) :
- 0);
+ return regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA,
+ UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset),
+ value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : 0);
}
static int ub953_gpio_of_xlate(struct gpio_chip *gc,
@@ -362,7 +361,7 @@ static int ub953_gpiochip_probe(struct ub953_data *priv)
gc->direction_input = ub953_gpio_direction_in;
gc->direction_output = ub953_gpio_direction_out;
gc->get = ub953_gpio_get;
- gc->set = ub953_gpio_set;
+ gc->set_rv = ub953_gpio_set;
gc->of_xlate = ub953_gpio_of_xlate;
gc->of_gpio_n_cells = 2;
@@ -400,14 +399,6 @@ static int _ub953_set_routing(struct v4l2_subdev *sd,
};
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
-
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -EINVAL;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index 082fc62b0f5b..3156f6d6c6de 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -3861,14 +3861,6 @@ static int _ub960_set_routing(struct v4l2_subdev *sd,
};
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
-
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -E2BIG;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX);
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 2ddd7daa79e2..1e7ad355a388 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -2,6 +2,7 @@
// Copyright (c) 2015--2017 Intel Corporation.
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -38,6 +39,7 @@ struct dw9714_device {
struct v4l2_subdev sd;
u16 current_val;
struct regulator *vcc;
+ struct gpio_desc *powerdown_gpio;
};
static inline struct dw9714_device *to_dw9714_vcm(struct v4l2_ctrl *ctrl)
@@ -137,6 +139,28 @@ static int dw9714_init_controls(struct dw9714_device *dev_vcm)
return hdl->error;
}
+static int dw9714_power_up(struct dw9714_device *dw9714_dev)
+{
+ int ret;
+
+ ret = regulator_enable(dw9714_dev->vcc);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 0);
+
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static int dw9714_power_down(struct dw9714_device *dw9714_dev)
+{
+ gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 1);
+
+ return regulator_disable(dw9714_dev->vcc);
+}
+
static int dw9714_probe(struct i2c_client *client)
{
struct dw9714_device *dw9714_dev;
@@ -144,20 +168,25 @@ static int dw9714_probe(struct i2c_client *client)
dw9714_dev = devm_kzalloc(&client->dev, sizeof(*dw9714_dev),
GFP_KERNEL);
- if (dw9714_dev == NULL)
+ if (!dw9714_dev)
return -ENOMEM;
dw9714_dev->vcc = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(dw9714_dev->vcc))
return PTR_ERR(dw9714_dev->vcc);
- rval = regulator_enable(dw9714_dev->vcc);
- if (rval < 0) {
- dev_err(&client->dev, "failed to enable vcc: %d\n", rval);
- return rval;
- }
+ dw9714_dev->powerdown_gpio = devm_gpiod_get_optional(&client->dev,
+ "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(dw9714_dev->powerdown_gpio))
+ return dev_err_probe(&client->dev,
+ PTR_ERR(dw9714_dev->powerdown_gpio),
+ "could not get powerdown gpio\n");
- usleep_range(1000, 2000);
+ rval = dw9714_power_up(dw9714_dev);
+ if (rval)
+ return dev_err_probe(&client->dev, rval,
+ "failed to power up: %d\n", rval);
v4l2_i2c_subdev_init(&dw9714_dev->sd, client, &dw9714_ops);
dw9714_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
@@ -185,7 +214,7 @@ static int dw9714_probe(struct i2c_client *client)
return 0;
err_cleanup:
- regulator_disable(dw9714_dev->vcc);
+ dw9714_power_down(dw9714_dev);
v4l2_ctrl_handler_free(&dw9714_dev->ctrls_vcm);
media_entity_cleanup(&dw9714_dev->sd.entity);
@@ -200,10 +229,10 @@ static void dw9714_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev)) {
- ret = regulator_disable(dw9714_dev->vcc);
+ ret = dw9714_power_down(dw9714_dev);
if (ret) {
dev_err(&client->dev,
- "Failed to disable vcc: %d\n", ret);
+ "Failed to power down: %d\n", ret);
}
}
pm_runtime_set_suspended(&client->dev);
@@ -234,9 +263,9 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev)
usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10);
}
- ret = regulator_disable(dw9714_dev->vcc);
+ ret = dw9714_power_down(dw9714_dev);
if (ret)
- dev_err(dev, "Failed to disable vcc: %d\n", ret);
+ dev_err(dev, "Failed to power down: %d\n", ret);
return ret;
}
@@ -247,7 +276,7 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev)
* The lens position is gradually moved in units of DW9714_CTRL_STEPS,
* to make the movements smoothly.
*/
-static int __maybe_unused dw9714_vcm_resume(struct device *dev)
+static int __maybe_unused dw9714_vcm_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
@@ -257,12 +286,11 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev)
if (pm_runtime_suspended(&client->dev))
return 0;
- ret = regulator_enable(dw9714_dev->vcc);
+ ret = dw9714_power_up(dw9714_dev);
if (ret) {
- dev_err(dev, "Failed to enable vcc: %d\n", ret);
+ dev_err(dev, "Failed to power up: %d\n", ret);
return ret;
}
- usleep_range(1000, 2000);
for (val = dw9714_dev->current_val % DW9714_CTRL_STEPS;
val < dw9714_dev->current_val + DW9714_CTRL_STEPS - 1;
@@ -271,7 +299,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev)
DW9714_VAL(val, DW9714_DEFAULT_S));
if (ret)
dev_err_ratelimited(dev, "%s I2C failure: %d",
- __func__, ret);
+ __func__, ret);
usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10);
}
diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
index aed258211b8a..076c19fcf99c 100644
--- a/drivers/media/i2c/hi556.c
+++ b/drivers/media/i2c/hi556.c
@@ -624,6 +624,12 @@ static const struct hi556_mode supported_modes[] = {
},
};
+static const char * const hi556_supply_names[] = {
+ "dovdd", /* Digital I/O power */
+ "avdd", /* Analog power */
+ "dvdd", /* Digital core power */
+};
+
struct hi556 {
struct v4l2_subdev sd;
struct media_pad pad;
@@ -639,7 +645,7 @@ struct hi556 {
/* GPIOs, clocks, etc. */
struct gpio_desc *reset_gpio;
struct clk *clk;
- struct regulator *avdd;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(hi556_supply_names)];
/* Current mode */
const struct hi556_mode *cur_mode;
@@ -756,21 +762,23 @@ static int hi556_test_pattern(struct hi556 *hi556, u32 pattern)
int ret;
u32 val;
- if (pattern) {
- ret = hi556_read_reg(hi556, HI556_REG_ISP,
- HI556_REG_VALUE_08BIT, &val);
- if (ret)
- return ret;
+ ret = hi556_read_reg(hi556, HI556_REG_ISP,
+ HI556_REG_VALUE_08BIT, &val);
+ if (ret)
+ return ret;
- ret = hi556_write_reg(hi556, HI556_REG_ISP,
- HI556_REG_VALUE_08BIT,
- val | HI556_REG_ISP_TPG_EN);
- if (ret)
- return ret;
- }
+ val = pattern ? (val | HI556_REG_ISP_TPG_EN) :
+ (val & ~HI556_REG_ISP_TPG_EN);
+
+ ret = hi556_write_reg(hi556, HI556_REG_ISP,
+ HI556_REG_VALUE_08BIT, val);
+ if (ret)
+ return ret;
+
+ val = pattern ? BIT(pattern - 1) : 0;
return hi556_write_reg(hi556, HI556_REG_TEST_PATTERN,
- HI556_REG_VALUE_08BIT, pattern);
+ HI556_REG_VALUE_08BIT, val);
}
static int hi556_set_ctrl(struct v4l2_ctrl *ctrl)
@@ -1289,17 +1297,10 @@ static int hi556_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct hi556 *hi556 = to_hi556(sd);
- int ret;
gpiod_set_value_cansleep(hi556->reset_gpio, 1);
-
- ret = regulator_disable(hi556->avdd);
- if (ret) {
- dev_err(dev, "failed to disable avdd: %d\n", ret);
- gpiod_set_value_cansleep(hi556->reset_gpio, 0);
- return ret;
- }
-
+ regulator_bulk_disable(ARRAY_SIZE(hi556_supply_names),
+ hi556->supplies);
clk_disable_unprepare(hi556->clk);
return 0;
}
@@ -1314,14 +1315,20 @@ static int hi556_resume(struct device *dev)
if (ret)
return ret;
- ret = regulator_enable(hi556->avdd);
+ ret = regulator_bulk_enable(ARRAY_SIZE(hi556_supply_names),
+ hi556->supplies);
if (ret) {
- dev_err(dev, "failed to enable avdd: %d\n", ret);
+ dev_err(dev, "failed to enable regulators: %d", ret);
clk_disable_unprepare(hi556->clk);
return ret;
}
- gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ if (hi556->reset_gpio) {
+ /* Assert reset for at least 2ms on back to back off-on */
+ usleep_range(2000, 2200);
+ gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ }
+
usleep_range(5000, 5500);
return 0;
}
@@ -1330,7 +1337,7 @@ static int hi556_probe(struct i2c_client *client)
{
struct hi556 *hi556;
bool full_power;
- int ret;
+ int i, ret;
ret = hi556_check_hwcfg(&client->dev);
if (ret)
@@ -1353,11 +1360,15 @@ static int hi556_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
"failed to get clock\n");
- /* The regulator core will provide a "dummy" regulator if necessary */
- hi556->avdd = devm_regulator_get(&client->dev, "avdd");
- if (IS_ERR(hi556->avdd))
- return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
- "failed to get avdd regulator\n");
+ for (i = 0; i < ARRAY_SIZE(hi556_supply_names); i++)
+ hi556->supplies[i].supply = hi556_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(hi556_supply_names),
+ hi556->supplies);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get regulators\n");
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index dd7bc45523d8..a0cef9e61b41 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -20,6 +20,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "ccs-pll.h"
+
/* Chip ID */
#define IMX214_REG_CHIP_ID CCI_REG16(0x0016)
#define IMX214_CHIP_ID 0x0214
@@ -30,11 +32,9 @@
#define IMX214_REG_FAST_STANDBY_CTRL CCI_REG8(0x0106)
-#define IMX214_DEFAULT_CLK_FREQ 24000000
#define IMX214_DEFAULT_LINK_FREQ 600000000
/* Keep wrong link frequency for backward compatibility */
#define IMX214_DEFAULT_LINK_FREQ_LEGACY 480000000
-#define IMX214_DEFAULT_PIXEL_RATE ((IMX214_DEFAULT_LINK_FREQ * 8LL) / 10)
#define IMX214_FPS 30
/* V-TIMING internal */
@@ -84,6 +84,7 @@
#define IMX214_CSI_DATA_FORMAT_RAW10 0x0A0A
#define IMX214_CSI_DATA_FORMAT_COMP6 0x0A06
#define IMX214_CSI_DATA_FORMAT_COMP8 0x0A08
+#define IMX214_BITS_PER_PIXEL_MASK 0xFF
#define IMX214_REG_CSI_LANE_MODE CCI_REG8(0x0114)
#define IMX214_CSI_2_LANE_MODE 1
@@ -249,6 +250,10 @@ struct imx214 {
struct clk *xclk;
struct regmap *regmap;
+ struct ccs_pll pll;
+
+ struct v4l2_fwnode_endpoint bus_cfg;
+
struct v4l2_subdev sd;
struct media_pad pad;
@@ -299,16 +304,6 @@ static const struct cci_reg_sequence mode_4096x2304[] = {
{ IMX214_REG_DIG_CROP_WIDTH, 4096 },
{ IMX214_REG_DIG_CROP_HEIGHT, 2304 },
- { IMX214_REG_VTPXCK_DIV, 5 },
- { IMX214_REG_VTSYCK_DIV, 2 },
- { IMX214_REG_PREPLLCK_VT_DIV, 3 },
- { IMX214_REG_PLL_VT_MPY, 150 },
- { IMX214_REG_OPPXCK_DIV, 10 },
- { IMX214_REG_OPSYCK_DIV, 1 },
- { IMX214_REG_PLL_MULT_DRIV, IMX214_PLL_SINGLE },
-
- { IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(4800) },
-
{ CCI_REG8(0x3A03), 0x09 },
{ CCI_REG8(0x3A04), 0x50 },
{ CCI_REG8(0x3A05), 0x01 },
@@ -362,16 +357,6 @@ static const struct cci_reg_sequence mode_1920x1080[] = {
{ IMX214_REG_DIG_CROP_WIDTH, 1920 },
{ IMX214_REG_DIG_CROP_HEIGHT, 1080 },
- { IMX214_REG_VTPXCK_DIV, 5 },
- { IMX214_REG_VTSYCK_DIV, 2 },
- { IMX214_REG_PREPLLCK_VT_DIV, 3 },
- { IMX214_REG_PLL_VT_MPY, 150 },
- { IMX214_REG_OPPXCK_DIV, 10 },
- { IMX214_REG_OPSYCK_DIV, 1 },
- { IMX214_REG_PLL_MULT_DRIV, IMX214_PLL_SINGLE },
-
- { IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(4800) },
-
{ CCI_REG8(0x3A03), 0x04 },
{ CCI_REG8(0x3A04), 0xF8 },
{ CCI_REG8(0x3A05), 0x02 },
@@ -405,9 +390,6 @@ static const struct cci_reg_sequence mode_table_common[] = {
/* ATR setting */
{ IMX214_REG_ATR_FAST_MOVE, 2 },
- /* external clock setting */
- { IMX214_REG_EXCK_FREQ, IMX214_EXCK_FREQ(IMX214_DEFAULT_CLK_FREQ / 1000000) },
-
/* global setting */
/* basic config */
{ IMX214_REG_MASK_CORR_FRAMES, IMX214_CORR_FRAMES_MASK },
@@ -777,6 +759,30 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev,
return 0;
}
+static int imx214_configure_pll(struct imx214 *imx214)
+{
+ int ret = 0;
+
+ cci_write(imx214->regmap, IMX214_REG_VTPXCK_DIV,
+ imx214->pll.vt_bk.pix_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_VTSYCK_DIV,
+ imx214->pll.vt_bk.sys_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_PREPLLCK_VT_DIV,
+ imx214->pll.vt_fr.pre_pll_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_PLL_VT_MPY,
+ imx214->pll.vt_fr.pll_multiplier, &ret);
+ cci_write(imx214->regmap, IMX214_REG_OPPXCK_DIV,
+ imx214->pll.op_bk.pix_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_OPSYCK_DIV,
+ imx214->pll.op_bk.sys_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_PLL_MULT_DRIV,
+ IMX214_PLL_SINGLE, &ret);
+ cci_write(imx214->regmap, IMX214_REG_EXCK_FREQ,
+ IMX214_EXCK_FREQ(imx214->pll.ext_clk_freq_hz / 1000000), &ret);
+
+ return ret;
+}
+
static int imx214_update_digital_gain(struct imx214 *imx214, u32 val)
{
int ret = 0;
@@ -877,9 +883,6 @@ static const struct v4l2_ctrl_ops imx214_ctrl_ops = {
static int imx214_ctrls_init(struct imx214 *imx214)
{
- static const s64 link_freq[] = {
- IMX214_DEFAULT_LINK_FREQ
- };
static const struct v4l2_area unit_size = {
.width = 1120,
.height = 1120,
@@ -900,15 +903,14 @@ static int imx214_ctrls_init(struct imx214 *imx214)
if (ret)
return ret;
- imx214->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
- V4L2_CID_PIXEL_RATE, 0,
- IMX214_DEFAULT_PIXEL_RATE, 1,
- IMX214_DEFAULT_PIXEL_RATE);
+ imx214->pixel_rate =
+ v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_PIXEL_RATE, 1,
+ INT_MAX, 1, 1);
imx214->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL,
V4L2_CID_LINK_FREQ,
- ARRAY_SIZE(link_freq) - 1,
- 0, link_freq);
+ imx214->bus_cfg.nr_of_link_frequencies - 1,
+ 0, imx214->bus_cfg.link_frequencies);
if (imx214->link_freq)
imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -1011,6 +1013,7 @@ static int imx214_start_streaming(struct imx214 *imx214)
const struct v4l2_mbus_framefmt *fmt;
struct v4l2_subdev_state *state;
const struct imx214_mode *mode;
+ int bit_rate_mbps;
int ret;
ret = cci_multi_reg_write(imx214->regmap, mode_table_common,
@@ -1020,6 +1023,21 @@ static int imx214_start_streaming(struct imx214 *imx214)
return ret;
}
+ ret = imx214_configure_pll(imx214);
+ if (ret) {
+ dev_err(imx214->dev, "failed to configure PLL: %d\n", ret);
+ return ret;
+ }
+
+ bit_rate_mbps = (imx214->pll.pixel_rate_csi / 1000000)
+ * imx214->pll.bits_per_pixel;
+ ret = cci_write(imx214->regmap, IMX214_REG_REQ_LINK_BIT_RATE,
+ IMX214_LINK_BIT_RATE_MBPS(bit_rate_mbps), NULL);
+ if (ret) {
+ dev_err(imx214->dev, "failed to configure link bit rate\n");
+ return ret;
+ }
+
ret = cci_write(imx214->regmap, IMX214_REG_CSI_LANE_MODE,
IMX214_CSI_4_LANE_MODE, NULL);
if (ret) {
@@ -1097,6 +1115,109 @@ err_rpm_put:
return ret;
}
+static int imx214_pll_calculate(struct imx214 *imx214, struct ccs_pll *pll,
+ unsigned int link_freq)
+{
+ struct ccs_pll_limits limits = {
+ .min_ext_clk_freq_hz = 6000000,
+ .max_ext_clk_freq_hz = 27000000,
+
+ .vt_fr = {
+ .min_pre_pll_clk_div = 1,
+ .max_pre_pll_clk_div = 15,
+ /* Value is educated guess as we don't have a spec */
+ .min_pll_ip_clk_freq_hz = 6000000,
+ /* Value is educated guess as we don't have a spec */
+ .max_pll_ip_clk_freq_hz = 12000000,
+ .min_pll_multiplier = 12,
+ .max_pll_multiplier = 1200,
+ .min_pll_op_clk_freq_hz = 338000000,
+ .max_pll_op_clk_freq_hz = 1200000000,
+ },
+ .vt_bk = {
+ .min_sys_clk_div = 2,
+ .max_sys_clk_div = 4,
+ .min_pix_clk_div = 5,
+ .max_pix_clk_div = 10,
+ .min_pix_clk_freq_hz = 30000000,
+ .max_pix_clk_freq_hz = 120000000,
+ },
+ .op_bk = {
+ .min_sys_clk_div = 1,
+ .max_sys_clk_div = 2,
+ .min_pix_clk_div = 6,
+ .max_pix_clk_div = 10,
+ .min_pix_clk_freq_hz = 30000000,
+ .max_pix_clk_freq_hz = 120000000,
+ },
+
+ .min_line_length_pck_bin = IMX214_PPL_DEFAULT,
+ .min_line_length_pck = IMX214_PPL_DEFAULT,
+ };
+ unsigned int num_lanes = imx214->bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+ /*
+ * There are no documented constraints on the sys clock frequency, for
+ * either branch. Recover them based on the PLL output clock frequency
+ * and sys_clk_div limits on one hand, and the pix clock frequency and
+ * the pix_clk_div limits on the other hand.
+ */
+ limits.vt_bk.min_sys_clk_freq_hz =
+ max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.vt_bk.max_sys_clk_div,
+ limits.vt_bk.min_pix_clk_freq_hz * limits.vt_bk.min_pix_clk_div);
+ limits.vt_bk.max_sys_clk_freq_hz =
+ min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.vt_bk.min_sys_clk_div,
+ limits.vt_bk.max_pix_clk_freq_hz * limits.vt_bk.max_pix_clk_div);
+
+ limits.op_bk.min_sys_clk_freq_hz =
+ max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.op_bk.max_sys_clk_div,
+ limits.op_bk.min_pix_clk_freq_hz * limits.op_bk.min_pix_clk_div);
+ limits.op_bk.max_sys_clk_freq_hz =
+ min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.op_bk.min_sys_clk_div,
+ limits.op_bk.max_pix_clk_freq_hz * limits.op_bk.max_pix_clk_div);
+
+ memset(pll, 0, sizeof(*pll));
+
+ pll->bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
+ pll->op_lanes = num_lanes;
+ pll->vt_lanes = num_lanes;
+ pll->csi2.lanes = num_lanes;
+
+ pll->binning_horizontal = 1;
+ pll->binning_vertical = 1;
+ pll->scale_m = 1;
+ pll->scale_n = 1;
+ pll->bits_per_pixel =
+ IMX214_CSI_DATA_FORMAT_RAW10 & IMX214_BITS_PER_PIXEL_MASK;
+ pll->flags = CCS_PLL_FLAG_LANE_SPEED_MODEL;
+ pll->link_freq = link_freq;
+ pll->ext_clk_freq_hz = clk_get_rate(imx214->xclk);
+
+ return ccs_pll_calculate(imx214->dev, &limits, pll);
+}
+
+static int imx214_pll_update(struct imx214 *imx214)
+{
+ u64 link_freq;
+ int ret;
+
+ link_freq = imx214->bus_cfg.link_frequencies[imx214->link_freq->val];
+ ret = imx214_pll_calculate(imx214, &imx214->pll, link_freq);
+ if (ret) {
+ dev_err(imx214->dev, "PLL calculations failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_ctrl_s_ctrl_int64(imx214->pixel_rate,
+ imx214->pll.pixel_rate_pixel_array);
+ if (ret) {
+ dev_err(imx214->dev, "failed to set pixel rate\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int imx214_get_frame_interval(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_interval *fival)
@@ -1203,12 +1324,10 @@ static int imx214_identify_module(struct imx214 *imx214)
return 0;
}
-static int imx214_parse_fwnode(struct device *dev)
+static int imx214_parse_fwnode(struct device *dev, struct imx214 *imx214)
{
+ struct v4l2_fwnode_endpoint *bus_cfg = &imx214->bus_cfg;
struct fwnode_handle *endpoint;
- struct v4l2_fwnode_endpoint bus_cfg = {
- .bus_type = V4L2_MBUS_CSI2_DPHY,
- };
unsigned int i;
int ret;
@@ -1216,42 +1335,52 @@ static int imx214_parse_fwnode(struct device *dev)
if (!endpoint)
return dev_err_probe(dev, -EINVAL, "endpoint node not found\n");
- ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ bus_cfg->bus_type = V4L2_MBUS_CSI2_DPHY;
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, bus_cfg);
+ fwnode_handle_put(endpoint);
if (ret) {
dev_err_probe(dev, ret, "parsing endpoint node failed\n");
- goto done;
+ goto error;
}
/* Check the number of MIPI CSI2 data lanes */
- if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+ if (bus_cfg->bus.mipi_csi2.num_data_lanes != 4) {
ret = dev_err_probe(dev, -EINVAL,
"only 4 data lanes are currently supported\n");
- goto done;
+ goto error;
}
- if (bus_cfg.nr_of_link_frequencies != 1)
+ if (bus_cfg->nr_of_link_frequencies != 1)
dev_warn(dev, "Only one link-frequency supported, please review your DT. Continuing anyway\n");
- for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
- if (bus_cfg.link_frequencies[i] == IMX214_DEFAULT_LINK_FREQ)
+ for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) {
+ u64 freq = bus_cfg->link_frequencies[i];
+ struct ccs_pll pll;
+
+ if (!imx214_pll_calculate(imx214, &pll, freq))
break;
- if (bus_cfg.link_frequencies[i] ==
- IMX214_DEFAULT_LINK_FREQ_LEGACY) {
+ if (freq == IMX214_DEFAULT_LINK_FREQ_LEGACY) {
dev_warn(dev,
"link-frequencies %d not supported, please review your DT. Continuing anyway\n",
IMX214_DEFAULT_LINK_FREQ);
+ freq = IMX214_DEFAULT_LINK_FREQ;
+ if (imx214_pll_calculate(imx214, &pll, freq))
+ continue;
+ bus_cfg->link_frequencies[i] = freq;
break;
}
}
- if (i == bus_cfg.nr_of_link_frequencies)
+ if (i == bus_cfg->nr_of_link_frequencies)
ret = dev_err_probe(dev, -EINVAL,
- "link-frequencies %d not supported, please review your DT\n",
- IMX214_DEFAULT_LINK_FREQ);
+ "link-frequencies %lld not supported, please review your DT\n",
+ bus_cfg->nr_of_link_frequencies ?
+ bus_cfg->link_frequencies[0] : 0);
-done:
- v4l2_fwnode_endpoint_free(&bus_cfg);
- fwnode_handle_put(endpoint);
+ return 0;
+
+error:
+ v4l2_fwnode_endpoint_free(&imx214->bus_cfg);
return ret;
}
@@ -1261,10 +1390,6 @@ static int imx214_probe(struct i2c_client *client)
struct imx214 *imx214;
int ret;
- ret = imx214_parse_fwnode(dev);
- if (ret)
- return ret;
-
imx214 = devm_kzalloc(dev, sizeof(*imx214), GFP_KERNEL);
if (!imx214)
return -ENOMEM;
@@ -1276,11 +1401,6 @@ static int imx214_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(imx214->xclk),
"failed to get xclk\n");
- ret = clk_set_rate(imx214->xclk, IMX214_DEFAULT_CLK_FREQ);
- if (ret)
- return dev_err_probe(dev, ret,
- "failed to set xclk frequency\n");
-
ret = imx214_get_regulators(dev, imx214);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get regulators\n");
@@ -1295,6 +1415,10 @@ static int imx214_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(imx214->regmap),
"failed to initialize CCI\n");
+ ret = imx214_parse_fwnode(dev, imx214);
+ if (ret)
+ return ret;
+
v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops);
imx214->sd.internal_ops = &imx214_internal_ops;
@@ -1302,7 +1426,9 @@ static int imx214_probe(struct i2c_client *client)
* Enable power initially, to avoid warnings
* from clk_disable on power_off
*/
- imx214_power_on(imx214->dev);
+ ret = imx214_power_on(imx214->dev);
+ if (ret < 0)
+ goto error_fwnode;
ret = imx214_identify_module(imx214);
if (ret)
@@ -1333,6 +1459,12 @@ static int imx214_probe(struct i2c_client *client)
pm_runtime_set_active(imx214->dev);
pm_runtime_enable(imx214->dev);
+ ret = imx214_pll_update(imx214);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "failed to update PLL\n");
+ goto error_subdev_cleanup;
+ }
+
ret = v4l2_async_register_subdev_sensor(&imx214->sd);
if (ret < 0) {
dev_err_probe(dev, ret,
@@ -1358,6 +1490,9 @@ free_ctrl:
error_power_off:
imx214_power_off(imx214->dev);
+error_fwnode:
+ v4l2_fwnode_endpoint_free(&imx214->bus_cfg);
+
return ret;
}
@@ -1370,6 +1505,8 @@ static void imx214_remove(struct i2c_client *client)
v4l2_subdev_cleanup(sd);
media_entity_cleanup(&imx214->sd.entity);
v4l2_ctrl_handler_free(&imx214->ctrls);
+ v4l2_fwnode_endpoint_free(&imx214->bus_cfg);
+
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev)) {
imx214_power_off(imx214->dev);
diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index fbf7eba3d71d..4f3f386c5353 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1294,7 +1294,6 @@ static int imx290_subdev_init(struct imx290 *imx290)
* will already be prevented even before the delay.
*/
v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
- imx290->sd.dev = imx290->dev;
pm_runtime_mark_last_busy(imx290->dev);
pm_runtime_put_autosuspend(imx290->dev);
diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c
index 9f37779bd611..278e743646ea 100644
--- a/drivers/media/i2c/imx415.c
+++ b/drivers/media/i2c/imx415.c
@@ -1251,7 +1251,7 @@ static int imx415_parse_hw_config(struct imx415 *sensor)
return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset),
"failed to get reset GPIO\n");
- sensor->clk = devm_clk_get(sensor->dev, "inck");
+ sensor->clk = devm_clk_get(sensor->dev, NULL);
if (IS_ERR(sensor->clk))
return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk),
"failed to get clock\n");
diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c
index 24857d683fcf..bdefdd157e69 100644
--- a/drivers/media/i2c/lt6911uxe.c
+++ b/drivers/media/i2c/lt6911uxe.c
@@ -600,7 +600,7 @@ static int lt6911uxe_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&lt6911uxe->sd, client, &lt6911uxe_subdev_ops);
- lt6911uxe->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
+ lt6911uxe->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(lt6911uxe->reset_gpio))
return dev_err_probe(dev, PTR_ERR(lt6911uxe->reset_gpio),
"failed to get reset gpio\n");
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 9fc4e130a273..1d0b5f56f989 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1193,12 +1193,12 @@ static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset,
MAX9286_0X0F_RESERVED | priv->gpio_state);
}
-static void max9286_gpiochip_set(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int max9286_gpiochip_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
{
struct max9286_priv *priv = gpiochip_get_data(chip);
- max9286_gpio_set(priv, offset, value);
+ return max9286_gpio_set(priv, offset, value);
}
static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset)
@@ -1220,7 +1220,7 @@ static int max9286_register_gpio(struct max9286_priv *priv)
gpio->owner = THIS_MODULE;
gpio->ngpio = 2;
gpio->base = -1;
- gpio->set = max9286_gpiochip_set;
+ gpio->set_rv = max9286_gpiochip_set;
gpio->get = max9286_gpiochip_get;
gpio->can_sleep = true;
diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c
index 3cc1b1ae47d1..e3e625e6f11a 100644
--- a/drivers/media/i2c/max96714.c
+++ b/drivers/media/i2c/max96714.c
@@ -370,13 +370,6 @@ static int _max96714_set_routing(struct v4l2_subdev *sd,
};
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -EINVAL;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
index 3746729366ac..015e42fbe246 100644
--- a/drivers/media/i2c/max96717.c
+++ b/drivers/media/i2c/max96717.c
@@ -297,13 +297,13 @@ static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
return !!(val & MAX96717_GPIO_OUT);
}
-static void max96717_gpiochip_set(struct gpio_chip *gpiochip,
- unsigned int offset, int value)
+static int max96717_gpiochip_set(struct gpio_chip *gpiochip,
+ unsigned int offset, int value)
{
struct max96717_priv *priv = gpiochip_get_data(gpiochip);
- cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset),
- MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL);
+ return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset),
+ MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL);
}
static int max96717_gpio_get_direction(struct gpio_chip *gpiochip,
@@ -355,9 +355,8 @@ static int max96717_gpiochip_probe(struct max96717_priv *priv)
gc->get_direction = max96717_gpio_get_direction;
gc->direction_input = max96717_gpio_direction_in;
gc->direction_output = max96717_gpio_direction_out;
- gc->set = max96717_gpiochip_set;
+ gc->set_rv = max96717_gpiochip_set;
gc->get = max96717_gpiochip_get;
- gc->of_gpio_n_cells = 2;
/* Disable GPIO forwarding */
for (i = 0; i < gc->ngpio; i++)
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
index 5f0b0ad8f885..3f540ca40f3c 100644
--- a/drivers/media/i2c/mt9m114.c
+++ b/drivers/media/i2c/mt9m114.c
@@ -261,6 +261,7 @@
#define MT9M114_CAM_PGA_PGA_CONTROL CCI_REG16(0xc95e)
#define MT9M114_CAM_SYSCTL_PLL_ENABLE CCI_REG8(0xc97e)
#define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0)
+#define MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE 0x00
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980)
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m))
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982)
@@ -377,6 +378,7 @@ struct mt9m114 {
struct gpio_desc *reset;
struct regulator_bulk_data supplies[3];
struct v4l2_fwnode_endpoint bus_cfg;
+ bool bypass_pll;
struct {
unsigned int m;
@@ -743,14 +745,21 @@ static int mt9m114_initialize(struct mt9m114 *sensor)
}
/* Configure the PLL. */
- cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE,
- MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret);
- cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N,
- MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m,
- sensor->pll.n),
- &ret);
- cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P,
- MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), &ret);
+ if (sensor->bypass_pll) {
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE,
+ MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE, &ret);
+ } else {
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE,
+ MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret);
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N,
+ MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m,
+ sensor->pll.n),
+ &ret);
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P,
+ MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p),
+ &ret);
+ }
+
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_PIXCLK,
sensor->pixrate, &ret);
@@ -781,41 +790,25 @@ static int mt9m114_initialize(struct mt9m114 *sensor)
return 0;
}
-static int mt9m114_configure(struct mt9m114 *sensor,
- struct v4l2_subdev_state *pa_state,
- struct v4l2_subdev_state *ifp_state)
+static int mt9m114_configure_pa(struct mt9m114 *sensor,
+ struct v4l2_subdev_state *state)
{
- const struct v4l2_mbus_framefmt *pa_format;
- const struct v4l2_rect *pa_crop;
- const struct mt9m114_format_info *ifp_info;
- const struct v4l2_mbus_framefmt *ifp_format;
- const struct v4l2_rect *ifp_crop;
- const struct v4l2_rect *ifp_compose;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
unsigned int hratio, vratio;
- u64 output_format;
u64 read_mode;
- int ret = 0;
-
- pa_format = v4l2_subdev_state_get_format(pa_state, 0);
- pa_crop = v4l2_subdev_state_get_crop(pa_state, 0);
+ int ret;
- ifp_format = v4l2_subdev_state_get_format(ifp_state, 1);
- ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code);
- ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0);
- ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0);
+ format = v4l2_subdev_state_get_format(state, 0);
+ crop = v4l2_subdev_state_get_crop(state, 0);
ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE,
&read_mode, NULL);
if (ret < 0)
return ret;
- ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT,
- &output_format, NULL);
- if (ret < 0)
- return ret;
-
- hratio = pa_crop->width / pa_format->width;
- vratio = pa_crop->height / pa_format->height;
+ hratio = crop->width / format->width;
+ vratio = crop->height / format->height;
/*
* Pixel array crop and binning. The CAM_SENSOR_CFG_CPIPE_LAST_ROW
@@ -824,15 +817,15 @@ static int mt9m114_configure(struct mt9m114 *sensor,
* example sensor modes.
*/
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_START,
- pa_crop->left, &ret);
+ crop->left, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_START,
- pa_crop->top, &ret);
+ crop->top, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_END,
- pa_crop->width + pa_crop->left - 1, &ret);
+ crop->width + crop->left - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_END,
- pa_crop->height + pa_crop->top - 1, &ret);
+ crop->height + crop->top - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW,
- (pa_crop->height - 4) / vratio - 1, &ret);
+ (crop->height - 4) / vratio - 1, &ret);
read_mode &= ~(MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK |
MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK);
@@ -845,6 +838,29 @@ static int mt9m114_configure(struct mt9m114 *sensor,
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE,
read_mode, &ret);
+ return ret;
+}
+
+static int mt9m114_configure_ifp(struct mt9m114 *sensor,
+ struct v4l2_subdev_state *state)
+{
+ const struct mt9m114_format_info *info;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+ const struct v4l2_rect *compose;
+ u64 output_format;
+ int ret = 0;
+
+ format = v4l2_subdev_state_get_format(state, 1);
+ info = mt9m114_format_info(sensor, 1, format->code);
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ compose = v4l2_subdev_state_get_compose(state, 0);
+
+ ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT,
+ &output_format, NULL);
+ if (ret < 0)
+ return ret;
+
/*
* Color pipeline (IFP) cropping and scaling. Subtract 4 from the left
* and top coordinates to compensate for the lines and columns removed
@@ -852,18 +868,18 @@ static int mt9m114_configure(struct mt9m114 *sensor,
* not in the hardware.
*/
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET,
- ifp_crop->left - 4, &ret);
+ crop->left - 4, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET,
- ifp_crop->top - 4, &ret);
+ crop->top - 4, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH,
- ifp_crop->width, &ret);
+ crop->width, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT,
- ifp_crop->height, &ret);
+ crop->height, &ret);
cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_WIDTH,
- ifp_compose->width, &ret);
+ compose->width, &ret);
cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_HEIGHT,
- ifp_compose->height, &ret);
+ compose->height, &ret);
/* AWB and AE windows, use the full frame. */
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART,
@@ -871,18 +887,18 @@ static int mt9m114_configure(struct mt9m114 *sensor,
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART,
0, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND,
- ifp_compose->width - 1, &ret);
+ compose->width - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND,
- ifp_compose->height - 1, &ret);
+ compose->height - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART,
0, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART,
0, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND,
- ifp_compose->width / 5 - 1, &ret);
+ compose->width / 5 - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND,
- ifp_compose->height / 5 - 1, &ret);
+ compose->height / 5 - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_CROPMODE,
MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN |
@@ -894,7 +910,7 @@ static int mt9m114_configure(struct mt9m114 *sensor,
MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK |
MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES |
MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE);
- output_format |= ifp_info->output_format;
+ output_format |= info->output_format;
cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT,
output_format, &ret);
@@ -925,7 +941,11 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor,
if (ret)
return ret;
- ret = mt9m114_configure(sensor, pa_state, ifp_state);
+ ret = mt9m114_configure_ifp(sensor, ifp_state);
+ if (ret)
+ goto error;
+
+ ret = mt9m114_configure_pa(sensor, pa_state);
if (ret)
goto error;
@@ -1599,13 +1619,9 @@ static int mt9m114_ifp_get_frame_interval(struct v4l2_subdev *sd,
if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
return -EINVAL;
- mutex_lock(sensor->ifp.hdl.lock);
-
ival->numerator = 1;
ival->denominator = sensor->ifp.frame_rate;
- mutex_unlock(sensor->ifp.hdl.lock);
-
return 0;
}
@@ -1624,8 +1640,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd,
if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
return -EINVAL;
- mutex_lock(sensor->ifp.hdl.lock);
-
if (ival->numerator != 0 && ival->denominator != 0)
sensor->ifp.frame_rate = min_t(unsigned int,
ival->denominator / ival->numerator,
@@ -1639,8 +1653,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd,
if (sensor->streaming)
ret = mt9m114_set_frame_rate(sensor);
- mutex_unlock(sensor->ifp.hdl.lock);
-
return ret;
}
@@ -2235,9 +2247,22 @@ static const struct dev_pm_ops mt9m114_pm_ops = {
* Probe & Remove
*/
+static int mt9m114_verify_link_frequency(struct mt9m114 *sensor,
+ unsigned int pixrate)
+{
+ unsigned int link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY
+ ? pixrate * 8 : pixrate * 2;
+
+ if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
+ sensor->bus_cfg.link_frequencies[0] != link_freq)
+ return -EINVAL;
+
+ return 0;
+}
+
static int mt9m114_clk_init(struct mt9m114 *sensor)
{
- unsigned int link_freq;
+ unsigned int pixrate;
/* Hardcode the PLL multiplier and dividers to default settings. */
sensor->pll.m = 32;
@@ -2249,19 +2274,29 @@ static int mt9m114_clk_init(struct mt9m114 *sensor)
* for 16-bit per pixel, transmitted in DDR over a single lane. For
* parallel mode, the sensor ouputs one pixel in two PIXCLK cycles.
*/
- sensor->pixrate = clk_get_rate(sensor->clk) * sensor->pll.m
- / ((sensor->pll.n + 1) * (sensor->pll.p + 1));
- link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY
- ? sensor->pixrate * 8 : sensor->pixrate * 2;
+ /*
+ * Check if EXTCLK fits the configured link frequency. Bypass the PLL
+ * in this case.
+ */
+ pixrate = clk_get_rate(sensor->clk) / 2;
+ if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) {
+ sensor->pixrate = pixrate;
+ sensor->bypass_pll = true;
+ return 0;
+ }
- if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
- sensor->bus_cfg.link_frequencies[0] != link_freq) {
- dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n");
- return -EINVAL;
+ /* Check if the PLL configuration fits the configured link frequency. */
+ pixrate = clk_get_rate(sensor->clk) * sensor->pll.m
+ / ((sensor->pll.n + 1) * (sensor->pll.p + 1));
+ if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) {
+ sensor->pixrate = pixrate;
+ sensor->bypass_pll = false;
+ return 0;
}
- return 0;
+ dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n");
+ return -EINVAL;
}
static int mt9m114_identify(struct mt9m114 *sensor)
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index 06b7896c3eaf..586b31ba076b 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -1469,14 +1469,15 @@ static int ov2659_probe(struct i2c_client *client)
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov2659_test_pattern_menu) - 1,
0, 0, ov2659_test_pattern_menu);
- ov2659->sd.ctrl_handler = &ov2659->ctrls;
if (ov2659->ctrls.error) {
dev_err(&client->dev, "%s: control initialization error %d\n",
__func__, ov2659->ctrls.error);
+ v4l2_ctrl_handler_free(&ov2659->ctrls);
return ov2659->ctrls.error;
}
+ ov2659->sd.ctrl_handler = &ov2659->ctrls;
sd = &ov2659->sd;
client->flags |= I2C_CLIENT_SCCB;
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 6cf461e3373c..4e959534e6e7 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -766,11 +766,9 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd);
struct v4l2_ctrl_handler *ctrl_hdlr;
- const struct ov2740_mode *cur_mode;
s64 exposure_max, h_blank, pixel_rate;
u32 vblank_min, vblank_max, vblank_default;
struct v4l2_fwnode_device_properties props;
- int size;
int ret;
ctrl_hdlr = &ov2740->ctrl_handler;
@@ -778,12 +776,10 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
if (ret)
return ret;
- cur_mode = ov2740->cur_mode;
- size = ARRAY_SIZE(link_freq_menu_items);
-
ov2740->link_freq =
v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
- V4L2_CID_LINK_FREQ, size - 1,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1,
ov2740->supported_modes->link_freq_index,
link_freq_menu_items);
if (ov2740->link_freq)
@@ -794,14 +790,14 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
V4L2_CID_PIXEL_RATE, 0,
pixel_rate, 1, pixel_rate);
- vblank_min = cur_mode->vts_min - cur_mode->height;
- vblank_max = cur_mode->vts_max - cur_mode->height;
- vblank_default = cur_mode->vts_def - cur_mode->height;
+ vblank_min = ov2740->cur_mode->vts_min - ov2740->cur_mode->height;
+ vblank_max = ov2740->cur_mode->vts_max - ov2740->cur_mode->height;
+ vblank_default = ov2740->cur_mode->vts_def - ov2740->cur_mode->height;
ov2740->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_VBLANK, vblank_min,
vblank_max, 1, vblank_default);
- h_blank = cur_mode->hts - cur_mode->width;
+ h_blank = ov2740->cur_mode->hts - ov2740->cur_mode->width;
ov2740->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_HBLANK, h_blank, h_blank, 1,
h_blank);
@@ -814,7 +810,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT);
- exposure_max = cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN;
+ exposure_max = ov2740->cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN;
ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_EXPOSURE,
OV2740_EXPOSURE_MIN, exposure_max,
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index c54bbc207189..b9efb2d2276a 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -2688,10 +2688,15 @@ static int ov5670_probe(struct i2c_client *client)
if (ret)
return dev_err_probe(&client->dev, ret, "GPIO probe failed\n");
- /* Graph Endpoint */
+ /*
+ * Graph Endpoint. If it's missing we defer rather than fail, as this
+ * sensor is known to co-exist on systems with the IPU3 and so it might
+ * be created by the ipu-bridge.
+ */
handle = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
if (!handle)
- return dev_err_probe(&client->dev, -ENXIO, "Endpoint for node get failed\n");
+ return dev_err_probe(&client->dev, -EPROBE_DEFER,
+ "Endpoint for node get failed\n");
ov5670->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
ov5670->endpoint.bus.mipi_csi2.num_data_lanes = 2;
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 46b9ce111676..485efd15257e 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1222,9 +1222,14 @@ static int ov5693_check_hwcfg(struct ov5693_device *ov5693)
unsigned int i;
int ret;
+ /*
+ * Sometimes the fwnode graph is initialized by the bridge driver
+ * Bridge drivers doing this may also add GPIO mappings, wait for this.
+ */
endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!endpoint)
- return -EPROBE_DEFER; /* Could be provided by cio2-bridge */
+ return dev_err_probe(ov5693->dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
fwnode_handle_put(endpoint);
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
index 3226888d77e9..31a42d81e970 100644
--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -1486,9 +1486,14 @@ static int ov7251_check_hwcfg(struct ov7251 *ov7251)
unsigned int i, j;
int ret;
+ /*
+ * Sometimes the fwnode graph is initialized by the bridge driver
+ * Bridge drivers doing this may also add GPIO mappings, wait for this.
+ */
endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!endpoint)
- return -EPROBE_DEFER; /* could be provided by cio2-bridge */
+ return dev_err_probe(ov7251->dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
fwnode_handle_put(endpoint);
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 95ffe7536aa6..a2138f7988aa 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -2991,7 +2991,8 @@ static int ov8865_probe(struct i2c_client *client)
handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
if (!handle)
- return -EPROBE_DEFER;
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index a1c71187e773..b8b8f206ec3a 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -25,6 +25,7 @@
#include "saa711x_regs.h"
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -664,15 +665,6 @@ static const unsigned char saa7115_init_misc[] = {
0x00, 0x00
};
-static int saa711x_odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
static int saa711x_decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
@@ -1227,7 +1219,7 @@ static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vb
vbi->type = V4L2_SLICED_TELETEXT_B;
break;
case 4:
- if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
+ if (!parity8(p[0]) || !parity8(p[1]))
return 0;
vbi->type = V4L2_SLICED_CAPTION_525;
break;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 3d6703b75bfa..1cc7636e446d 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -114,7 +114,7 @@ static inline struct tc358743_state *to_state(struct v4l2_subdev *sd)
/* --------------- I2C --------------- */
-static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
+static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
{
struct tc358743_state *state = to_state(sd);
struct i2c_client *client = state->i2c_client;
@@ -140,6 +140,7 @@ static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed: %d\n",
__func__, reg, client->addr, err);
}
+ return err != ARRAY_SIZE(msgs);
}
static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
@@ -196,15 +197,24 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
}
}
-static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n)
+static noinline u32 i2c_rdreg_err(struct v4l2_subdev *sd, u16 reg, u32 n,
+ int *err)
{
+ int error;
__le32 val = 0;
- i2c_rd(sd, reg, (u8 __force *)&val, n);
+ error = i2c_rd(sd, reg, (u8 __force *)&val, n);
+ if (err)
+ *err = error;
return le32_to_cpu(val);
}
+static inline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n)
+{
+ return i2c_rdreg_err(sd, reg, n, NULL);
+}
+
static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n)
{
__le32 raw = cpu_to_le32(val);
@@ -233,6 +243,13 @@ static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg)
return i2c_rdreg(sd, reg, 2);
}
+static int i2c_rd16_err(struct v4l2_subdev *sd, u16 reg, u16 *value)
+{
+ int err;
+ *value = i2c_rdreg_err(sd, reg, 2, &err);
+ return err;
+}
+
static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val)
{
i2c_wrreg(sd, reg, val, 2);
@@ -420,9 +437,9 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd)
v4l2_dbg(2, debug, sd, "%s:\n", __func__);
- /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when
+ /* Enable hotplug after 143 ms. DDC access to EDID is also enabled when
* hotplug is enabled. See register DDC_CTL */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
+ schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7);
tc358743_enable_interrupts(sd, true);
tc358743_s_ctrl_detect_tx_5v(sd);
@@ -1691,12 +1708,23 @@ static int tc358743_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
+static u32 tc358743_g_colorspace(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ return V4L2_COLORSPACE_SRGB;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ return V4L2_COLORSPACE_SMPTE170M;
+ default:
+ return 0;
+ }
+}
+
static int tc358743_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
struct tc358743_state *state = to_state(sd);
- u8 vi_rep = i2c_rd8(sd, VI_REP);
if (format->pad != 0)
return -EINVAL;
@@ -1706,23 +1734,7 @@ static int tc358743_get_fmt(struct v4l2_subdev *sd,
format->format.height = state->timings.bt.height;
format->format.field = V4L2_FIELD_NONE;
- switch (vi_rep & MASK_VOUT_COLOR_SEL) {
- case MASK_VOUT_COLOR_RGB_FULL:
- case MASK_VOUT_COLOR_RGB_LIMITED:
- format->format.colorspace = V4L2_COLORSPACE_SRGB;
- break;
- case MASK_VOUT_COLOR_601_YCBCR_LIMITED:
- case MASK_VOUT_COLOR_601_YCBCR_FULL:
- format->format.colorspace = V4L2_COLORSPACE_SMPTE170M;
- break;
- case MASK_VOUT_COLOR_709_YCBCR_FULL:
- case MASK_VOUT_COLOR_709_YCBCR_LIMITED:
- format->format.colorspace = V4L2_COLORSPACE_REC709;
- break;
- default:
- format->format.colorspace = 0;
- break;
- }
+ format->format.colorspace = tc358743_g_colorspace(format->format.code);
return 0;
}
@@ -1736,19 +1748,14 @@ static int tc358743_set_fmt(struct v4l2_subdev *sd,
u32 code = format->format.code; /* is overwritten by get_fmt */
int ret = tc358743_get_fmt(sd, sd_state, format);
- format->format.code = code;
+ if (code == MEDIA_BUS_FMT_RGB888_1X24 ||
+ code == MEDIA_BUS_FMT_UYVY8_1X16)
+ format->format.code = code;
+ format->format.colorspace = tc358743_g_colorspace(format->format.code);
if (ret)
return ret;
- switch (code) {
- case MEDIA_BUS_FMT_RGB888_1X24:
- case MEDIA_BUS_FMT_UYVY8_1X16:
- break;
- default:
- return -EINVAL;
- }
-
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
return 0;
@@ -1972,8 +1979,19 @@ static int tc358743_probe_of(struct tc358743_state *state)
state->pdata.refclk_hz = clk_get_rate(refclk);
state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS;
state->pdata.enable_hdcp = false;
- /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */
- state->pdata.fifo_level = 16;
+ /*
+ * Ideally the FIFO trigger level should be set based on the input and
+ * output data rates, but the calculations required are buried in
+ * Toshiba's register settings spreadsheet.
+ * A value of 16 works with a 594Mbps data rate for 720p60 (using 2
+ * lanes) and 1080p60 (using 4 lanes), but fails when the data rate
+ * is increased, or a lower pixel clock is used that result in CSI
+ * reading out faster than the data is arriving.
+ *
+ * A value of 374 works with both those modes at 594Mbps, and with most
+ * modes on 972Mbps.
+ */
+ state->pdata.fifo_level = 374;
/*
* The PLL input clock is obtained by dividing refclk by pll_prd.
* It must be between 6 MHz and 40 MHz, lower frequency is better.
@@ -1993,6 +2011,7 @@ static int tc358743_probe_of(struct tc358743_state *state)
/*
* The CSI bps per lane must be between 62.5 Mbps and 1 Gbps.
* The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60.
+ * 972 Mbps allows 1080P50 UYVY over 2-lane.
*/
bps_pr_lane = 2 * endpoint.link_frequencies[0];
if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) {
@@ -2006,23 +2025,42 @@ static int tc358743_probe_of(struct tc358743_state *state)
state->pdata.refclk_hz * state->pdata.pll_prd;
/*
- * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz
- * link frequency). In principle it should be possible to calculate
+ * FIXME: These timings are from REF_02 for 594 or 972 Mbps per lane
+ * (297 MHz or 486 MHz link frequency).
+ * In principle it should be possible to calculate
* them based on link frequency and resolution.
*/
- if (bps_pr_lane != 594000000U)
+ switch (bps_pr_lane) {
+ default:
dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane);
- state->pdata.lineinitcnt = 0xe80;
- state->pdata.lptxtimecnt = 0x003;
- /* tclk-preparecnt: 3, tclk-zerocnt: 20 */
- state->pdata.tclk_headercnt = 0x1403;
- state->pdata.tclk_trailcnt = 0x00;
- /* ths-preparecnt: 3, ths-zerocnt: 1 */
- state->pdata.ths_headercnt = 0x0103;
- state->pdata.twakeup = 0x4882;
- state->pdata.tclk_postcnt = 0x008;
- state->pdata.ths_trailcnt = 0x2;
- state->pdata.hstxvregcnt = 0;
+ fallthrough;
+ case 594000000U:
+ state->pdata.lineinitcnt = 0xe80;
+ state->pdata.lptxtimecnt = 0x003;
+ /* tclk-preparecnt: 3, tclk-zerocnt: 20 */
+ state->pdata.tclk_headercnt = 0x1403;
+ state->pdata.tclk_trailcnt = 0x00;
+ /* ths-preparecnt: 3, ths-zerocnt: 1 */
+ state->pdata.ths_headercnt = 0x0103;
+ state->pdata.twakeup = 0x4882;
+ state->pdata.tclk_postcnt = 0x008;
+ state->pdata.ths_trailcnt = 0x2;
+ state->pdata.hstxvregcnt = 0;
+ break;
+ case 972000000U:
+ state->pdata.lineinitcnt = 0x1b58;
+ state->pdata.lptxtimecnt = 0x007;
+ /* tclk-preparecnt: 6, tclk-zerocnt: 40 */
+ state->pdata.tclk_headercnt = 0x2806;
+ state->pdata.tclk_trailcnt = 0x00;
+ /* ths-preparecnt: 6, ths-zerocnt: 8 */
+ state->pdata.ths_headercnt = 0x0806;
+ state->pdata.twakeup = 0x4268;
+ state->pdata.tclk_postcnt = 0x008;
+ state->pdata.ths_trailcnt = 0x5;
+ state->pdata.hstxvregcnt = 0;
+ break;
+ }
state->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
@@ -2061,6 +2099,7 @@ static int tc358743_probe(struct i2c_client *client)
struct tc358743_platform_data *pdata = client->dev.platform_data;
struct v4l2_subdev *sd;
u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK;
+ u16 chipid;
int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -2092,7 +2131,8 @@ static int tc358743_probe(struct i2c_client *client)
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
/* i2c access */
- if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) {
+ if (i2c_rd16_err(sd, CHIPID, &chipid) ||
+ (chipid & MASK_CHIPID) != 0) {
v4l2_info(sd, "not a TC358743 on address 0x%x\n",
client->addr << 1);
return -ENODEV;
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 959590afc80f..1087d2bddaf2 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -589,8 +589,8 @@ static void tda1997x_enable_edid(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "%s\n", __func__);
- /* Enable hotplug after 100ms */
- schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10);
+ /* Enable hotplug after 143ms */
+ schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 7);
}
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
index 25e2fc88a036..c0754fd03b1d 100644
--- a/drivers/media/i2c/vd55g1.c
+++ b/drivers/media/i2c/vd55g1.c
@@ -111,9 +111,9 @@
#define VD55G1_WIDTH 804
#define VD55G1_HEIGHT 704
-#define VD55G1_DEFAULT_MODE 0
+#define VD55G1_MODE_DEF 0
#define VD55G1_NB_GPIOS 4
-#define VD55G1_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8
+#define VD55G1_MBUS_CODE_DEF 0
#define VD55G1_DGAIN_DEF 256
#define VD55G1_AGAIN_DEF 19
#define VD55G1_EXPO_MAX_TERM 64
@@ -129,8 +129,8 @@
#define VD55G1_FWPATCH_REVISION_MINOR 9
#define VD55G1_XCLK_FREQ_MIN (6 * HZ_PER_MHZ)
#define VD55G1_XCLK_FREQ_MAX (27 * HZ_PER_MHZ)
-#define VD55G1_MIPI_RATE_MIN (250 * HZ_PER_MHZ)
-#define VD55G1_MIPI_RATE_MAX (1200 * HZ_PER_MHZ)
+#define VD55G1_MIPI_RATE_MIN (250 * MEGA)
+#define VD55G1_MIPI_RATE_MAX (1200 * MEGA)
static const u8 patch_array[] = {
0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
@@ -883,10 +883,9 @@ static int vd55g1_apply_cold_start(struct vd55g1 *sensor,
return ret;
}
-static void vd55g1_update_img_pad_format(struct vd55g1 *sensor,
- const struct vd55g1_mode *mode,
- u32 code,
- struct v4l2_mbus_framefmt *fmt)
+static void vd55g1_update_pad_fmt(struct vd55g1 *sensor,
+ const struct vd55g1_mode *mode, u32 code,
+ struct v4l2_mbus_framefmt *fmt)
{
fmt->code = code;
fmt->width = mode->width;
@@ -1038,8 +1037,6 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd,
if (ret < 0)
return ret;
- vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret);
-
/* Configure output */
vd55g1_write(sensor, VD55G1_REG_MIPI_DATA_RATE,
sensor->mipi_rate, &ret);
@@ -1084,7 +1081,7 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd,
err_rpm_put:
pm_runtime_put(sensor->dev);
- return 0;
+ return -EINVAL;
}
static int vd55g1_disable_streams(struct v4l2_subdev *sd,
@@ -1231,8 +1228,8 @@ static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd,
width, height, sd_fmt->format.width,
sd_fmt->format.height);
- vd55g1_update_img_pad_format(sensor, new_mode, sd_fmt->format.code,
- &sd_fmt->format);
+ vd55g1_update_pad_fmt(sensor, new_mode, sd_fmt->format.code,
+ &sd_fmt->format);
/*
* Use binning to maximize the crop rectangle size, and centre it in the
@@ -1262,7 +1259,6 @@ static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd,
static int vd55g1_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
- unsigned int def_mode = VD55G1_DEFAULT_MODE;
struct vd55g1 *sensor = to_vd55g1(sd);
struct v4l2_subdev_format fmt = { 0 };
struct v4l2_subdev_route routes[] = {
@@ -1279,8 +1275,9 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
if (ret)
return ret;
- vd55g1_update_img_pad_format(sensor, &vd55g1_supported_modes[def_mode],
- VD55G1_MEDIA_BUS_FMT_DEF, &fmt.format);
+ vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF],
+ vd55g1_mbus_codes[VD55G1_MBUS_CODE_DEF].code,
+ &fmt.format);
return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
}
@@ -1613,6 +1610,9 @@ static int vd55g1_power_on(struct device *dev)
goto disable_clock;
}
+ /* Setup clock now to advance through system FSM states */
+ vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret);
+
ret = vd55g1_patch(sensor);
if (ret) {
dev_err(dev, "Sensor patch failed %d\n", ret);
diff --git a/drivers/media/pci/cx18/cx18-av-vbi.c b/drivers/media/pci/cx18/cx18-av-vbi.c
index 65281d40c681..1a113aad9cd4 100644
--- a/drivers/media/pci/cx18/cx18-av-vbi.c
+++ b/drivers/media/pci/cx18/cx18-av-vbi.c
@@ -8,6 +8,7 @@
*/
+#include <linux/bitops.h>
#include "cx18-driver.h"
/*
@@ -56,15 +57,6 @@ struct vbi_anc_data {
/* u8 fill[]; Variable number of fill bytes */
};
-static int odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
static int decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
@@ -278,7 +270,7 @@ int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
break;
case 6:
sdid = V4L2_SLICED_CAPTION_525;
- err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ err = !parity8(p[0]) || !parity8(p[1]);
break;
case 9:
sdid = V4L2_SLICED_VPS;
diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
index af05bde75816..485ca9747c4c 100644
--- a/drivers/media/pci/cx18/cx18-driver.h
+++ b/drivers/media/pci/cx18/cx18-driver.h
@@ -271,18 +271,6 @@ struct cx18_options {
#define CX18_SLICED_TYPE_WSS_625 (5)
#define CX18_SLICED_TYPE_VPS (7)
-/**
- * list_entry_is_past_end - check if a previous loop cursor is off list end
- * @pos: the type * previously used as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_head within the struct.
- *
- * Check if the entry's list_head is the head of the list, thus it's not a
- * real entry but was the loop cursor that walked past the end
- */
-#define list_entry_is_past_end(pos, head, member) \
- (&pos->member == (head))
-
struct cx18_vb2_buffer {
/* Common video buffer sub-system struct */
struct vb2_v4l2_buffer vb;
diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c
index 315577d71d95..cefa91b37f89 100644
--- a/drivers/media/pci/cx18/cx18-fileops.c
+++ b/drivers/media/pci/cx18/cx18-fileops.c
@@ -371,7 +371,7 @@ static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
mdl->curr_buf = list_first_entry(&mdl->buf_list,
struct cx18_buffer, list);
- if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ if (list_entry_is_head(mdl->curr_buf, &mdl->buf_list, list)) {
/*
* For some reason we've exhausted the buffers, but the MDL
* object still said some data was unread.
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 1817b9ed042c..9a1512b1ccaa 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -764,7 +764,7 @@ static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
mdl->curr_buf = list_first_entry(&mdl->buf_list,
struct cx18_buffer, list);
- if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ if (list_entry_is_head(mdl->curr_buf, &mdl->buf_list, list)) {
/*
* For some reason we've exhausted the buffers, but the MDL
* object still said some data was unread.
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 83e682e1a4b7..4e579352ab2c 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -55,11 +55,15 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* Himax HM2172 */
IPU_SENSOR_CONFIG("HIMX2172", 1, 384000000),
/* GalaxyCore GC0310 */
- IPU_SENSOR_CONFIG("INT0310", 0),
+ IPU_SENSOR_CONFIG("INT0310", 1, 55692000),
/* Omnivision OV5693 */
IPU_SENSOR_CONFIG("INT33BE", 1, 419200000),
+ /* Onsemi MT9M114 */
+ IPU_SENSOR_CONFIG("INT33F0", 1, 384000000),
/* Omnivision OV2740 */
IPU_SENSOR_CONFIG("INT3474", 1, 180000000),
+ /* Omnivision OV5670 */
+ IPU_SENSOR_CONFIG("INT3479", 1, 422400000),
/* Omnivision OV8865 */
IPU_SENSOR_CONFIG("INT347A", 1, 360000000),
/* Omnivision OV7251 */
@@ -78,7 +82,7 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* Omnivision OV08A10 */
IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000),
/* Omnivision OV08x40 */
- IPU_SENSOR_CONFIG("OVTI08F4", 1, 400000000),
+ IPU_SENSOR_CONFIG("OVTI08F4", 3, 400000000, 749000000, 800000000),
/* Omnivision OV13B10 */
IPU_SENSOR_CONFIG("OVTI13B1", 1, 560000000),
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
@@ -86,6 +90,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
+ /* Toshiba T4KA3 */
+ IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000),
};
static const struct ipu_property_names prop_names = {
@@ -809,7 +815,8 @@ int ipu_bridge_init(struct device *dev,
return 0;
if (!ipu_bridge_ivsc_is_ready())
- return -EPROBE_DEFER;
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "waiting for IVSC to become ready\n");
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
if (!bridge)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 16fde96c9fb2..a87f105beb5e 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -358,6 +358,8 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS,
CIO2_FBPT_SUBENTRY_UNIT);
const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
const struct ipu3_cio2_fmt *fmt;
void __iomem *const base = cio2->base;
u8 lanes, csi2bus = q->csi2.port;
@@ -365,7 +367,13 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
struct cio2_csi2_timing timing = { 0 };
int i, r;
- fmt = cio2_find_format(NULL, &q->subdev_fmt.code);
+ state = v4l2_subdev_lock_and_get_active_state(&q->subdev);
+ format = v4l2_subdev_state_get_format(state, CIO2_PAD_SINK);
+
+ fmt = cio2_find_format(NULL, &format->code);
+
+ v4l2_subdev_unlock_state(state);
+
if (!fmt)
return -EINVAL;
@@ -1194,9 +1202,9 @@ static int cio2_subdev_subscribe_event(struct v4l2_subdev *sd,
return v4l2_event_subscribe(fh, sub, 0, NULL);
}
-static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+static int cio2_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
{
- struct v4l2_mbus_framefmt *format;
const struct v4l2_mbus_framefmt fmt_default = {
.width = 1936,
.height = 1096,
@@ -1207,42 +1215,23 @@ static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
.quantization = V4L2_QUANTIZATION_DEFAULT,
.xfer_func = V4L2_XFER_FUNC_DEFAULT,
};
+ struct v4l2_mbus_framefmt *format;
- /* Initialize try_fmt */
- format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SINK);
+ /* Initialize the format on the sink and source pads. */
+ format = v4l2_subdev_state_get_format(state, CIO2_PAD_SINK);
*format = fmt_default;
/* same as sink */
- format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, CIO2_PAD_SOURCE);
*format = fmt_default;
return 0;
}
-static int cio2_subdev_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
-
- mutex_lock(&q->subdev_lock);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
- fmt->format = *v4l2_subdev_state_get_format(sd_state,
- fmt->pad);
- else
- fmt->format = q->subdev_fmt;
-
- mutex_unlock(&q->subdev_lock);
-
- return 0;
-}
-
static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
struct v4l2_mbus_framefmt *mbus;
u32 mbus_code = fmt->format.code;
unsigned int i;
@@ -1252,12 +1241,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
* source always propagates from sink
*/
if (fmt->pad == CIO2_PAD_SOURCE)
- return cio2_subdev_get_fmt(sd, sd_state, fmt);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
- mbus = v4l2_subdev_state_get_format(sd_state, fmt->pad);
- else
- mbus = &q->subdev_fmt;
+ return v4l2_subdev_get_fmt(sd, sd_state, fmt);
fmt->format.code = formats[0].mbus_code;
@@ -1272,9 +1256,12 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT);
fmt->format.field = V4L2_FIELD_NONE;
- mutex_lock(&q->subdev_lock);
+ mbus = v4l2_subdev_state_get_format(sd_state, CIO2_PAD_SINK);
+ *mbus = fmt->format;
+
+ /* Propagate the format to the source pad. */
+ mbus = v4l2_subdev_state_get_format(sd_state, CIO2_PAD_SOURCE);
*mbus = fmt->format;
- mutex_unlock(&q->subdev_lock);
return 0;
}
@@ -1345,12 +1332,12 @@ static const struct v4l2_subdev_core_ops cio2_subdev_core_ops = {
};
static const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = {
- .open = cio2_subdev_open,
+ .init_state = cio2_subdev_init_state,
};
static const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = {
.link_validate = v4l2_subdev_link_validate_default,
- .get_fmt = cio2_subdev_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = cio2_subdev_set_fmt,
.enum_mbus_code = cio2_subdev_enum_mbus_code,
};
@@ -1502,28 +1489,18 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
{
static const u32 default_width = 1936;
static const u32 default_height = 1096;
- const struct ipu3_cio2_fmt dflt_fmt = formats[0];
struct device *dev = &cio2->pci_dev->dev;
struct video_device *vdev = &q->vdev;
struct vb2_queue *vbq = &q->vbq;
struct v4l2_subdev *subdev = &q->subdev;
- struct v4l2_mbus_framefmt *fmt;
int r;
/* Initialize miscellaneous variables */
mutex_init(&q->lock);
- mutex_init(&q->subdev_lock);
-
- /* Initialize formats to default values */
- fmt = &q->subdev_fmt;
- fmt->width = default_width;
- fmt->height = default_height;
- fmt->code = dflt_fmt.mbus_code;
- fmt->field = V4L2_FIELD_NONE;
q->format.width = default_width;
q->format.height = default_height;
- q->format.pixelformat = dflt_fmt.fourcc;
+ q->format.pixelformat = formats[0].fourcc;
q->format.colorspace = V4L2_COLORSPACE_RAW;
q->format.field = V4L2_FIELD_NONE;
q->format.num_planes = 1;
@@ -1567,9 +1544,16 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
CIO2_ENTITY_NAME " %td", q - cio2->queue);
subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
v4l2_set_subdevdata(subdev, cio2);
+
+ r = v4l2_subdev_init_finalize(subdev);
+ if (r) {
+ dev_err(dev, "failed to initialize subdev (%d)\n", r);
+ goto fail_subdev;
+ }
+
r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev);
if (r) {
- dev_err(dev, "failed initialize subdev (%d)\n", r);
+ dev_err(dev, "failed to register subdev (%d)\n", r);
goto fail_subdev;
}
@@ -1626,7 +1610,6 @@ fail_vdev_media_entity:
fail_subdev_media_entity:
cio2_fbpt_exit(q, dev);
fail_fbpt:
- mutex_destroy(&q->subdev_lock);
mutex_destroy(&q->lock);
return r;
@@ -1639,7 +1622,6 @@ static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
v4l2_device_unregister_subdev(&q->subdev);
media_entity_cleanup(&q->subdev.entity);
cio2_fbpt_exit(q, &cio2->pci_dev->dev);
- mutex_destroy(&q->subdev_lock);
mutex_destroy(&q->lock);
}
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index d7cb7dae665b..19258190936a 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -351,9 +351,7 @@ struct cio2_queue {
/* Subdev, /dev/v4l-subdevX */
struct v4l2_subdev subdev;
- struct mutex subdev_lock; /* Serialise acces to subdev_fmt field */
struct media_pad subdev_pads[CIO2_PADS];
- struct v4l2_mbus_framefmt subdev_fmt;
atomic_t frame_sequence;
/* Video device, /dev/videoX */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
index da8581a37e22..6030bd23b4b9 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -354,9 +354,9 @@ static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,
remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
- sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC,
- CSI2_PAD_SINK,
- &streams_mask);
+ sink_streams =
+ v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
+ &streams_mask);
ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
if (ret)
@@ -384,9 +384,9 @@ static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd,
struct media_pad *remote_pad;
u64 sink_streams;
- sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC,
- CSI2_PAD_SINK,
- &streams_mask);
+ sink_streams =
+ v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
+ &streams_mask);
remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
index f488e782c26e..0e2c8b71edfc 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
@@ -40,7 +40,7 @@ struct ipu6_bus_device;
#define IPU6_ISYS_NUM_RECV_QUEUE 1
#define IPU6_ISYS_MIN_WIDTH 2U
-#define IPU6_ISYS_MIN_HEIGHT 2U
+#define IPU6_ISYS_MIN_HEIGHT 1U
#define IPU6_ISYS_MAX_WIDTH 4672U
#define IPU6_ISYS_MAX_HEIGHT 3416U
diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c
index 3622271c71c8..98310b8511b1 100644
--- a/drivers/media/pci/intel/ivsc/mei_ace.c
+++ b/drivers/media/pci/intel/ivsc/mei_ace.c
@@ -529,6 +529,8 @@ static void mei_ace_remove(struct mei_cl_device *cldev)
ace_set_camera_owner(ace, ACE_CAMERA_IVSC);
+ mei_cldev_disable(cldev);
+
mutex_destroy(&ace->lock);
}
@@ -574,7 +576,7 @@ static struct mei_cl_driver mei_ace_driver = {
module_mei_cl_driver(mei_ace_driver);
-MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_DESCRIPTION("Device driver for IVSC ACE");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c
index 92d871a378ba..c2917e156345 100644
--- a/drivers/media/pci/intel/ivsc/mei_csi.c
+++ b/drivers/media/pci/intel/ivsc/mei_csi.c
@@ -760,6 +760,8 @@ static void mei_csi_remove(struct mei_cl_device *cldev)
pm_runtime_disable(&cldev->dev);
+ mei_cldev_disable(cldev);
+
mutex_destroy(&csi->lock);
}
@@ -783,7 +785,7 @@ static struct mei_cl_driver mei_csi_driver = {
module_mei_cl_driver(mei_csi_driver);
MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
-MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_DESCRIPTION("Device driver for IVSC CSI");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c
index 14c5725bd4d8..c179c425e167 100644
--- a/drivers/media/pci/mgb4/mgb4_vout.c
+++ b/drivers/media/pci/mgb4/mgb4_vout.c
@@ -492,7 +492,14 @@ static int vidioc_s_dv_timings(struct file *file, void *fh,
static int vidioc_enum_dv_timings(struct file *file, void *fh,
struct v4l2_enum_dv_timings *timings)
{
- return v4l2_enum_dv_timings_cap(timings, &video_timings_cap, NULL, NULL);
+ struct mgb4_vout_dev *voutdev = video_drvdata(file);
+
+ if (timings->index != 0)
+ return -EINVAL;
+
+ get_timings(voutdev, &timings->timings);
+
+ return 0;
}
static int vidioc_dv_timings_cap(struct file *file, void *fh,
diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c
index 89c5b79a5b24..7820e4f47fd5 100644
--- a/drivers/media/pci/saa7164/saa7164-buffer.c
+++ b/drivers/media/pci/saa7164/saa7164-buffer.c
@@ -52,26 +52,6 @@
* | etc
*/
-void saa7164_buffer_display(struct saa7164_buffer *buf)
-{
- struct saa7164_dev *dev = buf->port->dev;
- int i;
-
- dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n",
- __func__, buf, buf->idx);
- dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n",
- buf->cpu, (long long)buf->dma, buf->pci_size);
- dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n",
- buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size);
-
- /* Format the Page Table Entries to point into the data buffer */
- for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
-
- dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n",
- i, buf->pt_cpu, (u64)*(buf->pt_cpu));
-
- }
-}
/* Allocate a new buffer structure and associated PCI space in bytes.
* len must be a multiple of sizeof(u64)
*/
diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
index 42bd8e76005b..a95662776ee8 100644
--- a/drivers/media/pci/saa7164/saa7164-cmd.c
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
@@ -295,34 +295,6 @@ static int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
return ret;
}
-void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno)
-{
- int i;
- dprintk(DBGLVL_CMD, "%s()\n", __func__);
-
- mutex_lock(&dev->lock);
- for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
- if (dev->cmds[i].inuse == 1) {
- dprintk(DBGLVL_CMD,
- "seqno %d inuse, sig = %d, t/out = %d\n",
- dev->cmds[i].seqno,
- dev->cmds[i].signalled,
- dev->cmds[i].timeout);
- }
- }
-
- for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
- if ((dev->cmds[i].inuse == 1) && ((i == 0) ||
- (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) {
- dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n",
- __func__, i);
- dev->cmds[i].signalled = 1;
- wake_up(&dev->cmds[i].wait);
- }
- }
- mutex_unlock(&dev->lock);
-}
-
int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
u16 controlselector, u16 size, void *buf)
{
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
index eede47b686a3..e1bac1fe19d3 100644
--- a/drivers/media/pci/saa7164/saa7164.h
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -508,7 +508,6 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
int saa7164_cmd_send(struct saa7164_dev *dev,
u8 id, enum tmComResCmd command, u16 controlselector,
u16 size, void *buf);
-void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
int saa7164_irq_dequeue(struct saa7164_dev *dev);
/* ----------------------------------------------------------- */
@@ -570,7 +569,6 @@ extern int saa7164_dvb_unregister(struct saa7164_port *port);
extern struct saa7164_buffer *saa7164_buffer_alloc(
struct saa7164_port *port, u32 len);
extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
-extern void saa7164_buffer_display(struct saa7164_buffer *buf);
extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c
index febb2c156cf6..d1d3a83d0122 100644
--- a/drivers/media/pci/solo6x10/solo6x10-core.c
+++ b/drivers/media/pci/solo6x10/solo6x10-core.c
@@ -432,7 +432,7 @@ static int solo_sysfs_init(struct solo_dev *solo_dev)
sysfs_attr_init(&sdram_attr->attr);
sdram_attr->attr.name = "sdram";
sdram_attr->attr.mode = 0440;
- sdram_attr->read_new = sdram_show;
+ sdram_attr->read = sdram_show;
sdram_attr->size = solo_dev->sdram_size;
if (device_create_bin_file(dev, sdram_attr)) {
diff --git a/drivers/media/pci/solo6x10/solo6x10-gpio.c b/drivers/media/pci/solo6x10/solo6x10-gpio.c
index 084c30760e45..b16a8453a62a 100644
--- a/drivers/media/pci/solo6x10/solo6x10-gpio.c
+++ b/drivers/media/pci/solo6x10/solo6x10-gpio.c
@@ -116,18 +116,6 @@ static int solo_gpiochip_get_direction(struct gpio_chip *chip,
return -1;
}
-static int solo_gpiochip_direction_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- return -1;
-}
-
-static int solo_gpiochip_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- return -1;
-}
-
static int solo_gpiochip_get(struct gpio_chip *chip,
unsigned int offset)
{
@@ -139,8 +127,8 @@ static int solo_gpiochip_get(struct gpio_chip *chip,
return 1 & (ret >> (offset + 8));
}
-static void solo_gpiochip_set(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int solo_gpiochip_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
{
struct solo_dev *solo_dev = gpiochip_get_data(chip);
@@ -148,6 +136,8 @@ static void solo_gpiochip_set(struct gpio_chip *chip,
solo_gpio_set(solo_dev, 1 << (offset + 8));
else
solo_gpio_clear(solo_dev, 1 << (offset + 8));
+
+ return 0;
}
#endif
@@ -167,10 +157,8 @@ int solo_gpio_init(struct solo_dev *solo_dev)
solo_dev->gpio_dev.can_sleep = 0;
solo_dev->gpio_dev.get_direction = solo_gpiochip_get_direction;
- solo_dev->gpio_dev.direction_input = solo_gpiochip_direction_input;
- solo_dev->gpio_dev.direction_output = solo_gpiochip_direction_output;
solo_dev->gpio_dev.get = solo_gpiochip_get;
- solo_dev->gpio_dev.set = solo_gpiochip_set;
+ solo_dev->gpio_dev.set_rv = solo_gpiochip_set;
ret = gpiochip_add_data(&solo_dev->gpio_dev, solo_dev);
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index 85d518823159..32eef2fd1f2a 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -26,6 +26,7 @@
#include "vpu_cmds.h"
#include "vpu_rpc.h"
+#define VDEC_SLOT_CNT_DFT 32
#define VDEC_MIN_BUFFER_CAP 8
#define VDEC_MIN_BUFFER_OUT 8
@@ -41,6 +42,14 @@ struct vdec_fs_info {
u32 tag;
};
+struct vdec_frame_store_t {
+ struct vpu_vb2_buffer *curr;
+ struct vpu_vb2_buffer *pend;
+ dma_addr_t addr;
+ unsigned int state;
+ u32 tag;
+};
+
struct vdec_t {
u32 seq_hdr_found;
struct vpu_buffer udata;
@@ -48,7 +57,8 @@ struct vdec_t {
struct vpu_dec_codec_info codec_info;
enum vpu_codec_state state;
- struct vpu_vb2_buffer *slots[VB2_MAX_FRAME];
+ struct vdec_frame_store_t *slots;
+ u32 slot_count;
u32 req_frame_count;
struct vdec_fs_info mbi;
struct vdec_fs_info dcp;
@@ -232,6 +242,35 @@ static int vdec_ctrl_init(struct vpu_inst *inst)
V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE,
0, 1, 1, 0);
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)),
+ V4L2_MPEG_VIDEO_H264_PROFILE_MAIN);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
+ 0,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ ~((1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10)),
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
+ 0,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_4);
+
ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2);
if (ctrl)
@@ -258,6 +297,63 @@ static int vdec_ctrl_init(struct vpu_inst *inst)
return 0;
}
+static void vdec_attach_frame_store(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+ struct vdec_t *vdec = inst->priv;
+ struct vdec_frame_store_t *new_slots = NULL;
+ dma_addr_t addr;
+ int i;
+
+ addr = vpu_get_vb_phy_addr(vb, 0);
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (addr == vdec->slots[i].addr) {
+ if (vdec->slots[i].curr && vdec->slots[i].curr != vpu_buf) {
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_CHANGED);
+ vdec->slots[i].pend = vpu_buf;
+ } else {
+ vpu_set_buffer_state(vbuf, vdec->slots[i].state);
+ }
+ vpu_buf->fs_id = i;
+ return;
+ }
+ }
+
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (!vdec->slots[i].addr) {
+ vdec->slots[i].addr = addr;
+ vpu_buf->fs_id = i;
+ return;
+ }
+ }
+
+ new_slots = krealloc_array(vdec->slots, vdec->slot_count * 2,
+ sizeof(*vdec->slots),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!new_slots) {
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_ERROR);
+ return;
+ }
+
+ vdec->slots = new_slots;
+ vdec->slot_count *= 2;
+
+ vdec->slots[i].addr = addr;
+ vpu_buf->fs_id = i;
+}
+
+static void vdec_reset_frame_store(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (!vdec->slots || !vdec->slot_count)
+ return;
+
+ vpu_trace(inst->dev, "inst[%d] reset slots\n", inst->id);
+ memset(vdec->slots, 0, sizeof(*vdec->slots) * vdec->slot_count);
+}
+
static void vdec_handle_resolution_change(struct vpu_inst *inst)
{
struct vdec_t *vdec = inst->priv;
@@ -714,11 +810,11 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg)
struct vb2_v4l2_buffer *src_buf;
int ret = 0;
- if (!info || info->id >= ARRAY_SIZE(vdec->slots))
+ if (!info || info->id >= vdec->slot_count)
return -EINVAL;
vpu_inst_lock(inst);
- vpu_buf = vdec->slots[info->id];
+ vpu_buf = vdec->slots[info->id].curr;
if (!vpu_buf) {
dev_err(inst->dev, "[%d] decoded invalid frame[%d]\n", inst->id, info->id);
ret = -EINVAL;
@@ -739,11 +835,13 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg)
if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED)
dev_info(inst->dev, "[%d] buf[%d] has been decoded\n", inst->id, info->id);
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_DECODED);
+ vdec->slots[info->id].state = VPU_BUF_STATE_DECODED;
vdec->decoded_frame_count++;
if (vdec->params.display_delay_enable) {
struct vpu_format *cur_fmt;
cur_fmt = vpu_get_format(inst, inst->cap_format.type);
+ vdec->slots[info->id].state = VPU_BUF_STATE_READY;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY);
for (int i = 0; i < vbuf->vb2_buf.num_planes; i++)
vb2_set_plane_payload(&vbuf->vb2_buf,
@@ -766,11 +864,11 @@ static struct vpu_vb2_buffer *vdec_find_buffer(struct vpu_inst *inst, u32 luma)
struct vdec_t *vdec = inst->priv;
int i;
- for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) {
- if (!vdec->slots[i])
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (!vdec->slots[i].curr)
continue;
- if (luma == vdec->slots[i]->luma)
- return vdec->slots[i];
+ if (luma == vdec->slots[i].addr)
+ return vdec->slots[i].curr;
}
return NULL;
@@ -804,11 +902,11 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
cur_fmt = vpu_get_format(inst, inst->cap_format.type);
vbuf = &vpu_buf->m2m_buf.vb;
- if (vbuf->vb2_buf.index != frame->id)
- dev_err(inst->dev, "[%d] buffer id(%d, %d) mismatch\n",
- inst->id, vbuf->vb2_buf.index, frame->id);
+ if (vpu_buf->fs_id != frame->id)
+ dev_err(inst->dev, "[%d] buffer id(%d(%d), %d) mismatch\n",
+ inst->id, vpu_buf->fs_id, vbuf->vb2_buf.index, frame->id);
- if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable)
+ if (vdec->params.display_delay_enable)
return;
if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_DECODED)
@@ -821,10 +919,11 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
vbuf->sequence = vdec->sequence;
dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
vpu_inst_lock(inst);
+ vdec->slots[vpu_buf->fs_id].state = VPU_BUF_STATE_READY;
vdec->display_frame_count++;
vpu_inst_unlock(inst);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
dev_dbg(inst->dev, "[%d] decoded : %d, display : %d, sequence : %d\n",
inst->id, vdec->decoded_frame_count, vdec->display_frame_count, vdec->sequence);
}
@@ -1052,18 +1151,30 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb
if (!vbuf)
return -EINVAL;
- if (vdec->slots[vbuf->vb2_buf.index]) {
- dev_err(inst->dev, "[%d] repeat alloc fs %d\n",
- inst->id, vbuf->vb2_buf.index);
+ vpu_buf = to_vpu_vb2_buffer(vbuf);
+ if (vpu_buf->fs_id < 0 || vpu_buf->fs_id >= vdec->slot_count) {
+ dev_err(inst->dev, "invalid fs %d for v4l2 buffer %d\n",
+ vpu_buf->fs_id, vbuf->vb2_buf.index);
return -EINVAL;
}
+ if (vdec->slots[vpu_buf->fs_id].curr) {
+ if (vdec->slots[vpu_buf->fs_id].curr != vpu_buf) {
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_CHANGED);
+ vdec->slots[vpu_buf->fs_id].pend = vpu_buf;
+ } else {
+ vpu_set_buffer_state(vbuf, vdec->slots[vpu_buf->fs_id].state);
+ }
+ dev_err(inst->dev, "[%d] repeat alloc fs %d (v4l2 index %d)\n",
+ inst->id, vpu_buf->fs_id, vbuf->vb2_buf.index);
+ return -EAGAIN;
+ }
+
dev_dbg(inst->dev, "[%d] state = %s, alloc fs %d, tag = 0x%x\n",
inst->id, vpu_codec_state_name(inst->state), vbuf->vb2_buf.index, vdec->seq_tag);
- vpu_buf = to_vpu_vb2_buffer(vbuf);
memset(&info, 0, sizeof(info));
- info.id = vbuf->vb2_buf.index;
+ info.id = vpu_buf->fs_id;
info.type = MEM_RES_FRAME;
info.tag = vdec->seq_tag;
info.luma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 0);
@@ -1078,12 +1189,13 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb
if (ret)
return ret;
- vpu_buf->tag = info.tag;
vpu_buf->luma = info.luma_addr;
vpu_buf->chroma_u = info.chroma_addr;
vpu_buf->chroma_v = 0;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE);
- vdec->slots[info.id] = vpu_buf;
+ vdec->slots[info.id].tag = info.tag;
+ vdec->slots[info.id].curr = vpu_buf;
+ vdec->slots[info.id].state = VPU_BUF_STATE_INUSE;
vdec->req_frame_count--;
return 0;
@@ -1144,25 +1256,76 @@ static void vdec_recycle_buffer(struct vpu_inst *inst, struct vb2_v4l2_buffer *v
v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf);
}
-static void vdec_clear_slots(struct vpu_inst *inst)
+static void vdec_release_curr_frame_store(struct vpu_inst *inst, u32 id)
{
struct vdec_t *vdec = inst->priv;
struct vpu_vb2_buffer *vpu_buf;
struct vb2_v4l2_buffer *vbuf;
+
+ if (id >= vdec->slot_count)
+ return;
+ if (!vdec->slots[id].curr)
+ return;
+
+ vpu_buf = vdec->slots[id].curr;
+ vbuf = &vpu_buf->m2m_buf.vb;
+
+ vdec_response_fs_release(inst, id, vdec->slots[id].tag);
+ if (vpu_buf->fs_id == id) {
+ if (vpu_buf->state != VPU_BUF_STATE_READY)
+ vdec_recycle_buffer(inst, vbuf);
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ }
+
+ vdec->slots[id].curr = NULL;
+ vdec->slots[id].state = VPU_BUF_STATE_IDLE;
+
+ if (vdec->slots[id].pend) {
+ vpu_set_buffer_state(&vdec->slots[id].pend->m2m_buf.vb, VPU_BUF_STATE_IDLE);
+ vdec->slots[id].pend = NULL;
+ }
+}
+
+static void vdec_clear_slots(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
int i;
- for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) {
- if (!vdec->slots[i])
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (!vdec->slots[i].curr)
continue;
- vpu_buf = vdec->slots[i];
- vbuf = &vpu_buf->m2m_buf.vb;
-
vpu_trace(inst->dev, "clear slot %d\n", i);
- vdec_response_fs_release(inst, i, vpu_buf->tag);
- vdec_recycle_buffer(inst, vbuf);
- vdec->slots[i]->state = VPU_BUF_STATE_IDLE;
- vdec->slots[i] = NULL;
+ vdec_release_curr_frame_store(inst, i);
+ }
+}
+
+static void vdec_update_v4l2_ctrl(struct vpu_inst *inst, u32 id, u32 val)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler, id);
+
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, val);
+}
+
+static void vdec_update_v4l2_profile_level(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr)
+{
+ switch (inst->out_format.pixfmt) {
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_H264_MVC:
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ vpu_get_h264_v4l2_profile(hdr));
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ vpu_get_h264_v4l2_level(hdr));
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ vpu_get_hevc_v4l2_profile(hdr));
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ vpu_get_hevc_v4l2_level(hdr));
+ break;
+ default:
+ return;
}
}
@@ -1189,6 +1352,7 @@ static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info
vdec_init_crop(inst);
vdec_init_mbi(inst);
vdec_init_dcp(inst);
+ vdec_update_v4l2_profile_level(inst, hdr);
if (!vdec->seq_hdr_found) {
vdec->seq_tag = vdec->codec_info.tag;
if (vdec->is_source_changed) {
@@ -1263,39 +1427,29 @@ static void vdec_event_req_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
static void vdec_evnet_rel_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
{
struct vdec_t *vdec = inst->priv;
- struct vpu_vb2_buffer *vpu_buf;
- struct vb2_v4l2_buffer *vbuf;
- if (!fs || fs->id >= ARRAY_SIZE(vdec->slots))
+ if (!fs || fs->id >= vdec->slot_count)
return;
if (fs->type != MEM_RES_FRAME)
return;
- if (fs->id >= vpu_get_num_buffers(inst, inst->cap_format.type)) {
+ if (fs->id >= vdec->slot_count) {
dev_err(inst->dev, "[%d] invalid fs(%d) to release\n", inst->id, fs->id);
return;
}
vpu_inst_lock(inst);
- vpu_buf = vdec->slots[fs->id];
- vdec->slots[fs->id] = NULL;
-
- if (!vpu_buf) {
+ if (!vdec->slots[fs->id].curr) {
dev_dbg(inst->dev, "[%d] fs[%d] has bee released\n", inst->id, fs->id);
goto exit;
}
- vbuf = &vpu_buf->m2m_buf.vb;
- if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) {
+ if (vdec->slots[fs->id].state == VPU_BUF_STATE_DECODED) {
dev_dbg(inst->dev, "[%d] frame skip\n", inst->id);
vdec->sequence++;
}
- vdec_response_fs_release(inst, fs->id, vpu_buf->tag);
- if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_READY)
- vdec_recycle_buffer(inst, vbuf);
-
- vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ vdec_release_curr_frame_store(inst, fs->id);
vpu_process_capture_buffer(inst);
exit:
@@ -1485,6 +1639,11 @@ static void vdec_cleanup(struct vpu_inst *inst)
return;
vdec = inst->priv;
+ if (vdec) {
+ kfree(vdec->slots);
+ vdec->slots = NULL;
+ vdec->slot_count = 0;
+ }
vfree(vdec);
inst->priv = NULL;
vfree(inst);
@@ -1606,11 +1765,43 @@ static int vdec_stop_session(struct vpu_inst *inst, u32 type)
return 0;
}
-static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+static int vdec_get_slot_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
{
struct vdec_t *vdec = inst->priv;
+ struct vpu_vb2_buffer *vpu_buf;
int num = -1;
+ vpu_inst_lock(inst);
+ if (i >= vdec->slot_count || !vdec->slots[i].addr)
+ goto exit;
+
+ vpu_buf = vdec->slots[i].curr;
+
+ num = scnprintf(str, size, "slot[%2d] :", i);
+ if (vpu_buf) {
+ num += scnprintf(str + num, size - num, " %2d",
+ vpu_buf->m2m_buf.vb.vb2_buf.index);
+ num += scnprintf(str + num, size - num, "; state = %d", vdec->slots[i].state);
+ } else {
+ num += scnprintf(str + num, size - num, " -1");
+ }
+
+ if (vdec->slots[i].pend)
+ num += scnprintf(str + num, size - num, "; %d",
+ vdec->slots[i].pend->m2m_buf.vb.vb2_buf.index);
+
+ num += scnprintf(str + num, size - num, "\n");
+exit:
+ vpu_inst_unlock(inst);
+
+ return num;
+}
+
+static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+{
+ struct vdec_t *vdec = inst->priv;
+ int num;
+
switch (i) {
case 0:
num = scnprintf(str, size,
@@ -1664,6 +1855,7 @@ static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i
vdec->codec_info.vui_present);
break;
default:
+ num = vdec_get_slot_debug_info(inst, str, size, i - 10);
break;
}
@@ -1687,6 +1879,8 @@ static struct vpu_inst_ops vdec_inst_ops = {
.get_debug_info = vdec_get_debug_info,
.wait_prepare = vpu_inst_unlock,
.wait_finish = vpu_inst_lock,
+ .attach_frame_store = vdec_attach_frame_store,
+ .reset_frame_store = vdec_reset_frame_store,
};
static void vdec_init(struct file *file)
@@ -1727,6 +1921,16 @@ static int vdec_open(struct file *file)
return -ENOMEM;
}
+ vdec->slots = kmalloc_array(VDEC_SLOT_CNT_DFT,
+ sizeof(*vdec->slots),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!vdec->slots) {
+ vfree(vdec);
+ vfree(inst);
+ return -ENOMEM;
+ }
+ vdec->slot_count = VDEC_SLOT_CNT_DFT;
+
inst->ops = &vdec_inst_ops;
inst->formats = vdec_formats;
inst->type = VPU_CORE_TYPE_DEC;
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h
index 1451549c9dd2..cac0f1a64fea 100644
--- a/drivers/media/platform/amphion/vpu.h
+++ b/drivers/media/platform/amphion/vpu.h
@@ -222,6 +222,8 @@ struct vpu_inst_ops {
int (*get_debug_info)(struct vpu_inst *inst, char *str, u32 size, u32 i);
void (*wait_prepare)(struct vpu_inst *inst);
void (*wait_finish)(struct vpu_inst *inst);
+ void (*attach_frame_store)(struct vpu_inst *inst, struct vb2_buffer *vb);
+ void (*reset_frame_store)(struct vpu_inst *inst);
};
struct vpu_inst {
@@ -295,7 +297,8 @@ enum {
VPU_BUF_STATE_DECODED,
VPU_BUF_STATE_READY,
VPU_BUF_STATE_SKIP,
- VPU_BUF_STATE_ERROR
+ VPU_BUF_STATE_ERROR,
+ VPU_BUF_STATE_CHANGED
};
struct vpu_vb2_buffer {
@@ -304,8 +307,8 @@ struct vpu_vb2_buffer {
dma_addr_t chroma_u;
dma_addr_t chroma_v;
unsigned int state;
- u32 tag;
u32 average_qp;
+ s32 fs_id;
};
void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val);
diff --git a/drivers/media/platform/amphion/vpu_color.c b/drivers/media/platform/amphion/vpu_color.c
index 4ae435cbc5cd..7c0ab8289a7b 100644
--- a/drivers/media/platform/amphion/vpu_color.c
+++ b/drivers/media/platform/amphion/vpu_color.c
@@ -108,76 +108,3 @@ u32 vpu_color_cvrt_full_range_i2v(u32 full_range)
return V4L2_QUANTIZATION_LIM_RANGE;
}
-
-int vpu_color_check_primaries(u32 primaries)
-{
- return vpu_color_cvrt_primaries_v2i(primaries) ? 0 : -EINVAL;
-}
-
-int vpu_color_check_transfers(u32 transfers)
-{
- return vpu_color_cvrt_transfers_v2i(transfers) ? 0 : -EINVAL;
-}
-
-int vpu_color_check_matrix(u32 matrix)
-{
- return vpu_color_cvrt_matrix_v2i(matrix) ? 0 : -EINVAL;
-}
-
-int vpu_color_check_full_range(u32 full_range)
-{
- int ret = -EINVAL;
-
- switch (full_range) {
- case V4L2_QUANTIZATION_FULL_RANGE:
- case V4L2_QUANTIZATION_LIM_RANGE:
- ret = 0;
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range)
-{
- u32 transfers;
- u32 matrix;
- u32 full_range;
-
- switch (primaries) {
- case V4L2_COLORSPACE_REC709:
- transfers = V4L2_XFER_FUNC_709;
- matrix = V4L2_YCBCR_ENC_709;
- break;
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- case V4L2_COLORSPACE_SMPTE170M:
- transfers = V4L2_XFER_FUNC_709;
- matrix = V4L2_YCBCR_ENC_601;
- break;
- case V4L2_COLORSPACE_SMPTE240M:
- transfers = V4L2_XFER_FUNC_SMPTE240M;
- matrix = V4L2_YCBCR_ENC_SMPTE240M;
- break;
- case V4L2_COLORSPACE_BT2020:
- transfers = V4L2_XFER_FUNC_709;
- matrix = V4L2_YCBCR_ENC_BT2020;
- break;
- default:
- transfers = V4L2_XFER_FUNC_DEFAULT;
- matrix = V4L2_YCBCR_ENC_DEFAULT;
- break;
- }
- full_range = V4L2_QUANTIZATION_LIM_RANGE;
-
- if (ptransfers)
- *ptransfers = transfers;
- if (pmatrix)
- *pmatrix = matrix;
- if (pfull_range)
- *pfull_range = full_range;
-
- return 0;
-}
diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c
index 940e5bda5fa3..497ae4e8a229 100644
--- a/drivers/media/platform/amphion/vpu_dbg.c
+++ b/drivers/media/platform/amphion/vpu_dbg.c
@@ -48,6 +48,7 @@ static char *vpu_stat_name[] = {
[VPU_BUF_STATE_READY] = "ready",
[VPU_BUF_STATE_SKIP] = "skip",
[VPU_BUF_STATE_ERROR] = "error",
+ [VPU_BUF_STATE_CHANGED] = "changed",
};
static inline const char *to_vpu_stat_name(int state)
@@ -164,6 +165,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data)
for (i = 0; i < vb2_get_num_buffers(vq); i++) {
struct vb2_buffer *vb;
struct vb2_v4l2_buffer *vbuf;
+ struct vpu_vb2_buffer *vpu_buf;
vb = vb2_get_buffer(vq, i);
if (!vb)
@@ -173,13 +175,24 @@ static int vpu_dbg_instance(struct seq_file *s, void *data)
continue;
vbuf = to_vb2_v4l2_buffer(vb);
+ vpu_buf = to_vpu_vb2_buffer(vbuf);
num = scnprintf(str, sizeof(str),
- "capture[%2d] state = %10s, %8s\n",
+ "capture[%2d] state = %10s, %8s",
i, vb2_stat_name[vb->state],
to_vpu_stat_name(vpu_get_buffer_state(vbuf)));
if (seq_write(s, str, num))
return 0;
+
+ if (vpu_buf->fs_id >= 0) {
+ num = scnprintf(str, sizeof(str), "; fs %d", vpu_buf->fs_id);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+
+ num = scnprintf(str, sizeof(str), "\n");
+ if (seq_write(s, str, num))
+ return 0;
}
num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence);
diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h
index 428d988cf2f7..f56245ae2205 100644
--- a/drivers/media/platform/amphion/vpu_defs.h
+++ b/drivers/media/platform/amphion/vpu_defs.h
@@ -134,6 +134,7 @@ struct vpu_dec_codec_info {
u32 decoded_height;
struct v4l2_fract frame_rate;
u32 dsp_asp_ratio;
+ u32 profile_idc;
u32 level_idc;
u32 bit_depth_luma;
u32 bit_depth_chroma;
@@ -147,6 +148,17 @@ struct vpu_dec_codec_info {
u32 mbi_size;
u32 dcp_size;
u32 stride;
+ union {
+ struct {
+ u32 constraint_set5_flag : 1;
+ u32 constraint_set4_flag : 1;
+ u32 constraint_set3_flag : 1;
+ u32 constraint_set2_flag : 1;
+ u32 constraint_set1_flag : 1;
+ u32 constraint_set0_flag : 1;
+ };
+ u32 constraint_set_flags;
+ };
};
struct vpu_dec_pic_info {
diff --git a/drivers/media/platform/amphion/vpu_helpers.c b/drivers/media/platform/amphion/vpu_helpers.c
index d12310af9ebc..886d5632388e 100644
--- a/drivers/media/platform/amphion/vpu_helpers.c
+++ b/drivers/media/platform/amphion/vpu_helpers.c
@@ -509,3 +509,126 @@ const char *vpu_codec_state_name(enum vpu_codec_state state)
}
return "<unknown>";
}
+
+struct codec_id_mapping {
+ u32 id;
+ u32 v4l2_id;
+};
+
+static struct codec_id_mapping h264_profiles[] = {
+ {66, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE},
+ {77, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN},
+ {88, V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED},
+ {100, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH}
+};
+
+static struct codec_id_mapping h264_levels[] = {
+ {10, V4L2_MPEG_VIDEO_H264_LEVEL_1_0},
+ {9, V4L2_MPEG_VIDEO_H264_LEVEL_1B},
+ {11, V4L2_MPEG_VIDEO_H264_LEVEL_1_1},
+ {12, V4L2_MPEG_VIDEO_H264_LEVEL_1_2},
+ {13, V4L2_MPEG_VIDEO_H264_LEVEL_1_3},
+ {20, V4L2_MPEG_VIDEO_H264_LEVEL_2_0},
+ {21, V4L2_MPEG_VIDEO_H264_LEVEL_2_1},
+ {22, V4L2_MPEG_VIDEO_H264_LEVEL_2_2},
+ {30, V4L2_MPEG_VIDEO_H264_LEVEL_3_0},
+ {31, V4L2_MPEG_VIDEO_H264_LEVEL_3_1},
+ {32, V4L2_MPEG_VIDEO_H264_LEVEL_3_2},
+ {40, V4L2_MPEG_VIDEO_H264_LEVEL_4_0},
+ {41, V4L2_MPEG_VIDEO_H264_LEVEL_4_1},
+ {42, V4L2_MPEG_VIDEO_H264_LEVEL_4_2},
+ {50, V4L2_MPEG_VIDEO_H264_LEVEL_5_0},
+ {51, V4L2_MPEG_VIDEO_H264_LEVEL_5_1},
+ {52, V4L2_MPEG_VIDEO_H264_LEVEL_5_2},
+ {60, V4L2_MPEG_VIDEO_H264_LEVEL_6_0},
+ {61, V4L2_MPEG_VIDEO_H264_LEVEL_6_1},
+ {62, V4L2_MPEG_VIDEO_H264_LEVEL_6_2}
+};
+
+static struct codec_id_mapping hevc_profiles[] = {
+ {1, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN},
+ {2, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10}
+};
+
+static struct codec_id_mapping hevc_levels[] = {
+ {30, V4L2_MPEG_VIDEO_HEVC_LEVEL_1},
+ {60, V4L2_MPEG_VIDEO_HEVC_LEVEL_2},
+ {63, V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1},
+ {90, V4L2_MPEG_VIDEO_HEVC_LEVEL_3},
+ {93, V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1},
+ {120, V4L2_MPEG_VIDEO_HEVC_LEVEL_4},
+ {123, V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1},
+ {150, V4L2_MPEG_VIDEO_HEVC_LEVEL_5},
+ {153, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1},
+ {156, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2},
+ {180, V4L2_MPEG_VIDEO_HEVC_LEVEL_6},
+ {183, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1},
+ {186, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2}
+};
+
+static u32 vpu_find_v4l2_id(u32 id, struct codec_id_mapping *array, u32 array_sz)
+{
+ u32 i;
+
+ if (!array || !array_sz)
+ return 0;
+
+ for (i = 0; i < array_sz; i++) {
+ if (id == array[i].id)
+ return array[i].v4l2_id;
+ }
+
+ return 0;
+}
+
+u32 vpu_get_h264_v4l2_profile(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ /*
+ * In H.264 Document section A.2.1.1 Constrained Baseline profile
+ * Conformance of a bitstream to the Constrained Baseline profile is indicated by
+ * profile_idc being equal to 66 with constraint_set1_flag being equal to 1.
+ */
+ if (hdr->profile_idc == 66 && hdr->constraint_set1_flag)
+ return V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE;
+
+ return vpu_find_v4l2_id(hdr->profile_idc, h264_profiles, ARRAY_SIZE(h264_profiles));
+}
+
+u32 vpu_get_h264_v4l2_level(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ /*
+ * In H.264 Document section 7.4.2.1.1 Sequence parameter set data semantics
+ * If profile_idc is equal to 66, 77, or 88 and level_idc is equal to 11,
+ * constraint_set3_flag equal to 1 indicates that the coded video sequence
+ * obeys all constraints specified in Annex A for level 1b
+ * and constraint_set3_flag equal to 0 indicates that the coded video sequence
+ * obeys all constraints specified in Annex A for level 1.1.
+ */
+ if (hdr->level_idc == 11 && hdr->constraint_set3_flag &&
+ (hdr->profile_idc == 66 || hdr->profile_idc == 77 || hdr->profile_idc == 88))
+ return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
+
+ return vpu_find_v4l2_id(hdr->level_idc, h264_levels, ARRAY_SIZE(h264_levels));
+}
+
+u32 vpu_get_hevc_v4l2_profile(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ return vpu_find_v4l2_id(hdr->profile_idc, hevc_profiles, ARRAY_SIZE(hevc_profiles));
+}
+
+u32 vpu_get_hevc_v4l2_level(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ return vpu_find_v4l2_id(hdr->level_idc, hevc_levels, ARRAY_SIZE(hevc_levels));
+}
diff --git a/drivers/media/platform/amphion/vpu_helpers.h b/drivers/media/platform/amphion/vpu_helpers.h
index 0eaddb07190d..76fba0d6090c 100644
--- a/drivers/media/platform/amphion/vpu_helpers.h
+++ b/drivers/media/platform/amphion/vpu_helpers.h
@@ -6,6 +6,8 @@
#ifndef _AMPHION_VPU_HELPERS_H
#define _AMPHION_VPU_HELPERS_H
+#include "vpu_defs.h"
+
struct vpu_pair {
u32 src;
u32 dst;
@@ -54,10 +56,6 @@ static inline u8 vpu_helper_read_byte(struct vpu_buffer *stream_buffer, u32 pos)
return pdata[pos % stream_buffer->length];
}
-int vpu_color_check_primaries(u32 primaries);
-int vpu_color_check_transfers(u32 transfers);
-int vpu_color_check_matrix(u32 matrix);
-int vpu_color_check_full_range(u32 full_range);
u32 vpu_color_cvrt_primaries_v2i(u32 primaries);
u32 vpu_color_cvrt_primaries_i2v(u32 primaries);
u32 vpu_color_cvrt_transfers_v2i(u32 transfers);
@@ -66,8 +64,12 @@ u32 vpu_color_cvrt_matrix_v2i(u32 matrix);
u32 vpu_color_cvrt_matrix_i2v(u32 matrix);
u32 vpu_color_cvrt_full_range_v2i(u32 full_range);
u32 vpu_color_cvrt_full_range_i2v(u32 full_range);
-int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range);
int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src);
int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst);
+
+u32 vpu_get_h264_v4l2_profile(struct vpu_dec_codec_info *hdr);
+u32 vpu_get_h264_v4l2_level(struct vpu_dec_codec_info *hdr);
+u32 vpu_get_hevc_v4l2_profile(struct vpu_dec_codec_info *hdr);
+u32 vpu_get_hevc_v4l2_level(struct vpu_dec_codec_info *hdr);
#endif
diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c
index feca7d4220ed..ba688566dffd 100644
--- a/drivers/media/platform/amphion/vpu_malone.c
+++ b/drivers/media/platform/amphion/vpu_malone.c
@@ -908,7 +908,8 @@ static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt,
info->frame_rate.numerator = 1000;
info->frame_rate.denominator = pkt->data[8];
info->dsp_asp_ratio = pkt->data[9];
- info->level_idc = pkt->data[10];
+ info->profile_idc = (pkt->data[10] >> 8) & 0xff;
+ info->level_idc = pkt->data[10] & 0xff;
info->bit_depth_luma = pkt->data[13];
info->bit_depth_chroma = pkt->data[14];
info->chroma_fmt = pkt->data[15];
@@ -925,6 +926,8 @@ static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt,
info->pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128;
else
info->pixfmt = V4L2_PIX_FMT_NV12M_8L128;
+ if (pkt->hdr.num > 28)
+ info->constraint_set_flags = pkt->data[28];
if (info->frame_rate.numerator && info->frame_rate.denominator) {
unsigned long n, d;
diff --git a/drivers/media/platform/amphion/vpu_mbox.c b/drivers/media/platform/amphion/vpu_mbox.c
index c2963b8deb48..b2ac8de6a2d9 100644
--- a/drivers/media/platform/amphion/vpu_mbox.c
+++ b/drivers/media/platform/amphion/vpu_mbox.c
@@ -109,7 +109,3 @@ void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data)
mbox_send_message(core->tx_data.ch, &data);
mbox_send_message(core->tx_type.ch, &type);
}
-
-void vpu_mbox_enable_rx(struct vpu_dev *dev)
-{
-}
diff --git a/drivers/media/platform/amphion/vpu_mbox.h b/drivers/media/platform/amphion/vpu_mbox.h
index 79cfd874e92b..8b7aea4f606c 100644
--- a/drivers/media/platform/amphion/vpu_mbox.h
+++ b/drivers/media/platform/amphion/vpu_mbox.h
@@ -11,6 +11,5 @@ int vpu_mbox_request(struct vpu_core *core);
void vpu_mbox_free(struct vpu_core *core);
void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data);
void vpu_mbox_send_type(struct vpu_core *core, u32 type);
-void vpu_mbox_enable_rx(struct vpu_dev *dev);
#endif
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index 45707931bc4f..74668fa362e2 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -501,14 +501,25 @@ static int vpu_vb2_queue_setup(struct vb2_queue *vq,
call_void_vop(inst, release);
}
+ if (V4L2_TYPE_IS_CAPTURE(vq->type))
+ call_void_vop(inst, reset_frame_store);
+
return 0;
}
static int vpu_vb2_buf_init(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+ struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ vpu_buf->fs_id = -1;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+
+ if (!inst->ops->attach_frame_store || V4L2_TYPE_IS_OUTPUT(vb->type))
+ return 0;
+
+ call_void_vop(inst, attach_frame_store, vb);
return 0;
}
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index cebcae196eec..7f1ce95cdc3f 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -57,6 +57,25 @@
#define CSI2RX_LANES_MAX 4
#define CSI2RX_STREAMS_MAX 4
+#define CSI2RX_ERROR_IRQS_REG 0x28
+#define CSI2RX_ERROR_IRQS_MASK_REG 0x2C
+
+#define CSI2RX_STREAM3_FIFO_OVERFLOW_IRQ BIT(19)
+#define CSI2RX_STREAM2_FIFO_OVERFLOW_IRQ BIT(18)
+#define CSI2RX_STREAM1_FIFO_OVERFLOW_IRQ BIT(17)
+#define CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ BIT(16)
+#define CSI2RX_FRONT_TRUNC_HDR_IRQ BIT(12)
+#define CSI2RX_PROT_TRUNCATED_PACKET_IRQ BIT(11)
+#define CSI2RX_FRONT_LP_NO_PAYLOAD_IRQ BIT(10)
+#define CSI2RX_SP_INVALID_RCVD_IRQ BIT(9)
+#define CSI2RX_DATA_ID_IRQ BIT(7)
+#define CSI2RX_HEADER_CORRECTED_ECC_IRQ BIT(6)
+#define CSI2RX_HEADER_ECC_IRQ BIT(5)
+#define CSI2RX_PAYLOAD_CRC_IRQ BIT(4)
+
+#define CSI2RX_ECC_ERRORS GENMASK(7, 4)
+#define CSI2RX_PACKET_ERRORS GENMASK(12, 9)
+
enum csi2rx_pads {
CSI2RX_PAD_SINK,
CSI2RX_PAD_SOURCE_STREAM0,
@@ -71,9 +90,32 @@ struct csi2rx_fmt {
u8 bpp;
};
+struct csi2rx_event {
+ u32 mask;
+ const char *name;
+};
+
+static const struct csi2rx_event csi2rx_events[] = {
+ { CSI2RX_STREAM3_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 3 FIFO detected" },
+ { CSI2RX_STREAM2_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 2 FIFO detected" },
+ { CSI2RX_STREAM1_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 1 FIFO detected" },
+ { CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 0 FIFO detected" },
+ { CSI2RX_FRONT_TRUNC_HDR_IRQ, "A truncated header [short or long] has been received" },
+ { CSI2RX_PROT_TRUNCATED_PACKET_IRQ, "A truncated long packet has been received" },
+ { CSI2RX_FRONT_LP_NO_PAYLOAD_IRQ, "A truncated long packet has been received. No payload" },
+ { CSI2RX_SP_INVALID_RCVD_IRQ, "A reserved or invalid short packet has been received" },
+ { CSI2RX_DATA_ID_IRQ, "Data ID error in the header packet" },
+ { CSI2RX_HEADER_CORRECTED_ECC_IRQ, "ECC error detected and corrected" },
+ { CSI2RX_HEADER_ECC_IRQ, "Unrecoverable ECC error" },
+ { CSI2RX_PAYLOAD_CRC_IRQ, "CRC error" },
+};
+
+#define CSI2RX_NUM_EVENTS ARRAY_SIZE(csi2rx_events)
+
struct csi2rx_priv {
struct device *dev;
unsigned int count;
+ int error_irq;
/*
* Used to prevent race conditions between multiple,
@@ -95,6 +137,7 @@ struct csi2rx_priv {
u8 max_lanes;
u8 max_streams;
bool has_internal_dphy;
+ u32 events[CSI2RX_NUM_EVENTS];
struct v4l2_subdev subdev;
struct v4l2_async_notifier notifier;
@@ -124,6 +167,54 @@ static const struct csi2rx_fmt formats[] = {
{ .code = MEDIA_BUS_FMT_BGR888_1X24, .bpp = 24, },
};
+static void csi2rx_configure_error_irq_mask(void __iomem *base,
+ struct csi2rx_priv *csi2rx)
+{
+ u32 error_irq_mask = 0;
+
+ error_irq_mask |= CSI2RX_ECC_ERRORS;
+ error_irq_mask |= CSI2RX_PACKET_ERRORS;
+
+ /*
+ * Iterate through all source pads and check if they are linked
+ * to an active remote pad. If an active remote pad is found,
+ * calculate the corresponding bit position and set it in
+ * mask, enabling the stream overflow error in the mask.
+ */
+ for (int i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) {
+ struct media_pad *remote_pad;
+
+ remote_pad = media_pad_remote_pad_first(&csi2rx->pads[i]);
+ if (remote_pad) {
+ int pad = i - CSI2RX_PAD_SOURCE_STREAM0;
+ u32 bit_mask = CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ << pad;
+
+ error_irq_mask |= bit_mask;
+ }
+ }
+
+ writel(error_irq_mask, base + CSI2RX_ERROR_IRQS_MASK_REG);
+}
+
+static irqreturn_t csi2rx_irq_handler(int irq, void *dev_id)
+{
+ struct csi2rx_priv *csi2rx = dev_id;
+ int i;
+ u32 error_status, error_mask;
+
+ error_status = readl(csi2rx->base + CSI2RX_ERROR_IRQS_REG);
+ error_mask = readl(csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG);
+
+ for (i = 0; i < CSI2RX_NUM_EVENTS; i++)
+ if ((error_status & csi2rx_events[i].mask) &&
+ (error_mask & csi2rx_events[i].mask))
+ csi2rx->events[i]++;
+
+ writel(error_status, csi2rx->base + CSI2RX_ERROR_IRQS_REG);
+
+ return IRQ_HANDLED;
+}
+
static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code)
{
unsigned int i;
@@ -220,6 +311,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
reset_control_deassert(csi2rx->p_rst);
csi2rx_reset(csi2rx);
+ if (csi2rx->error_irq >= 0)
+ csi2rx_configure_error_irq_mask(csi2rx->base, csi2rx);
+
reg = csi2rx->num_lanes << 8;
for (i = 0; i < csi2rx->num_lanes; i++) {
reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]);
@@ -332,6 +426,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
reset_control_assert(csi2rx->sys_rst);
clk_disable_unprepare(csi2rx->sys_clk);
+ writel(0, csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG);
+
for (i = 0; i < csi2rx->max_streams; i++) {
writel(CSI2RX_STREAM_CTRL_STOP,
csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
@@ -363,6 +459,21 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
}
}
+static int csi2rx_log_status(struct v4l2_subdev *sd)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(sd);
+ unsigned int i;
+
+ for (i = 0; i < CSI2RX_NUM_EVENTS; i++) {
+ if (csi2rx->events[i])
+ dev_info(csi2rx->dev, "%s events: %d\n",
+ csi2rx_events[i].name,
+ csi2rx->events[i]);
+ }
+
+ return 0;
+}
+
static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
@@ -468,7 +579,12 @@ static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
.s_stream = csi2rx_s_stream,
};
+static const struct v4l2_subdev_core_ops csi2rx_core_ops = {
+ .log_status = csi2rx_log_status,
+};
+
static const struct v4l2_subdev_ops csi2rx_subdev_ops = {
+ .core = &csi2rx_core_ops,
.video = &csi2rx_video_ops,
.pad = &csi2rx_pad_ops,
};
@@ -705,6 +821,21 @@ static int csi2rx_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup;
+ csi2rx->error_irq = platform_get_irq_byname_optional(pdev, "error_irq");
+
+ if (csi2rx->error_irq < 0) {
+ dev_dbg(csi2rx->dev, "Optional interrupt not defined, proceeding without it\n");
+ } else {
+ ret = devm_request_irq(csi2rx->dev, csi2rx->error_irq,
+ csi2rx_irq_handler, 0,
+ dev_name(&pdev->dev), csi2rx);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Unable to request interrupt: %d\n", ret);
+ goto err_cleanup;
+ }
+ }
+
ret = v4l2_subdev_init_finalize(&csi2rx->subdev);
if (ret)
goto err_cleanup;
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 5c17bc58181e..8681dd193033 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -598,6 +598,27 @@ static void _bswap16(u16 *a)
*a = ((*a & 0x00FF) << 8) | ((*a & 0xFF00) >> 8);
}
+static dma_addr_t mxc_jpeg_get_plane_dma_addr(struct vb2_buffer *buf, unsigned int plane_no)
+{
+ if (plane_no >= buf->num_planes)
+ return 0;
+ return vb2_dma_contig_plane_dma_addr(buf, plane_no) + buf->planes[plane_no].data_offset;
+}
+
+static void *mxc_jpeg_get_plane_vaddr(struct vb2_buffer *buf, unsigned int plane_no)
+{
+ if (plane_no >= buf->num_planes)
+ return NULL;
+ return vb2_plane_vaddr(buf, plane_no) + buf->planes[plane_no].data_offset;
+}
+
+static unsigned long mxc_jpeg_get_plane_payload(struct vb2_buffer *buf, unsigned int plane_no)
+{
+ if (plane_no >= buf->num_planes)
+ return 0;
+ return vb2_get_plane_payload(buf, plane_no) - buf->planes[plane_no].data_offset;
+}
+
static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf,
unsigned long len)
{
@@ -610,11 +631,11 @@ static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf,
return;
for (plane_no = 0; plane_no < buf->num_planes; plane_no++) {
- payload = vb2_get_plane_payload(buf, plane_no);
+ payload = mxc_jpeg_get_plane_payload(buf, plane_no);
if (len == 0)
len = payload;
- dma_addr = vb2_dma_contig_plane_dma_addr(buf, plane_no);
- vaddr = vb2_plane_vaddr(buf, plane_no);
+ dma_addr = mxc_jpeg_get_plane_dma_addr(buf, plane_no);
+ vaddr = mxc_jpeg_get_plane_vaddr(buf, plane_no);
v4l2_dbg(3, debug, &jpeg->v4l2_dev,
"plane %d (vaddr=%p dma_addr=%x payload=%ld):",
plane_no, vaddr, dma_addr, payload);
@@ -712,16 +733,15 @@ static void mxc_jpeg_addrs(struct mxc_jpeg_desc *desc,
struct mxc_jpeg_q_data *q_data;
q_data = mxc_jpeg_get_q_data(ctx, raw_buf->type);
- desc->buf_base0 = vb2_dma_contig_plane_dma_addr(raw_buf, 0);
+ desc->buf_base0 = mxc_jpeg_get_plane_dma_addr(raw_buf, 0);
desc->buf_base1 = 0;
if (img_fmt == STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV420)) {
if (raw_buf->num_planes == 2)
- desc->buf_base1 = vb2_dma_contig_plane_dma_addr(raw_buf, 1);
+ desc->buf_base1 = mxc_jpeg_get_plane_dma_addr(raw_buf, 1);
else
desc->buf_base1 = desc->buf_base0 + q_data->sizeimage[0];
}
- desc->stm_bufbase = vb2_dma_contig_plane_dma_addr(jpeg_buf, 0) +
- offset;
+ desc->stm_bufbase = mxc_jpeg_get_plane_dma_addr(jpeg_buf, 0) + offset;
}
static bool mxc_jpeg_is_extended_sequential(const struct mxc_jpeg_fmt *fmt)
@@ -1029,8 +1049,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
vb2_set_plane_payload(&dst_buf->vb2_buf, 1, payload);
}
dev_dbg(dev, "Decoding finished, payload size: %ld + %ld\n",
- vb2_get_plane_payload(&dst_buf->vb2_buf, 0),
- vb2_get_plane_payload(&dst_buf->vb2_buf, 1));
+ mxc_jpeg_get_plane_payload(&dst_buf->vb2_buf, 0),
+ mxc_jpeg_get_plane_payload(&dst_buf->vb2_buf, 1));
}
/* short preview of the results */
@@ -1889,8 +1909,8 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb)
struct mxc_jpeg_sof *psof = NULL;
struct mxc_jpeg_sos *psos = NULL;
struct mxc_jpeg_src_buf *jpeg_src_buf = vb2_to_mxc_buf(vb);
- u8 *src_addr = (u8 *)vb2_plane_vaddr(vb, 0);
- u32 size = vb2_get_plane_payload(vb, 0);
+ u8 *src_addr = (u8 *)mxc_jpeg_get_plane_vaddr(vb, 0);
+ u32 size = mxc_jpeg_get_plane_payload(vb, 0);
int ret;
memset(&header, 0, sizeof(header));
@@ -2027,6 +2047,11 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb)
i, vb2_plane_size(vb, i), sizeimage);
return -EINVAL;
}
+ if (!IS_ALIGNED(mxc_jpeg_get_plane_dma_addr(vb, i), MXC_JPEG_ADDR_ALIGNMENT)) {
+ dev_err(dev, "planes[%d] address is not %d aligned\n",
+ i, MXC_JPEG_ADDR_ALIGNMENT);
+ return -EINVAL;
+ }
}
if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
vb2_set_plane_payload(vb, 0, 0);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index fdde45f7e163..44e46face6d1 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -30,6 +30,7 @@
#define MXC_JPEG_MAX_PLANES 2
#define MXC_JPEG_PATTERN_WIDTH 128
#define MXC_JPEG_PATTERN_HEIGHT 64
+#define MXC_JPEG_ADDR_ALIGNMENT 16
enum mxc_jpeg_enc_state {
MXC_JPEG_ENCODING = 0, /* jpeg encode phase */
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index d060eadebc7a..2beb5f43c2c0 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -28,6 +28,7 @@
#include <linux/reset.h>
#include <linux/spinlock.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -229,25 +230,6 @@
#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
-/* MIPI CSI-2 Data Types */
-#define MIPI_CSI2_DATA_TYPE_YUV420_8 0x18
-#define MIPI_CSI2_DATA_TYPE_YUV420_10 0x19
-#define MIPI_CSI2_DATA_TYPE_LE_YUV420_8 0x1a
-#define MIPI_CSI2_DATA_TYPE_CS_YUV420_8 0x1c
-#define MIPI_CSI2_DATA_TYPE_CS_YUV420_10 0x1d
-#define MIPI_CSI2_DATA_TYPE_YUV422_8 0x1e
-#define MIPI_CSI2_DATA_TYPE_YUV422_10 0x1f
-#define MIPI_CSI2_DATA_TYPE_RGB565 0x22
-#define MIPI_CSI2_DATA_TYPE_RGB666 0x23
-#define MIPI_CSI2_DATA_TYPE_RGB888 0x24
-#define MIPI_CSI2_DATA_TYPE_RAW6 0x28
-#define MIPI_CSI2_DATA_TYPE_RAW7 0x29
-#define MIPI_CSI2_DATA_TYPE_RAW8 0x2a
-#define MIPI_CSI2_DATA_TYPE_RAW10 0x2b
-#define MIPI_CSI2_DATA_TYPE_RAW12 0x2c
-#define MIPI_CSI2_DATA_TYPE_RAW14 0x2d
-#define MIPI_CSI2_DATA_TYPE_USER(x) (0x30 + (x))
-
struct mipi_csis_event {
bool debug;
u32 mask;
@@ -357,116 +339,116 @@ static const struct csis_pix_format mipi_csis_formats[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_1X16,
.output = MEDIA_BUS_FMT_UYVY8_1X16,
- .data_type = MIPI_CSI2_DATA_TYPE_YUV422_8,
+ .data_type = MIPI_CSI2_DT_YUV422_8B,
.width = 16,
},
/* RGB formats. */
{
.code = MEDIA_BUS_FMT_RGB565_1X16,
.output = MEDIA_BUS_FMT_RGB565_1X16,
- .data_type = MIPI_CSI2_DATA_TYPE_RGB565,
+ .data_type = MIPI_CSI2_DT_RGB565,
.width = 16,
}, {
.code = MEDIA_BUS_FMT_BGR888_1X24,
.output = MEDIA_BUS_FMT_RGB888_1X24,
- .data_type = MIPI_CSI2_DATA_TYPE_RGB888,
+ .data_type = MIPI_CSI2_DT_RGB888,
.width = 24,
},
/* RAW (Bayer and greyscale) formats. */
{
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
.output = MEDIA_BUS_FMT_SBGGR8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
.output = MEDIA_BUS_FMT_SGBRG8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
.output = MEDIA_BUS_FMT_SGRBG8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
.output = MEDIA_BUS_FMT_SRGGB8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_Y8_1X8,
.output = MEDIA_BUS_FMT_Y8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
.output = MEDIA_BUS_FMT_SBGGR10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SGBRG10_1X10,
.output = MEDIA_BUS_FMT_SGBRG10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SGRBG10_1X10,
.output = MEDIA_BUS_FMT_SGRBG10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SRGGB10_1X10,
.output = MEDIA_BUS_FMT_SRGGB10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_Y10_1X10,
.output = MEDIA_BUS_FMT_Y10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SBGGR12_1X12,
.output = MEDIA_BUS_FMT_SBGGR12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SGBRG12_1X12,
.output = MEDIA_BUS_FMT_SGBRG12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SGRBG12_1X12,
.output = MEDIA_BUS_FMT_SGRBG12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SRGGB12_1X12,
.output = MEDIA_BUS_FMT_SRGGB12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_Y12_1X12,
.output = MEDIA_BUS_FMT_Y12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SBGGR14_1X14,
.output = MEDIA_BUS_FMT_SBGGR14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
}, {
.code = MEDIA_BUS_FMT_SGBRG14_1X14,
.output = MEDIA_BUS_FMT_SGBRG14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
}, {
.code = MEDIA_BUS_FMT_SGRBG14_1X14,
.output = MEDIA_BUS_FMT_SGRBG14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
}, {
.code = MEDIA_BUS_FMT_SRGGB14_1X14,
.output = MEDIA_BUS_FMT_SRGGB14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
},
/* JPEG */
@@ -494,7 +476,7 @@ static const struct csis_pix_format mipi_csis_formats[] = {
* SoC that can support quad pixel mode, this will have to be
* revisited.
*/
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}
};
@@ -583,7 +565,7 @@ static void __mipi_csis_set_format(struct mipi_csis_device *csis,
*
* TODO: Verify which other formats require DUAL (or QUAD) modes.
*/
- if (csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8)
+ if (csis_fmt->data_type == MIPI_CSI2_DT_YUV422_8B)
val |= MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL;
val |= MIPI_CSIS_ISPCFG_FMT(csis_fmt->data_type);
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 1e79b1211b60..981648a03113 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -3,6 +3,7 @@
* Copyright 2019-2020 NXP
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -245,26 +246,41 @@ static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi)
/* Panic will assert when the buffers are 50% full */
-/* For i.MX8QXP C0 and i.MX8MN ISI IER version */
+/* For i.MX8MN ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v1 = {
- .oflw_y_buf_en = { .offset = 19, .mask = 0x80000 },
- .oflw_u_buf_en = { .offset = 21, .mask = 0x200000 },
- .oflw_v_buf_en = { .offset = 23, .mask = 0x800000 },
+ .oflw_y_buf_en = { .mask = BIT(19) },
+ .oflw_u_buf_en = { .mask = BIT(21) },
+ .oflw_v_buf_en = { .mask = BIT(23) },
- .panic_y_buf_en = {.offset = 20, .mask = 0x100000 },
- .panic_u_buf_en = {.offset = 22, .mask = 0x400000 },
- .panic_v_buf_en = {.offset = 24, .mask = 0x1000000 },
+ .panic_y_buf_en = { .mask = BIT(20) },
+ .panic_u_buf_en = { .mask = BIT(22) },
+ .panic_v_buf_en = { .mask = BIT(24) },
};
-/* For i.MX8MP ISI IER version */
+/* For i.MX8QXP C0 and i.MX8MP ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v2 = {
- .oflw_y_buf_en = { .offset = 18, .mask = 0x40000 },
- .oflw_u_buf_en = { .offset = 20, .mask = 0x100000 },
- .oflw_v_buf_en = { .offset = 22, .mask = 0x400000 },
+ .oflw_y_buf_en = { .mask = BIT(18) },
+ .oflw_u_buf_en = { .mask = BIT(20) },
+ .oflw_v_buf_en = { .mask = BIT(22) },
- .panic_y_buf_en = {.offset = 19, .mask = 0x80000 },
- .panic_u_buf_en = {.offset = 21, .mask = 0x200000 },
- .panic_v_buf_en = {.offset = 23, .mask = 0x800000 },
+ .panic_y_buf_en = { .mask = BIT(19) },
+ .panic_u_buf_en = { .mask = BIT(21) },
+ .panic_v_buf_en = { .mask = BIT(23) },
+};
+
+/* For i.MX8QM ISI IER version */
+static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_qm = {
+ .oflw_y_buf_en = { .mask = BIT(16) },
+ .oflw_u_buf_en = { .mask = BIT(19) },
+ .oflw_v_buf_en = { .mask = BIT(22) },
+
+ .excs_oflw_y_buf_en = { .mask = BIT(17) },
+ .excs_oflw_u_buf_en = { .mask = BIT(20) },
+ .excs_oflw_v_buf_en = { .mask = BIT(23) },
+
+ .panic_y_buf_en = { .mask = BIT(18) },
+ .panic_u_buf_en = { .mask = BIT(21) },
+ .panic_v_buf_en = { .mask = BIT(24) },
};
/* Panic will assert when the buffers are 50% full */
@@ -274,11 +290,6 @@ static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1 = {
.panic_set_thd_v = { .mask = 0xf0000, .offset = 16, .threshold = 0x7 },
};
-static const struct clk_bulk_data mxc_imx8mn_clks[] = {
- { .id = "axi" },
- { .id = "apb" },
-};
-
static const struct mxc_isi_plat_data mxc_imx8mn_data = {
.model = MXC_ISI_IMX8MN,
.num_ports = 1,
@@ -286,8 +297,6 @@ static const struct mxc_isi_plat_data mxc_imx8mn_data = {
.reg_offset = 0,
.ier_reg = &mxc_imx8_isi_ier_v1,
.set_thd = &mxc_imx8_isi_thd_v1,
- .clks = mxc_imx8mn_clks,
- .num_clks = ARRAY_SIZE(mxc_imx8mn_clks),
.buf_active_reverse = false,
.gasket_ops = &mxc_imx8_gasket_ops,
.has_36bit_dma = false,
@@ -300,8 +309,6 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = {
.reg_offset = 0x2000,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
- .clks = mxc_imx8mn_clks,
- .num_clks = ARRAY_SIZE(mxc_imx8mn_clks),
.buf_active_reverse = true,
.gasket_ops = &mxc_imx8_gasket_ops,
.has_36bit_dma = true,
@@ -314,8 +321,6 @@ static const struct mxc_isi_plat_data mxc_imx8ulp_data = {
.reg_offset = 0x0,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
- .clks = mxc_imx8mn_clks,
- .num_clks = ARRAY_SIZE(mxc_imx8mn_clks),
.buf_active_reverse = true,
.has_36bit_dma = false,
};
@@ -327,13 +332,33 @@ static const struct mxc_isi_plat_data mxc_imx93_data = {
.reg_offset = 0,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
- .clks = mxc_imx8mn_clks,
- .num_clks = ARRAY_SIZE(mxc_imx8mn_clks),
.buf_active_reverse = true,
.gasket_ops = &mxc_imx93_gasket_ops,
.has_36bit_dma = false,
};
+static const struct mxc_isi_plat_data mxc_imx8qm_data = {
+ .model = MXC_ISI_IMX8QM,
+ .num_ports = 5,
+ .num_channels = 8,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_qm,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
+static const struct mxc_isi_plat_data mxc_imx8qxp_data = {
+ .model = MXC_ISI_IMX8QXP,
+ .num_ports = 5,
+ .num_channels = 6,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_v2,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
/* -----------------------------------------------------------------------------
* Power management
*/
@@ -385,7 +410,7 @@ static int mxc_isi_runtime_suspend(struct device *dev)
{
struct mxc_isi_dev *isi = dev_get_drvdata(dev);
- clk_bulk_disable_unprepare(isi->pdata->num_clks, isi->clks);
+ clk_bulk_disable_unprepare(isi->num_clks, isi->clks);
return 0;
}
@@ -395,7 +420,7 @@ static int mxc_isi_runtime_resume(struct device *dev)
struct mxc_isi_dev *isi = dev_get_drvdata(dev);
int ret;
- ret = clk_bulk_prepare_enable(isi->pdata->num_clks, isi->clks);
+ ret = clk_bulk_prepare_enable(isi->num_clks, isi->clks);
if (ret) {
dev_err(dev, "Failed to enable clocks (%d)\n", ret);
return ret;
@@ -413,27 +438,6 @@ static const struct dev_pm_ops mxc_isi_pm_ops = {
* Probe, remove & driver
*/
-static int mxc_isi_clk_get(struct mxc_isi_dev *isi)
-{
- unsigned int size = isi->pdata->num_clks
- * sizeof(*isi->clks);
- int ret;
-
- isi->clks = devm_kmemdup(isi->dev, isi->pdata->clks, size, GFP_KERNEL);
- if (!isi->clks)
- return -ENOMEM;
-
- ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks,
- isi->clks);
- if (ret < 0) {
- dev_err(isi->dev, "Failed to acquire clocks: %d\n",
- ret);
- return ret;
- }
-
- return 0;
-}
-
static int mxc_isi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -456,34 +460,25 @@ static int mxc_isi_probe(struct platform_device *pdev)
if (!isi->pipes)
return -ENOMEM;
- ret = mxc_isi_clk_get(isi);
- if (ret < 0) {
- dev_err(dev, "Failed to get clocks\n");
- return ret;
- }
+ isi->num_clks = devm_clk_bulk_get_all(dev, &isi->clks);
+ if (isi->num_clks < 0)
+ return dev_err_probe(dev, isi->num_clks, "Failed to get clocks\n");
isi->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(isi->regs)) {
- dev_err(dev, "Failed to get ISI register map\n");
- return PTR_ERR(isi->regs);
- }
+ if (IS_ERR(isi->regs))
+ return dev_err_probe(dev, PTR_ERR(isi->regs),
+ "Failed to get ISI register map\n");
if (isi->pdata->gasket_ops) {
isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node,
"fsl,blk-ctrl");
- if (IS_ERR(isi->gasket)) {
- ret = PTR_ERR(isi->gasket);
- dev_err(dev, "failed to get gasket: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(isi->gasket))
+ return dev_err_probe(dev, PTR_ERR(isi->gasket),
+ "failed to get gasket\n");
}
dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
- if (ret) {
- dev_err(dev, "failed to set DMA mask\n");
- return ret;
- }
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
pm_runtime_enable(dev);
@@ -541,6 +536,8 @@ static void mxc_isi_remove(struct platform_device *pdev)
static const struct of_device_id mxc_isi_of_match[] = {
{ .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data },
{ .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data },
+ { .compatible = "fsl,imx8qm-isi", .data = &mxc_imx8qm_data },
+ { .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data },
{ .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data },
{ .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data },
{ /* sentinel */ },
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 9c7fe9e5f941..206995bedca4 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -114,7 +114,6 @@ struct mxc_isi_buffer {
};
struct mxc_isi_reg {
- u32 offset;
u32 mask;
};
@@ -158,6 +157,8 @@ struct mxc_gasket_ops {
enum model {
MXC_ISI_IMX8MN,
MXC_ISI_IMX8MP,
+ MXC_ISI_IMX8QM,
+ MXC_ISI_IMX8QXP,
MXC_ISI_IMX8ULP,
MXC_ISI_IMX93,
};
@@ -170,8 +171,6 @@ struct mxc_isi_plat_data {
const struct mxc_isi_ier_reg *ier_reg;
const struct mxc_isi_set_thd *set_thd;
const struct mxc_gasket_ops *gasket_ops;
- const struct clk_bulk_data *clks;
- unsigned int num_clks;
bool buf_active_reverse;
bool has_36bit_dma;
};
@@ -283,6 +282,7 @@ struct mxc_isi_dev {
void __iomem *regs;
struct clk_bulk_data *clks;
+ int num_clks;
struct regmap *gasket;
struct mxc_isi_crossbar crossbar;
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 93a55c97cd17..ede6cc74c023 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -188,11 +188,12 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd,
* Create a 1:1 mapping between pixel link inputs and outputs to
* pipelines by default.
*/
- routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL);
+ routing.num_routes = min(xbar->num_sinks - 1, xbar->num_sources);
+ routes = kcalloc(routing.num_routes, sizeof(*routes), GFP_KERNEL);
if (!routes)
return -ENOMEM;
- for (i = 0; i < xbar->num_sources; ++i) {
+ for (i = 0; i < routing.num_routes; ++i) {
struct v4l2_subdev_route *route = &routes[i];
route->sink_pad = i;
@@ -200,7 +201,6 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd,
route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
}
- routing.num_routes = xbar->num_sources;
routing.routes = routes;
ret = __mxc_isi_crossbar_set_routing(sd, state, &routing);
@@ -352,9 +352,8 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
sink_streams);
if (ret) {
dev_err(xbar->isi->dev,
- "failed to %s streams 0x%llx on '%s':%u: %d\n",
- "enable", sink_streams, remote_sd->name,
- remote_pad, ret);
+ "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;
}
@@ -392,9 +391,8 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
sink_streams);
if (ret)
dev_err(xbar->isi->dev,
- "failed to %s streams 0x%llx on '%s':%u: %d\n",
- "disable", sink_streams, remote_sd->name,
- remote_pad, ret);
+ "failed to disable streams 0x%llx on '%s':%u: %d\n",
+ sink_streams, remote_sd->name, remote_pad, ret);
mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
}
@@ -453,7 +451,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
* the memory input.
*/
xbar->num_sinks = isi->pdata->num_ports + 1;
- xbar->num_sources = isi->pdata->num_ports;
+ xbar->num_sources = isi->pdata->num_channels;
num_pads = xbar->num_sinks + xbar->num_sources;
xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL);
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index a8bcf60e2f37..3a4645f59a44 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -5,6 +5,7 @@
* Copyright (C) 2021 Purism SPC
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@@ -62,6 +63,8 @@
#define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL 0x188
#define CSI2RX_CFG_DISABLE_PAYLOAD_1 0x130
+struct csi_state;
+
enum {
ST_POWERED = 1,
ST_STREAMING = 2,
@@ -83,11 +86,11 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
#define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
-#define GPR_CSI2_1_RX_ENABLE BIT(13)
-#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
-#define GPR_CSI2_1_HSEL BIT(10)
-#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
-#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2)
+struct imx8mq_plat_data {
+ int (*enable)(struct csi_state *state, u32 hs_settle);
+ void (*disable)(struct csi_state *state);
+ bool use_reg_csr;
+};
/*
* The send level configures the number of entries that must accumulate in
@@ -106,6 +109,7 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
struct csi_state {
struct device *dev;
+ const struct imx8mq_plat_data *pdata;
void __iomem *regs;
struct clk_bulk_data clks[CSI2_NUM_CLKS];
struct reset_control *rst;
@@ -137,6 +141,123 @@ struct csi2_pix_format {
u8 width;
};
+/* -----------------------------------------------------------------------------
+ * i.MX8MQ GPR
+ */
+
+#define GPR_CSI2_1_RX_ENABLE BIT(13)
+#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
+#define GPR_CSI2_1_HSEL BIT(10)
+#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
+#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2)
+
+static int imx8mq_gpr_enable(struct csi_state *state, u32 hs_settle)
+{
+ regmap_update_bits(state->phy_gpr,
+ state->phy_gpr_reg,
+ 0x3fff,
+ GPR_CSI2_1_RX_ENABLE |
+ GPR_CSI2_1_VID_INTFC_ENB |
+ GPR_CSI2_1_HSEL |
+ GPR_CSI2_1_CONT_CLK_MODE |
+ GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle));
+
+ return 0;
+}
+
+static const struct imx8mq_plat_data imx8mq_data = {
+ .enable = imx8mq_gpr_enable,
+};
+
+/* -----------------------------------------------------------------------------
+ * i.MX8QXP
+ */
+
+#define CSI2SS_PL_CLK_INTERVAL_US 100
+#define CSI2SS_PL_CLK_TIMEOUT_US 100000
+
+#define CSI2SS_PLM_CTRL 0x0
+#define CSI2SS_PLM_CTRL_ENABLE_PL BIT(0)
+#define CSI2SS_PLM_CTRL_VSYNC_OVERRIDE BIT(9)
+#define CSI2SS_PLM_CTRL_HSYNC_OVERRIDE BIT(10)
+#define CSI2SS_PLM_CTRL_VALID_OVERRIDE BIT(11)
+#define CSI2SS_PLM_CTRL_POLARITY_HIGH BIT(12)
+#define CSI2SS_PLM_CTRL_PL_CLK_RUN BIT(31)
+
+#define CSI2SS_PHY_CTRL 0x4
+#define CSI2SS_PHY_CTRL_RX_ENABLE BIT(0)
+#define CSI2SS_PHY_CTRL_AUTO_PD_EN BIT(1)
+#define CSI2SS_PHY_CTRL_DDRCLK_EN BIT(2)
+#define CSI2SS_PHY_CTRL_CONT_CLK_MODE BIT(3)
+#define CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK GENMASK(9, 4)
+#define CSI2SS_PHY_CTRL_RTERM_SEL BIT(21)
+#define CSI2SS_PHY_CTRL_PD BIT(22)
+
+#define CSI2SS_DATA_TYPE_DISABLE_BF 0x38
+#define CSI2SS_DATA_TYPE_DISABLE_BF_MASK GENMASK(23, 0)
+
+#define CSI2SS_CTRL_CLK_RESET 0x44
+#define CSI2SS_CTRL_CLK_RESET_EN BIT(0)
+
+static int imx8qxp_gpr_enable(struct csi_state *state, u32 hs_settle)
+{
+ int ret;
+ u32 val;
+
+ /* Clear format */
+ regmap_clear_bits(state->phy_gpr, CSI2SS_DATA_TYPE_DISABLE_BF,
+ CSI2SS_DATA_TYPE_DISABLE_BF_MASK);
+
+ regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);
+
+ regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL,
+ FIELD_PREP(CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK, hs_settle) |
+ CSI2SS_PHY_CTRL_RX_ENABLE | CSI2SS_PHY_CTRL_DDRCLK_EN |
+ CSI2SS_PHY_CTRL_CONT_CLK_MODE | CSI2SS_PHY_CTRL_PD |
+ CSI2SS_PHY_CTRL_RTERM_SEL | CSI2SS_PHY_CTRL_AUTO_PD_EN);
+
+ ret = regmap_read_poll_timeout(state->phy_gpr, CSI2SS_PLM_CTRL,
+ val, !(val & CSI2SS_PLM_CTRL_PL_CLK_RUN),
+ CSI2SS_PL_CLK_INTERVAL_US,
+ CSI2SS_PL_CLK_TIMEOUT_US);
+
+ if (ret) {
+ dev_err(state->dev, "Timeout waiting for Pixel-Link clock\n");
+ return ret;
+ }
+
+ /* Enable Pixel link Master */
+ regmap_set_bits(state->phy_gpr, CSI2SS_PLM_CTRL,
+ CSI2SS_PLM_CTRL_ENABLE_PL | CSI2SS_PLM_CTRL_VALID_OVERRIDE);
+
+ /* PHY Enable */
+ regmap_clear_bits(state->phy_gpr, CSI2SS_PHY_CTRL,
+ CSI2SS_PHY_CTRL_PD | CSI2SS_PLM_CTRL_POLARITY_HIGH);
+
+ /* Release Reset */
+ regmap_set_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET, CSI2SS_CTRL_CLK_RESET_EN);
+
+ return ret;
+}
+
+static void imx8qxp_gpr_disable(struct csi_state *state)
+{
+ /* Disable Pixel Link */
+ regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);
+
+ /* Disable PHY */
+ regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL, 0x0);
+
+ regmap_clear_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET,
+ CSI2SS_CTRL_CLK_RESET_EN);
+};
+
+static const struct imx8mq_plat_data imx8qxp_data = {
+ .enable = imx8qxp_gpr_enable,
+ .disable = imx8qxp_gpr_disable,
+ .use_reg_csr = true,
+};
+
static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
/* RAW (Bayer and greyscale) formats. */
{
@@ -371,14 +492,9 @@ static int imx8mq_mipi_csi_start_stream(struct csi_state *state,
if (ret)
return ret;
- regmap_update_bits(state->phy_gpr,
- state->phy_gpr_reg,
- 0x3fff,
- GPR_CSI2_1_RX_ENABLE |
- GPR_CSI2_1_VID_INTFC_ENB |
- GPR_CSI2_1_HSEL |
- GPR_CSI2_1_CONT_CLK_MODE |
- GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle));
+ ret = state->pdata->enable(state, hs_settle);
+ if (ret)
+ return ret;
return 0;
}
@@ -386,6 +502,9 @@ static int imx8mq_mipi_csi_start_stream(struct csi_state *state,
static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
{
imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
+
+ if (state->pdata->disable)
+ state->pdata->disable(state);
}
/* -----------------------------------------------------------------------------
@@ -837,6 +956,25 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
return PTR_ERR(state->rst);
}
+ if (state->pdata->use_reg_csr) {
+ const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource(to_platform_device(dev), 1);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base), "Missing CSR register\n");
+
+ state->phy_gpr = devm_regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(state->phy_gpr))
+ return dev_err_probe(dev, PTR_ERR(state->phy_gpr),
+ "Failed to init CSI MMIO regmap\n");
+ return 0;
+ }
+
ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
ARRAY_SIZE(out_val));
if (ret) {
@@ -876,6 +1014,8 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
state->dev = dev;
+ state->pdata = of_device_get_match_data(dev);
+
ret = imx8mq_mipi_csi_parse_dt(state);
if (ret < 0) {
dev_err(dev, "Failed to parse device tree: %d\n", ret);
@@ -953,7 +1093,8 @@ static void imx8mq_mipi_csi_remove(struct platform_device *pdev)
}
static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
- { .compatible = "fsl,imx8mq-mipi-csi2", },
+ { .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data },
+ { .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index f732a76de93e..88c0ba495c32 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -849,8 +849,7 @@ static int csiphy_init(struct csiphy_device *csiphy)
regs->offset = 0x1000;
break;
default:
- WARN(1, "unknown csiphy version\n");
- return -ENODEV;
+ break;
}
return 0;
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index c622efcc92ff..2de97f58f9ae 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -103,11 +103,6 @@ const struct csiphy_formats csiphy_formats_8x96 = {
.formats = formats_8x96
};
-const struct csiphy_formats csiphy_formats_sc7280 = {
- .nformats = ARRAY_SIZE(formats_sdm845),
- .formats = formats_sdm845
-};
-
const struct csiphy_formats csiphy_formats_sdm845 = {
.nformats = ARRAY_SIZE(formats_sdm845),
.formats = formats_sdm845
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index ab91273303b9..895f80003c44 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -126,7 +126,6 @@ void msm_csiphy_unregister_entity(struct csiphy_device *csiphy);
extern const struct csiphy_formats csiphy_formats_8x16;
extern const struct csiphy_formats csiphy_formats_8x96;
-extern const struct csiphy_formats csiphy_formats_sc7280;
extern const struct csiphy_formats csiphy_formats_sdm845;
extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index aa021fd5e123..8d05802d1735 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -225,6 +225,21 @@ static int video_check_format(struct camss_video *video)
return 0;
}
+static int video_prepare_streaming(struct vb2_queue *q)
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ int ret;
+
+ ret = v4l2_pipeline_pm_get(&vdev->entity);
+ if (ret < 0) {
+ dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
static int video_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct camss_video *video = vb2_get_drv_priv(q);
@@ -308,13 +323,23 @@ static void video_stop_streaming(struct vb2_queue *q)
video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
}
+static void video_unprepare_streaming(struct vb2_queue *q)
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+
+ v4l2_pipeline_pm_put(&vdev->entity);
+}
+
static const struct vb2_ops msm_video_vb2_q_ops = {
.queue_setup = video_queue_setup,
.buf_init = video_buf_init,
.buf_prepare = video_buf_prepare,
.buf_queue = video_buf_queue,
+ .prepare_streaming = video_prepare_streaming,
.start_streaming = video_start_streaming,
.stop_streaming = video_stop_streaming,
+ .unprepare_streaming = video_unprepare_streaming,
};
/* -----------------------------------------------------------------------------
@@ -599,20 +624,10 @@ static int video_open(struct file *file)
file->private_data = vfh;
- ret = v4l2_pipeline_pm_get(&vdev->entity);
- if (ret < 0) {
- dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
- ret);
- goto error_pm_use;
- }
-
mutex_unlock(&video->lock);
return 0;
-error_pm_use:
- v4l2_fh_release(file);
-
error_alloc:
mutex_unlock(&video->lock);
@@ -621,12 +636,8 @@ error_alloc:
static int video_release(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
-
vb2_fop_release(file);
- v4l2_pipeline_pm_put(&vdev->entity);
-
file->private_data = NULL;
return 0;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 06f42875702f..e08e70b93824 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -1481,7 +1481,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY1 */
@@ -1496,7 +1496,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY2 */
@@ -1511,7 +1511,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY3 */
@@ -1526,7 +1526,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 3,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY4 */
@@ -1541,7 +1541,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 4,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
};
@@ -2486,8 +2486,8 @@ static const struct resources_icc icc_res_sm8550[] = {
static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
/* CSIPHY0 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -2501,8 +2501,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -2516,8 +2516,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -2531,8 +2531,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -3386,43 +3386,39 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
struct camss *camss = container_of(async, struct camss, notifier);
struct v4l2_device *v4l2_dev = &camss->v4l2_dev;
struct v4l2_subdev *sd;
- int ret;
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (sd->host_priv) {
- struct media_entity *sensor = &sd->entity;
- struct csiphy_device *csiphy =
- (struct csiphy_device *) sd->host_priv;
- struct media_entity *input = &csiphy->subdev.entity;
- unsigned int i;
-
- for (i = 0; i < sensor->num_pads; i++) {
- if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
- break;
- }
- if (i == sensor->num_pads) {
- dev_err(camss->dev,
- "No source pad in external entity\n");
- return -EINVAL;
- }
+ struct csiphy_device *csiphy = sd->host_priv;
+ struct media_entity *input, *sensor;
+ unsigned int i;
+ int ret;
- ret = media_create_pad_link(sensor, i,
- input, MSM_CSIPHY_PAD_SINK,
- MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- if (ret < 0) {
- camss_link_err(camss, sensor->name,
- input->name,
- ret);
- return ret;
- }
+ if (!csiphy)
+ continue;
+
+ input = &csiphy->subdev.entity;
+ sensor = &sd->entity;
+
+ for (i = 0; i < sensor->num_pads; i++) {
+ if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+ if (i == sensor->num_pads) {
+ dev_err(camss->dev,
+ "No source pad in external entity\n");
+ return -EINVAL;
}
- }
- ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
- if (ret < 0)
- return ret;
+ ret = media_create_pad_link(sensor, i, input,
+ MSM_CSIPHY_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret < 0) {
+ camss_link_err(camss, sensor->name, input->name, ret);
+ return ret;
+ }
+ }
- return media_device_register(&camss->media_dev);
+ return v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
}
static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = {
@@ -3625,7 +3621,7 @@ static int camss_probe(struct platform_device *pdev)
ret = v4l2_device_register(camss->dev, &camss->v4l2_dev);
if (ret < 0) {
dev_err(dev, "Failed to register V4L2 device: %d\n", ret);
- goto err_genpd_cleanup;
+ goto err_media_device_cleanup;
}
v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev);
@@ -3646,6 +3642,12 @@ static int camss_probe(struct platform_device *pdev)
if (ret < 0)
goto err_register_subdevs;
+ ret = media_device_register(&camss->media_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register media device: %d\n", ret);
+ goto err_register_subdevs;
+ }
+
if (num_subdevs) {
camss->notifier.ops = &camss_subdev_notifier_ops;
@@ -3654,32 +3656,29 @@ static int camss_probe(struct platform_device *pdev)
dev_err(dev,
"Failed to register async subdev nodes: %d\n",
ret);
- goto err_register_subdevs;
+ goto err_media_device_unregister;
}
} else {
ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
if (ret < 0) {
dev_err(dev, "Failed to register subdev nodes: %d\n",
ret);
- goto err_register_subdevs;
- }
-
- ret = media_device_register(&camss->media_dev);
- if (ret < 0) {
- dev_err(dev, "Failed to register media device: %d\n",
- ret);
- goto err_register_subdevs;
+ goto err_media_device_unregister;
}
}
return 0;
+err_media_device_unregister:
+ media_device_unregister(&camss->media_dev);
err_register_subdevs:
camss_unregister_entities(camss);
err_v4l2_device_unregister:
v4l2_device_unregister(&camss->v4l2_dev);
v4l2_async_nf_cleanup(&camss->notifier);
pm_runtime_disable(dev);
+err_media_device_cleanup:
+ media_device_cleanup(&camss->media_dev);
err_genpd_cleanup:
camss_genpd_cleanup(camss);
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
index e5c5a564fcb8..6425e4919e3b 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -205,6 +205,9 @@ static u32 iris_bitstream_buffer_size(struct iris_inst *inst)
if (num_mbs > NUM_MBS_4K) {
div_factor = 4;
base_res_mbs = caps->max_mbpf;
+ } else {
+ if (inst->codec == V4L2_PIX_FMT_VP9)
+ div_factor = 1;
}
/*
@@ -376,7 +379,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf
return 0;
}
-int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
+static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
struct iris_buffer *buf, *next;
@@ -396,6 +399,14 @@ int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
for (i = 0; i < len; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
list_for_each_entry_safe(buf, next, &buffers->list, list) {
+ /*
+ * during stream on, skip destroying internal(DPB) buffer
+ * if firmware did not return it.
+ * during close, destroy all buffers irrespectively.
+ */
+ if (!force && buf->attr & BUF_ATTR_QUEUED)
+ continue;
+
ret = iris_destroy_internal_buffer(inst, buf);
if (ret)
return ret;
@@ -405,6 +416,16 @@ int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
return 0;
}
+int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ return iris_destroy_internal_buffers(inst, plane, true);
+}
+
+int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ return iris_destroy_internal_buffers(inst, plane, false);
+}
+
static int iris_release_internal_buffers(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
@@ -593,10 +614,13 @@ int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
vb2 = &vbuf->vb2_buf;
- if (buf->flags & V4L2_BUF_FLAG_ERROR)
+ if (buf->flags & V4L2_BUF_FLAG_ERROR) {
state = VB2_BUF_STATE_ERROR;
- else
- state = VB2_BUF_STATE_DONE;
+ vb2_set_plane_payload(vb2, 0, 0);
+ vb2->timestamp = 0;
+ v4l2_m2m_buf_done(vbuf, state);
+ return 0;
+ }
vbuf->flags |= buf->flags;
@@ -615,7 +639,10 @@ int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
v4l2_event_queue_fh(&inst->fh, &ev);
v4l2_m2m_mark_stopped(m2m_ctx);
}
+ inst->last_buffer_dequeued = true;
}
+
+ state = VB2_BUF_STATE_DONE;
vb2->timestamp = buf->timestamp;
v4l2_m2m_buf_done(vbuf, state);
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
index c36b6347b077..00825ad2dc3a 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_buffer.h
@@ -106,7 +106,8 @@ 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_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer);
-int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane);
+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);
int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst);
int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf);
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index b690578256d5..9136b723c0f2 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -17,12 +17,20 @@ static inline bool iris_valid_cap_id(enum platform_inst_fw_cap_type cap_id)
static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
{
switch (id) {
- case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
- return DEBLOCK;
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
- return PROFILE;
+ return PROFILE_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ return PROFILE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+ return PROFILE_VP9;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- return LEVEL;
+ return LEVEL_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ return LEVEL_HEVC;
+ case V4L2_CID_MPEG_VIDEO_VP9_LEVEL:
+ return LEVEL_VP9;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+ return TIER;
default:
return INST_FW_CAP_MAX;
}
@@ -34,12 +42,20 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
return 0;
switch (cap_id) {
- case DEBLOCK:
- return V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER;
- case PROFILE:
+ case PROFILE_H264:
return V4L2_CID_MPEG_VIDEO_H264_PROFILE;
- case LEVEL:
+ case PROFILE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_PROFILE;
+ case PROFILE_VP9:
+ return V4L2_CID_MPEG_VIDEO_VP9_PROFILE;
+ case LEVEL_H264:
return V4L2_CID_MPEG_VIDEO_H264_LEVEL;
+ case LEVEL_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_LEVEL;
+ case LEVEL_VP9:
+ return V4L2_CID_MPEG_VIDEO_VP9_LEVEL;
+ case TIER:
+ return V4L2_CID_MPEG_VIDEO_HEVC_TIER;
default:
return 0;
}
@@ -84,8 +100,6 @@ int iris_ctrls_init(struct iris_inst *inst)
if (iris_get_v4l2_id(cap[idx].cap_id))
num_ctrls++;
}
- if (!num_ctrls)
- return -EINVAL;
/* Adding 1 to num_ctrls to include V4L2_CID_MIN_BUFFERS_FOR_CAPTURE */
@@ -163,6 +177,7 @@ void iris_session_init_caps(struct iris_core *core)
core->inst_fw_caps[cap_id].value = caps[i].value;
core->inst_fw_caps[cap_id].flags = caps[i].flags;
core->inst_fw_caps[cap_id].hfi_id = caps[i].hfi_id;
+ core->inst_fw_caps[cap_id].set = caps[i].set;
}
}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h
index b2c541367fc6..9e6aadb83783 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -140,6 +140,7 @@ struct hfi_subscription_params {
u32 color_info;
u32 profile;
u32 level;
+ u32 tier;
};
u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries);
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 64f887d9a17d..5fc30d54af4d 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -88,16 +88,29 @@ static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core)
static int iris_hfi_gen1_session_open(struct iris_inst *inst)
{
struct hfi_session_open_pkt packet;
+ u32 codec = 0;
int ret;
if (inst->state != IRIS_INST_DEINIT)
return -EALREADY;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ codec = HFI_VIDEO_CODEC_H264;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ codec = HFI_VIDEO_CODEC_HEVC;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ codec = HFI_VIDEO_CODEC_VP9;
+ break;
+ }
+
packet.shdr.hdr.size = sizeof(struct hfi_session_open_pkt);
packet.shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT;
packet.shdr.session_id = inst->session_id;
packet.session_domain = HFI_SESSION_TYPE_DEC;
- packet.session_codec = HFI_VIDEO_CODEC_H264;
+ packet.session_codec = codec;
reinit_completion(&inst->completion);
@@ -208,8 +221,10 @@ static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
flush_pkt.flush_type = flush_type;
ret = iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
- if (!ret)
+ if (!ret) {
+ inst->flush_responses_pending++;
ret = iris_wait_for_session_response(inst, true);
+ }
}
return ret;
@@ -386,6 +401,8 @@ static int iris_hfi_gen1_session_drain(struct iris_inst *inst, u32 plane)
ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
ip_pkt.shdr.session_id = inst->session_id;
ip_pkt.flags = HFI_BUFFERFLAG_EOS;
+ if (inst->codec == V4L2_PIX_FMT_VP9)
+ ip_pkt.packet_buffer = 0xdeadb000;
return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
}
@@ -490,14 +507,6 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm);
break;
}
- case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: {
- struct hfi_enable *en = prop_data;
- u32 *in = pdata;
-
- en->enable = *in;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*en);
- break;
- }
default:
return -EINVAL;
}
@@ -546,14 +555,15 @@ static int iris_hfi_gen1_set_resolution(struct iris_inst *inst)
struct hfi_framesize fs;
int ret;
- fs.buffer_type = HFI_BUFFER_INPUT;
- fs.width = inst->fmt_src->fmt.pix_mp.width;
- fs.height = inst->fmt_src->fmt.pix_mp.height;
-
- ret = hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs));
- if (ret)
- return ret;
+ if (!iris_drc_pending(inst)) {
+ fs.buffer_type = HFI_BUFFER_INPUT;
+ fs.width = inst->fmt_src->fmt.pix_mp.width;
+ fs.height = inst->fmt_src->fmt.pix_mp.height;
+ ret = hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs));
+ if (ret)
+ return ret;
+ }
fs.buffer_type = HFI_BUFFER_OUTPUT2;
fs.width = inst->fmt_dst->fmt.pix_mp.width;
fs.height = inst->fmt_dst->fmt.pix_mp.height;
@@ -768,8 +778,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p
iris_hfi_gen1_set_bufsize},
};
- config_params = core->iris_platform_data->input_config_params;
- config_params_size = core->iris_platform_data->input_config_params_size;
+ config_params = core->iris_platform_data->input_config_params_default;
+ config_params_size = core->iris_platform_data->input_config_params_default_size;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
for (i = 0; i < config_params_size; i++) {
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 9f246816a286..d4d119ca98b0 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -13,6 +13,8 @@
#define HFI_SESSION_TYPE_DEC 2
#define HFI_VIDEO_CODEC_H264 0x00000002
+#define HFI_VIDEO_CODEC_HEVC 0x00002000
+#define HFI_VIDEO_CODEC_VP9 0x00004000
#define HFI_ERR_NONE 0x0
@@ -65,7 +67,6 @@
#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001
-#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER 0x1200001
#define HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS 0x120300e
#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004
@@ -117,6 +118,8 @@
#define HFI_FRAME_NOTCODED 0x7f002000
#define HFI_FRAME_YUV 0x7f004000
#define HFI_UNUSED_PICT 0x10000000
+#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008
+#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000
struct hfi_pkt_hdr {
u32 size;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
index b72d503dd740..8d1ce8a19a45 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -200,14 +200,14 @@ static void iris_hfi_gen1_event_seq_changed(struct iris_inst *inst,
iris_hfi_gen1_read_changed_params(inst, pkt);
- if (inst->state != IRIS_INST_ERROR) {
- reinit_completion(&inst->flush_completion);
+ if (inst->state != IRIS_INST_ERROR && !(inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) {
flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
flush_pkt.shdr.session_id = inst->session_id;
flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
- iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ if (!iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size))
+ inst->flush_responses_pending++;
}
iris_vdec_src_change(inst);
@@ -348,6 +348,10 @@ static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet)
struct iris_buffer *buf = NULL;
bool found = false;
+ /* EOS buffer sent via drain won't be in v4l2 buffer list */
+ if (pkt->packet_buffer == 0xdeadb000)
+ return;
+
v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
buf = to_iris_buffer(&m2m_buffer->vb);
if (buf->index == pkt->input_tag) {
@@ -408,7 +412,9 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
flush_pkt.shdr.session_id = inst->session_id;
flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
- iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ if (!iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size))
+ inst->flush_responses_pending++;
+
iris_inst_sub_state_change_drain_last(inst);
return;
@@ -455,7 +461,12 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
timestamp_us = timestamp_hi;
timestamp_us = (timestamp_us << 32) | timestamp_lo;
} else {
- flags |= V4L2_BUF_FLAG_LAST;
+ if (pkt->stream_id == 1 && !inst->last_buffer_dequeued) {
+ if (iris_drc_pending(inst)) {
+ flags |= V4L2_BUF_FLAG_LAST;
+ inst->last_buffer_dequeued = true;
+ }
+ }
}
buf->timestamp = timestamp_us;
@@ -481,6 +492,12 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
buf->attr |= BUF_ATTR_DEQUEUED;
buf->attr |= BUF_ATTR_BUFFER_DONE;
+ if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT)
+ flags |= V4L2_BUF_FLAG_ERROR;
+
+ if (hfi_flags & HFI_BUFFERFLAG_DROP_FRAME)
+ flags |= V4L2_BUF_FLAG_ERROR;
+
buf->flags |= flags;
iris_vb2_buffer_done(inst, buf);
@@ -558,7 +575,6 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response
const struct iris_hfi_gen1_response_pkt_info *pkt_info;
struct device *dev = core->dev;
struct hfi_session_pkt *pkt;
- struct completion *done;
struct iris_inst *inst;
bool found = false;
u32 i;
@@ -619,9 +635,12 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response
if (shdr->error_type != HFI_ERR_NONE)
iris_inst_change_state(inst, IRIS_INST_ERROR);
- done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ?
- &inst->flush_completion : &inst->completion;
- complete(done);
+ if (pkt_info->pkt == HFI_MSG_SESSION_FLUSH) {
+ if (!(--inst->flush_responses_pending))
+ complete(&inst->flush_completion);
+ } else {
+ complete(&inst->completion);
+ }
}
mutex_unlock(&inst->lock);
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 a908b41e2868..7ca5ae13d62b 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -178,7 +178,7 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst)
sizeof(u64));
}
-static int iris_hfi_gen2_set_bit_dpeth(struct iris_inst *inst)
+static int iris_hfi_gen2_set_bit_depth(struct iris_inst *inst)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
@@ -295,7 +295,19 @@ static int iris_hfi_gen2_set_profile(struct iris_inst *inst)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- u32 profile = inst->fw_caps[PROFILE].value;
+ u32 profile = 0;
+
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ profile = inst->fw_caps[PROFILE_HEVC].value;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ profile = inst->fw_caps[PROFILE_VP9].value;
+ break;
+ case V4L2_PIX_FMT_H264:
+ profile = inst->fw_caps[PROFILE_H264].value;
+ break;
+ }
inst_hfi_gen2->src_subcr_params.profile = profile;
@@ -312,7 +324,19 @@ static int iris_hfi_gen2_set_level(struct iris_inst *inst)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- u32 level = inst->fw_caps[LEVEL].value;
+ u32 level = 0;
+
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ level = inst->fw_caps[LEVEL_HEVC].value;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ level = inst->fw_caps[LEVEL_VP9].value;
+ break;
+ case V4L2_PIX_FMT_H264:
+ level = inst->fw_caps[LEVEL_H264].value;
+ break;
+ }
inst_hfi_gen2->src_subcr_params.level = level;
@@ -367,18 +391,35 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst)
sizeof(u64));
}
+static int iris_hfi_gen2_set_tier(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 tier = inst->fw_caps[TIER].value;
+
+ inst_hfi_gen2->src_subcr_params.tier = tier;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_TIER,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &tier,
+ sizeof(u32));
+}
+
static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane)
{
struct iris_core *core = inst->core;
- u32 config_params_size, i, j;
- const u32 *config_params;
+ u32 config_params_size = 0, i, j;
+ const u32 *config_params = NULL;
int ret;
static const struct iris_hfi_prop_type_handle prop_type_handle_arr[] = {
{HFI_PROP_BITSTREAM_RESOLUTION, iris_hfi_gen2_set_bitstream_resolution },
{HFI_PROP_CROP_OFFSETS, iris_hfi_gen2_set_crop_offsets },
{HFI_PROP_CODED_FRAMES, iris_hfi_gen2_set_coded_frames },
- {HFI_PROP_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_dpeth },
+ {HFI_PROP_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_depth },
{HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, iris_hfi_gen2_set_min_output_count },
{HFI_PROP_PIC_ORDER_CNT_TYPE, iris_hfi_gen2_set_picture_order_count },
{HFI_PROP_SIGNAL_COLOR_INFO, iris_hfi_gen2_set_colorspace },
@@ -386,11 +427,27 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
{HFI_PROP_LEVEL, iris_hfi_gen2_set_level },
{HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat },
{HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline },
+ {HFI_PROP_TIER, iris_hfi_gen2_set_tier },
};
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- config_params = core->iris_platform_data->input_config_params;
- config_params_size = core->iris_platform_data->input_config_params_size;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ config_params = core->iris_platform_data->input_config_params_default;
+ config_params_size =
+ core->iris_platform_data->input_config_params_default_size;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ config_params = core->iris_platform_data->input_config_params_hevc;
+ config_params_size =
+ core->iris_platform_data->input_config_params_hevc_size;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ config_params = core->iris_platform_data->input_config_params_vp9;
+ config_params_size =
+ core->iris_platform_data->input_config_params_vp9_size;
+ break;
+ }
} else {
config_params = core->iris_platform_data->output_config_params;
config_params_size = core->iris_platform_data->output_config_params_size;
@@ -416,7 +473,19 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
- u32 codec = HFI_CODEC_DECODE_AVC;
+ u32 codec = 0;
+
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ codec = HFI_CODEC_DECODE_AVC;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ codec = HFI_CODEC_DECODE_HEVC;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ codec = HFI_CODEC_DECODE_VP9;
+ break;
+ }
iris_hfi_gen2_packet_session_property(inst,
HFI_PROP_CODEC,
@@ -548,8 +617,8 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
struct hfi_subscription_params subsc_params;
u32 prop_type, payload_size, payload_type;
struct iris_core *core = inst->core;
- const u32 *change_param;
- u32 change_param_size;
+ const u32 *change_param = NULL;
+ u32 change_param_size = 0;
u32 payload[32] = {0};
u32 hfi_port = 0, i;
int ret;
@@ -560,8 +629,23 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
return 0;
}
- change_param = core->iris_platform_data->input_config_params;
- change_param_size = core->iris_platform_data->input_config_params_size;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ change_param = core->iris_platform_data->input_config_params_default;
+ change_param_size =
+ core->iris_platform_data->input_config_params_default_size;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ change_param = core->iris_platform_data->input_config_params_hevc;
+ change_param_size =
+ core->iris_platform_data->input_config_params_hevc_size;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ change_param = core->iris_platform_data->input_config_params_vp9;
+ change_param_size =
+ core->iris_platform_data->input_config_params_vp9_size;
+ break;
+ }
payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE;
@@ -608,6 +692,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
payload_size = sizeof(u32);
payload_type = HFI_PAYLOAD_U32;
break;
+ case HFI_PROP_LUMA_CHROMA_BIT_DEPTH:
+ payload[0] = subsc_params.bit_depth;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT:
payload[0] = subsc_params.fw_min_count;
payload_size = sizeof(u32);
@@ -633,6 +722,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
payload_size = sizeof(u32);
payload_type = HFI_PAYLOAD_U32;
break;
+ case HFI_PROP_TIER:
+ payload[0] = subsc_params.tier;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
default:
prop_type = 0;
ret = -EINVAL;
@@ -659,8 +753,8 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
{
struct iris_core *core = inst->core;
- u32 subscribe_prop_size, i;
- const u32 *subcribe_prop;
+ u32 subscribe_prop_size = 0, i;
+ const u32 *subcribe_prop = NULL;
u32 payload[32] = {0};
payload[0] = HFI_MODE_PROPERTY;
@@ -669,8 +763,23 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
subscribe_prop_size = core->iris_platform_data->dec_input_prop_size;
subcribe_prop = core->iris_platform_data->dec_input_prop;
} else {
- subscribe_prop_size = core->iris_platform_data->dec_output_prop_size;
- subcribe_prop = core->iris_platform_data->dec_output_prop;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_avc;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_avc_size;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_hevc;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_hevc_size;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_vp9;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_vp9_size;
+ break;
+ }
}
for (i = 0; i < subscribe_prop_size; i++)
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 806f8bb7f505..5f13dc11bea5 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -46,6 +46,7 @@
#define HFI_PROP_CROP_OFFSETS 0x03000105
#define HFI_PROP_PROFILE 0x03000107
#define HFI_PROP_LEVEL 0x03000108
+#define HFI_PROP_TIER 0x03000109
#define HFI_PROP_STAGE 0x0300010a
#define HFI_PROP_PIPE 0x0300010b
#define HFI_PROP_LUMA_CHROMA_BIT_DEPTH 0x0300010f
@@ -104,6 +105,9 @@ enum hfi_color_format {
enum hfi_codec_type {
HFI_CODEC_DECODE_AVC = 1,
HFI_CODEC_ENCODE_AVC = 2,
+ HFI_CODEC_DECODE_HEVC = 3,
+ HFI_CODEC_ENCODE_HEVC = 4,
+ HFI_CODEC_DECODE_VP9 = 5,
};
enum hfi_picture_type {
@@ -113,6 +117,7 @@ enum hfi_picture_type {
HFI_PICTURE_I = 0x00000008,
HFI_PICTURE_CRA = 0x00000010,
HFI_PICTURE_BLA = 0x00000020,
+ HFI_PICTURE_NOSHOW = 0x00000040,
};
enum hfi_buffer_type {
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 b75a01641d5d..a8c30fc5c0d0 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -91,7 +91,9 @@ static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
u32 driver_flags = 0;
- if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe)
+ if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_NOSHOW)
+ driver_flags |= V4L2_BUF_FLAG_ERROR;
+ else if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe)
driver_flags |= V4L2_BUF_FLAG_KEYFRAME;
else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_P)
driver_flags |= V4L2_BUF_FLAG_PFRAME;
@@ -265,7 +267,8 @@ static int iris_hfi_gen2_handle_system_error(struct iris_core *core,
{
struct iris_inst *instance;
- dev_err(core->dev, "received system error of type %#x\n", pkt->type);
+ if (pkt)
+ dev_err(core->dev, "received system error of type %#x\n", pkt->type);
core->state = IRIS_CORE_ERROR;
@@ -377,6 +380,11 @@ static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst,
buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags);
+ if (!buf->data_size && inst->state == IRIS_INST_STREAMING &&
+ !(hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST)) {
+ buf->flags |= V4L2_BUF_FLAG_ERROR;
+ }
+
return 0;
}
@@ -563,9 +571,23 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
inst->crop.width = pixmp_ip->width -
((subsc_params.crop_offsets[1] >> 16) & 0xFFFF) - inst->crop.left;
- inst->fw_caps[PROFILE].value = subsc_params.profile;
- inst->fw_caps[LEVEL].value = subsc_params.level;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ inst->fw_caps[PROFILE_HEVC].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_HEVC].value = subsc_params.level;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ inst->fw_caps[PROFILE_VP9].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_VP9].value = subsc_params.level;
+ break;
+ case V4L2_PIX_FMT_H264:
+ inst->fw_caps[PROFILE_H264].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_H264].value = subsc_params.level;
+ break;
+ }
+
inst->fw_caps[POC].value = subsc_params.pic_order_cnt;
+ inst->fw_caps[TIER].value = subsc_params.tier;
if (subsc_params.bit_depth != BIT_DEPTH_8 ||
!(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) {
@@ -636,9 +658,6 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
{
struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
- if (pkt->port != HFI_PORT_BITSTREAM)
- return 0;
-
if (pkt->flags & HFI_FW_FLAGS_INFORMATION)
return 0;
@@ -650,6 +669,9 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
inst_hfi_gen2->src_subcr_params.crop_offsets[0] = pkt->payload[0];
inst_hfi_gen2->src_subcr_params.crop_offsets[1] = pkt->payload[1];
break;
+ case HFI_PROP_LUMA_CHROMA_BIT_DEPTH:
+ inst_hfi_gen2->src_subcr_params.bit_depth = pkt->payload[0];
+ break;
case HFI_PROP_CODED_FRAMES:
inst_hfi_gen2->src_subcr_params.coded_frames = pkt->payload[0];
break;
@@ -668,6 +690,9 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
case HFI_PROP_LEVEL:
inst_hfi_gen2->src_subcr_params.level = pkt->payload[0];
break;
+ case HFI_PROP_TIER:
+ inst_hfi_gen2->src_subcr_params.tier = pkt->payload[0];
+ break;
case HFI_PROP_PICTURE_TYPE:
inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0];
break;
@@ -791,8 +816,21 @@ static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst)
full_range, video_format,
video_signal_type_present_flag);
- subsc_params->profile = inst->fw_caps[PROFILE].value;
- subsc_params->level = inst->fw_caps[LEVEL].value;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ subsc_params->profile = inst->fw_caps[PROFILE_HEVC].value;
+ subsc_params->level = inst->fw_caps[LEVEL_HEVC].value;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ subsc_params->profile = inst->fw_caps[PROFILE_VP9].value;
+ subsc_params->level = inst->fw_caps[LEVEL_VP9].value;
+ break;
+ case V4L2_PIX_FMT_H264:
+ subsc_params->profile = inst->fw_caps[PROFILE_H264].value;
+ subsc_params->level = inst->fw_caps[LEVEL_H264].value;
+ break;
+ }
+
subsc_params->pic_order_cnt = inst->fw_caps[POC].value;
subsc_params->bit_depth = inst->fw_caps[BIT_DEPTH].value;
if (inst->fw_caps[CODED_FRAMES].value ==
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
index fac7df0c4d1a..221dcd09e1e1 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_queue.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
@@ -113,7 +113,7 @@ int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_s
{
struct iris_iface_q_info *q_info = &core->command_queue;
- if (core->state == IRIS_CORE_ERROR)
+ if (core->state == IRIS_CORE_ERROR || core->state == IRIS_CORE_DEINIT)
return -EINVAL;
if (!iris_hfi_queue_write(q_info, pkt, pkt_size)) {
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
index caa3c6507006..0e1f5799b72d 100644
--- a/drivers/media/platform/qcom/iris/iris_instance.h
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -27,6 +27,7 @@
* @crop: structure of crop info
* @completion: structure of signal completions
* @flush_completion: structure of signal completions for flush cmd
+ * @flush_responses_pending: counter to track number of pending flush responses
* @fw_caps: array of supported instance firmware capabilities
* @buffers: array of different iris buffers
* @fw_min_count: minimnum count of buffers needed by fw
@@ -42,6 +43,8 @@
* @sequence_out: a sequence counter for output queue
* @tss: timestamp metadata
* @metadata_idx: index for metadata buffer
+ * @codec: codec type
+ * @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver
*/
struct iris_inst {
@@ -57,6 +60,7 @@ struct iris_inst {
struct iris_hfi_rect_desc crop;
struct completion completion;
struct completion flush_completion;
+ u32 flush_responses_pending;
struct platform_inst_fw_cap fw_caps[INST_FW_CAP_MAX];
struct iris_buffers buffers[BUF_TYPE_MAX];
u32 fw_min_count;
@@ -72,6 +76,8 @@ struct iris_inst {
u32 sequence_out;
struct iris_ts_metadata tss[VIDEO_MAX_FRAME];
u32 metadata_idx;
+ u32 codec;
+ bool last_buffer_dequeued;
};
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index ac76d9e1ef9c..adafdce8a856 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -21,6 +21,7 @@ struct iris_inst;
#define DEFAULT_MAX_HOST_BUF_COUNT 64
#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
#define DEFAULT_FPS 30
+#define NUM_MBS_8K ((8192 * 4352) / 256)
enum stage_type {
STAGE_1 = 1,
@@ -80,8 +81,12 @@ struct platform_inst_caps {
};
enum platform_inst_fw_cap_type {
- PROFILE = 1,
- LEVEL,
+ PROFILE_H264 = 1,
+ PROFILE_HEVC,
+ PROFILE_VP9,
+ LEVEL_H264,
+ LEVEL_HEVC,
+ LEVEL_VP9,
INPUT_BUF_HOST_MAX_COUNT,
STAGE,
PIPE,
@@ -89,7 +94,7 @@ enum platform_inst_fw_cap_type {
CODED_FRAMES,
BIT_DEPTH,
RAP_FRAME,
- DEBLOCK,
+ TIER,
INST_FW_CAP_MAX,
};
@@ -172,15 +177,24 @@ struct iris_platform_data {
struct ubwc_config_data *ubwc_config;
u32 num_vpp_pipe;
u32 max_session_count;
+ /* max number of macroblocks per frame supported */
u32 max_core_mbpf;
- const u32 *input_config_params;
- unsigned int input_config_params_size;
+ const u32 *input_config_params_default;
+ unsigned int input_config_params_default_size;
+ const u32 *input_config_params_hevc;
+ unsigned int input_config_params_hevc_size;
+ const u32 *input_config_params_vp9;
+ unsigned int input_config_params_vp9_size;
const u32 *output_config_params;
unsigned int output_config_params_size;
const u32 *dec_input_prop;
unsigned int dec_input_prop_size;
- const u32 *dec_output_prop;
- unsigned int dec_output_prop_size;
+ const u32 *dec_output_prop_avc;
+ unsigned int dec_output_prop_avc_size;
+ const u32 *dec_output_prop_hevc;
+ unsigned int dec_output_prop_hevc_size;
+ const u32 *dec_output_prop_vp9;
+ unsigned int dec_output_prop_vp9_size;
const u32 *dec_ip_int_buf_tbl;
unsigned int dec_ip_int_buf_tbl_size;
const u32 *dec_op_int_buf_tbl;
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
index 1e69ba15db0f..d3026b2bcb70 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
@@ -17,7 +17,7 @@
static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
{
- .cap_id = PROFILE,
+ .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) |
@@ -31,7 +31,29 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
.set = iris_set_u32_enum,
},
{
- .cap_id = LEVEL,
+ .cap_id = PROFILE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
+ .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_6_2,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
@@ -60,6 +82,60 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
.set = iris_set_u32_enum,
},
{
+ .cap_id = LEVEL_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
+ .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) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
+ .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_6_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) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0),
+ .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_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,
@@ -181,9 +257,10 @@ static struct tz_cp_config tz_cp_config_sm8550 = {
.cp_nonpixel_size = 0x24800000,
};
-static const u32 sm8550_vdec_input_config_params[] = {
+static const u32 sm8550_vdec_input_config_params_default[] = {
HFI_PROP_BITSTREAM_RESOLUTION,
HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
HFI_PROP_CODED_FRAMES,
HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
HFI_PROP_PIC_ORDER_CNT_TYPE,
@@ -192,6 +269,26 @@ static const u32 sm8550_vdec_input_config_params[] = {
HFI_PROP_SIGNAL_COLOR_INFO,
};
+static const u32 sm8550_vdec_input_config_param_hevc[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+ HFI_PROP_TIER,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+};
+
+static const u32 sm8550_vdec_input_config_param_vp9[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+};
+
static const u32 sm8550_vdec_output_config_params[] = {
HFI_PROP_COLOR_FORMAT,
HFI_PROP_LINEAR_STRIDE_SCANLINE,
@@ -201,11 +298,19 @@ static const u32 sm8550_vdec_subscribe_input_properties[] = {
HFI_PROP_NO_OUTPUT,
};
-static const u32 sm8550_vdec_subscribe_output_properties[] = {
+static const u32 sm8550_vdec_subscribe_output_properties_avc[] = {
HFI_PROP_PICTURE_TYPE,
HFI_PROP_CABAC_SESSION,
};
+static const u32 sm8550_vdec_subscribe_output_properties_hevc[] = {
+ HFI_PROP_PICTURE_TYPE,
+};
+
+static const u32 sm8550_vdec_subscribe_output_properties_vp9[] = {
+ HFI_PROP_PICTURE_TYPE,
+};
+
static const u32 sm8550_dec_ip_int_buf_tbl[] = {
BUF_BIN,
BUF_COMV,
@@ -248,19 +353,34 @@ struct iris_platform_data sm8550_data = {
.ubwc_config = &ubwc_config_sm8550,
.num_vpp_pipe = 4,
.max_session_count = 16,
- .max_core_mbpf = ((8192 * 4352) / 256) * 2,
- .input_config_params =
- sm8550_vdec_input_config_params,
- .input_config_params_size =
- ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
.output_config_params =
sm8550_vdec_output_config_params,
.output_config_params_size =
ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties,
- .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_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),
@@ -308,19 +428,34 @@ struct iris_platform_data sm8650_data = {
.ubwc_config = &ubwc_config_sm8550,
.num_vpp_pipe = 4,
.max_session_count = 16,
- .max_core_mbpf = ((8192 * 4352) / 256) * 2,
- .input_config_params =
- sm8550_vdec_input_config_params,
- .input_config_params_size =
- ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
.output_config_params =
sm8550_vdec_output_config_params,
.output_config_params_size =
ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties,
- .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_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),
@@ -365,18 +500,33 @@ struct iris_platform_data qcs8300_data = {
.num_vpp_pipe = 2,
.max_session_count = 16,
.max_core_mbpf = ((4096 * 2176) / 256) * 4,
- .input_config_params =
- sm8550_vdec_input_config_params,
- .input_config_params_size =
- ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
.output_config_params =
sm8550_vdec_output_config_params,
.output_config_params_size =
ARRAY_SIZE(sm8550_vdec_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 = sm8550_vdec_subscribe_output_properties,
- .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_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),
diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
index f82355d72fcf..a8d66ed388a3 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
@@ -5,49 +5,125 @@
static struct platform_inst_fw_cap inst_fw_cap_qcs8300[] = {
{
- .cap_id = PROFILE,
+ .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),
+ 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 = LEVEL,
+ .cap_id = PROFILE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
+ .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_6_2,
.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) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
+ 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) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
.value = V4L2_MPEG_VIDEO_H264_LEVEL_6_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_6_2,
+ .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) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
+ .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_6_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) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0),
+ .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_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,
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
index 5c86fd7b7b6f..8d0816a67ae0 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
@@ -30,15 +30,6 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8250[] = {
.hfi_id = HFI_PROPERTY_PARAM_WORK_MODE,
.set = iris_set_stage,
},
- {
- .cap_id = DEBLOCK,
- .min = 0,
- .max = 1,
- .step_or_mask = 1,
- .value = 0,
- .hfi_id = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
- .set = iris_set_u32,
- },
};
static struct platform_inst_caps platform_inst_cap_sm8250 = {
@@ -136,10 +127,10 @@ struct iris_platform_data sm8250_data = {
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.num_vpp_pipe = 4,
.max_session_count = 16,
- .max_core_mbpf = (8192 * 4352) / 256,
- .input_config_params =
+ .max_core_mbpf = NUM_MBS_8K,
+ .input_config_params_default =
sm8250_vdec_input_config_param_default,
- .input_config_params_size =
+ .input_config_params_default_size =
ARRAY_SIZE(sm8250_vdec_input_config_param_default),
.dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index 9a7ce142f700..4e6e92357968 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -53,7 +53,7 @@ static int iris_init_power_domains(struct iris_core *core)
struct dev_pm_domain_attach_data iris_opp_pd_data = {
.pd_names = core->iris_platform_data->opp_pd_tbl,
.num_pd_names = core->iris_platform_data->opp_pd_tbl_size,
- .pd_flags = PD_FLAG_DEV_LINK_ON,
+ .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
};
ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl);
diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c
index 5976e926c83d..104e1687ad39 100644
--- a/drivers/media/platform/qcom/iris/iris_state.c
+++ b/drivers/media/platform/qcom/iris/iris_state.c
@@ -245,7 +245,7 @@ int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane)
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
-static inline bool iris_drc_pending(struct iris_inst *inst)
+bool iris_drc_pending(struct iris_inst *inst)
{
return inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST;
diff --git a/drivers/media/platform/qcom/iris/iris_state.h b/drivers/media/platform/qcom/iris/iris_state.h
index 78c61aac5e7e..e718386dbe04 100644
--- a/drivers/media/platform/qcom/iris/iris_state.h
+++ b/drivers/media/platform/qcom/iris/iris_state.h
@@ -140,5 +140,6 @@ int iris_inst_sub_state_change_drain_last(struct iris_inst *inst);
int iris_inst_sub_state_change_drc_last(struct iris_inst *inst);
int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane);
bool iris_allow_cmd(struct iris_inst *inst, u32 cmd);
+bool iris_drc_pending(struct iris_inst *inst);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
index cdf11feb590b..8b17c7c39487 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -259,13 +259,14 @@ int iris_vb2_buf_prepare(struct vb2_buffer *vb)
return -EINVAL;
}
- if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
- vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT))
- return -EINVAL;
- if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
- vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT))
- return -EINVAL;
-
+ if (!(inst->sub_state & IRIS_INST_SUB_DRC)) {
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT))
+ return -EINVAL;
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT))
+ return -EINVAL;
+ }
return 0;
}
@@ -304,7 +305,7 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2)
goto exit;
}
- if (V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
+ if (!inst->last_buffer_dequeued && V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
if ((inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST) ||
(inst->sub_state & IRIS_INST_SUB_DRAIN &&
@@ -318,6 +319,7 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2)
v4l2_event_queue_fh(&inst->fh, &eos);
v4l2_m2m_mark_stopped(m2m_ctx);
}
+ inst->last_buffer_dequeued = true;
goto exit;
}
}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
index 4143acedfc57..d670b51c5839 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -32,6 +32,7 @@ int iris_vdec_inst_init(struct iris_inst *inst)
f->fmt.pix_mp.width = DEFAULT_WIDTH;
f->fmt.pix_mp.height = DEFAULT_HEIGHT;
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ inst->codec = f->fmt.pix_mp.pixelformat;
f->fmt.pix_mp.num_planes = 1;
f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
@@ -67,14 +68,67 @@ void iris_vdec_inst_deinit(struct iris_inst *inst)
kfree(inst->fmt_src);
}
+static const struct iris_fmt iris_vdec_formats[] = {
+ [IRIS_FMT_H264] = {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_HEVC] = {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_VP9] = {
+ .pixfmt = V4L2_PIX_FMT_VP9,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
+static const struct iris_fmt *
+find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
+{
+ unsigned int size = ARRAY_SIZE(iris_vdec_formats);
+ const struct iris_fmt *fmt = iris_vdec_formats;
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ if (fmt[i].pixfmt == pixfmt)
+ break;
+ }
+
+ if (i == size || fmt[i].type != type)
+ return NULL;
+
+ return &fmt[i];
+}
+
+static const struct iris_fmt *
+find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
+{
+ const struct iris_fmt *fmt = iris_vdec_formats;
+ unsigned int size = ARRAY_SIZE(iris_vdec_formats);
+
+ if (index >= size || fmt[index].type != type)
+ return NULL;
+
+ return &fmt[index];
+}
+
int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
{
+ const struct iris_fmt *fmt;
+
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- f->pixelformat = V4L2_PIX_FMT_H264;
+ fmt = find_format_by_index(inst, f->index, f->type);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->pixfmt;
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (f->index)
+ return -EINVAL;
f->pixelformat = V4L2_PIX_FMT_NV12;
break;
default:
@@ -88,13 +142,15 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ const struct iris_fmt *fmt;
struct v4l2_format *f_inst;
struct vb2_queue *src_q;
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+ fmt = find_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264) {
+ if (!fmt) {
f_inst = inst->fmt_src;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
@@ -102,7 +158,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) {
+ if (!fmt) {
f_inst = inst->fmt_dst;
f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
@@ -145,13 +201,14 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264)
+ if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
return -EINVAL;
fmt = inst->fmt_src;
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-
- codec_align = DEFAULT_CODEC_ALIGNMENT;
+ fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ inst->codec = fmt->fmt.pix_mp.pixelformat;
+ codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align);
fmt->fmt.pix_mp.num_planes = 1;
@@ -171,6 +228,11 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+ /* Update capture format based on new ip w/h */
+ output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
+ output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
+ inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT);
+
inst->crop.left = 0;
inst->crop.top = 0;
inst->crop.width = f->fmt.pix_mp.width;
@@ -239,35 +301,6 @@ void iris_vdec_src_change(struct iris_inst *inst)
v4l2_event_queue_fh(&inst->fh, &event);
}
-static int iris_vdec_get_num_queued_buffers(struct iris_inst *inst,
- enum iris_buffer_type type)
-{
- struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
- struct v4l2_m2m_buffer *buffer, *n;
- struct iris_buffer *buf;
- u32 count = 0;
-
- switch (type) {
- case BUF_INPUT:
- v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
- buf = to_iris_buffer(&buffer->vb);
- if (!(buf->attr & BUF_ATTR_QUEUED))
- continue;
- count++;
- }
- return count;
- case BUF_OUTPUT:
- v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
- buf = to_iris_buffer(&buffer->vb);
- if (!(buf->attr & BUF_ATTR_QUEUED))
- continue;
- count++;
- }
- return count;
- default:
- return count;
- }
-}
static void iris_vdec_flush_deferred_buffers(struct iris_inst *inst,
enum iris_buffer_type type)
@@ -316,7 +349,6 @@ int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
{
const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
enum iris_buffer_type buffer_type;
- u32 count;
int ret;
switch (plane) {
@@ -334,12 +366,6 @@ int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
if (ret)
goto error;
- count = iris_vdec_get_num_queued_buffers(inst, buffer_type);
- if (count) {
- ret = -EINVAL;
- goto error;
- }
-
ret = iris_inst_state_change_streamoff(inst, plane);
if (ret)
goto error;
@@ -408,7 +434,7 @@ int iris_vdec_streamon_input(struct iris_inst *inst)
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
@@ -482,6 +508,8 @@ static int iris_vdec_process_streamon_output(struct iris_inst *inst)
if (ret)
return ret;
+ inst->last_buffer_dequeued = false;
+
return iris_inst_change_sub_state(inst, clear_sub_state, 0);
}
@@ -496,7 +524,7 @@ int iris_vdec_streamon_output(struct iris_inst *inst)
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h
index b24932dc511a..cd7aab66dc7c 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.h
+++ b/drivers/media/platform/qcom/iris/iris_vdec.h
@@ -8,6 +8,17 @@
struct iris_inst;
+enum iris_fmt_type {
+ IRIS_FMT_H264,
+ IRIS_FMT_HEVC,
+ IRIS_FMT_VP9,
+};
+
+struct iris_fmt {
+ u32 pixfmt;
+ u32 type;
+};
+
int iris_vdec_inst_init(struct iris_inst *inst);
void iris_vdec_inst_deinit(struct iris_inst *inst);
int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index ca0f4e310f77..c417e8c31f80 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -221,6 +221,33 @@ static void iris_session_close(struct iris_inst *inst)
iris_wait_for_session_response(inst, false);
}
+static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ struct iris_buffer *buf, *next;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+ u32 count = 0;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ list_for_each_entry_safe(buf, next, &buffers->list, list)
+ count++;
+ if (count)
+ dev_err(inst->core->dev, "%d buffer of type %d not released",
+ count, internal_buf_type[i]);
+ }
+}
+
int iris_close(struct file *filp)
{
struct iris_inst *inst = iris_get_inst(filp, NULL);
@@ -233,8 +260,10 @@ int iris_close(struct file *filp)
iris_session_close(inst);
iris_inst_change_state(inst, IRIS_INST_DEINIT);
iris_v4l2_fh_deinit(inst);
- iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
iris_remove_session(inst);
mutex_unlock(&inst->lock);
mutex_destroy(&inst->ctx_q_lock);
@@ -249,9 +278,6 @@ static int iris_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
{
struct iris_inst *inst = iris_get_inst(filp, NULL);
- if (f->index)
- return -EINVAL;
-
return iris_vdec_enum_fmt(inst, f);
}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
index dce25e410d80..f92fd39fe310 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -31,6 +31,42 @@ static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_p
return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
}
+static u32 size_h265d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 product = frame_width * frame_height;
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+
+ size_yuv = (product <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1);
+ size_bin_hdr = size_yuv * H265_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * H265_CABAC_RES_RATIO_HD_TOT;
+ size_bin_hdr = ALIGN(size_bin_hdr / num_vpp_pipes, DMA_ALIGNMENT) * num_vpp_pipes;
+ size_bin_res = ALIGN(size_bin_res / num_vpp_pipes, DMA_ALIGNMENT) * num_vpp_pipes;
+
+ return size_bin_hdr + size_bin_res;
+}
+
+static u32 hfi_buffer_bin_vp9d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 _size_yuv = ALIGN(frame_width, 16) * ALIGN(frame_height, 16) * 3 / 2;
+ u32 _size = ALIGN(((max_t(u32, _size_yuv, ((BIN_BUFFER_THRESHOLD * 3) >> 1)) *
+ VPX_DECODER_FRAME_BIN_HDR_BUDGET / VPX_DECODER_FRAME_BIN_DENOMINATOR *
+ VPX_DECODER_FRAME_CONCURENCY_LVL) / num_vpp_pipes), DMA_ALIGNMENT) +
+ ALIGN(((max_t(u32, _size_yuv, ((BIN_BUFFER_THRESHOLD * 3) >> 1)) *
+ VPX_DECODER_FRAME_BIN_RES_BUDGET / VPX_DECODER_FRAME_BIN_DENOMINATOR *
+ VPX_DECODER_FRAME_CONCURENCY_LVL) / num_vpp_pipes), DMA_ALIGNMENT);
+
+ return _size * num_vpp_pipes;
+}
+
+static u32 hfi_buffer_bin_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 n_aligned_w = ALIGN(frame_width, 16);
+ u32 n_aligned_h = ALIGN(frame_height, 16);
+
+ return size_h265d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
+}
+
static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bufcount)
{
u32 frame_height_in_mbs = DIV_ROUND_UP(frame_height, 16);
@@ -55,6 +91,17 @@ static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bu
return (size_colloc * (_comv_bufcount)) + 512;
}
+static u32 hfi_buffer_comv_h265d(u32 frame_width, u32 frame_height, u32 _comv_bufcount)
+{
+ u32 frame_height_in_mbs = (frame_height + 15) >> 4;
+ u32 frame_width_in_mbs = (frame_width + 15) >> 4;
+ u32 _size;
+
+ _size = ALIGN(((frame_width_in_mbs * frame_height_in_mbs) << 8), 512);
+
+ return (_size * (_comv_bufcount)) + 512;
+}
+
static u32 size_h264d_bse_cmd_buf(u32 frame_height)
{
u32 height = ALIGN(frame_height, 32);
@@ -63,6 +110,44 @@ static u32 size_h264d_bse_cmd_buf(u32 frame_height)
SIZE_H264D_BSE_CMD_PER_BUF;
}
+static u32 size_h265d_bse_cmd_buf(u32 frame_width, u32 frame_height)
+{
+ u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) *
+ NUM_HW_PIC_BUF, DMA_ALIGNMENT);
+ _size = min_t(u32, _size, H265D_MAX_SLICE + 1);
+ _size = 2 * _size * SIZE_H265D_BSE_CMD_PER_BUF;
+
+ return _size;
+}
+
+static u32 hfi_buffer_persist_h265d(u32 rpu_enabled)
+{
+ return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 +
+ H265_NUM_FRM_INFO * H265_DISPLAY_BUF_SIZE +
+ H265_NUM_TILE * sizeof(u32) +
+ NUM_HW_PIC_BUF * SIZE_SEI_USERDATA +
+ rpu_enabled * NUM_HW_PIC_BUF * SIZE_DOLBY_RPU_METADATA),
+ DMA_ALIGNMENT);
+}
+
+static inline
+u32 hfi_iris3_vp9d_comv_size(void)
+{
+ return (((8192 + 63) >> 6) * ((4320 + 63) >> 6) * 8 * 8 * 2 * 8);
+}
+
+static u32 hfi_buffer_persist_vp9d(void)
+{
+ return ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, DMA_ALIGNMENT) +
+ ALIGN(hfi_iris3_vp9d_comv_size(), DMA_ALIGNMENT) +
+ ALIGN(MAX_SUPERFRAME_HEADER_LEN, DMA_ALIGNMENT) +
+ ALIGN(VP9_UDC_HEADER_BUF_SIZE, DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * VP9_FRAME_INFO_BUF_SIZE, DMA_ALIGNMENT) +
+ HDR10_HIST_EXTRADATA_SIZE;
+}
+
static u32 size_h264d_vpp_cmd_buf(u32 frame_height)
{
u32 size, height = ALIGN(frame_height, 32);
@@ -83,17 +168,45 @@ static u32 hfi_buffer_persist_h264d(void)
static u32 hfi_buffer_non_comv_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
{
- u32 size_bse, size_vpp, size;
-
- size_bse = size_h264d_bse_cmd_buf(frame_height);
- size_vpp = size_h264d_vpp_cmd_buf(frame_height);
- size = ALIGN(size_bse, DMA_ALIGNMENT) +
+ u32 size_bse = size_h264d_bse_cmd_buf(frame_height);
+ u32 size_vpp = size_h264d_vpp_cmd_buf(frame_height);
+ u32 size = ALIGN(size_bse, DMA_ALIGNMENT) +
ALIGN(size_vpp, DMA_ALIGNMENT) +
ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), DMA_ALIGNMENT);
return ALIGN(size, DMA_ALIGNMENT);
}
+static u32 size_h265d_vpp_cmd_buf(u32 frame_width, u32 frame_height)
+{
+ u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) *
+ NUM_HW_PIC_BUF, DMA_ALIGNMENT);
+ _size = min_t(u32, _size, H265D_MAX_SLICE + 1);
+ _size = ALIGN(_size, 4);
+ _size = 2 * _size * SIZE_H265D_VPP_CMD_PER_BUF;
+ if (_size > VPP_CMD_MAX_SIZE)
+ _size = VPP_CMD_MAX_SIZE;
+
+ return _size;
+}
+
+static u32 hfi_buffer_non_comv_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 _size_bse = size_h265d_bse_cmd_buf(frame_width, frame_height);
+ u32 _size_vpp = size_h265d_vpp_cmd_buf(frame_width, frame_height);
+ u32 _size = ALIGN(_size_bse, DMA_ALIGNMENT) +
+ ALIGN(_size_vpp, DMA_ALIGNMENT) +
+ ALIGN(NUM_HW_PIC_BUF * 20 * 22 * 4, DMA_ALIGNMENT) +
+ ALIGN(2 * sizeof(u16) *
+ (ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS), DMA_ALIGNMENT) +
+ ALIGN(SIZE_HW_PIC(SIZE_H265D_HW_PIC_T), DMA_ALIGNMENT) +
+ HDR10_HIST_EXTRADATA_SIZE;
+
+ return ALIGN(_size, DMA_ALIGNMENT);
+}
+
static u32 size_vpss_lb(u32 frame_width, u32 frame_height)
{
u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size;
@@ -119,6 +232,203 @@ static u32 size_vpss_lb(u32 frame_width, u32 frame_height)
opb_lb_wr_llb_y_buffer_size;
}
+static inline
+u32 size_h265d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE *
+ (ALIGN(frame_width, 64) + 8) * 2;
+}
+
+static inline
+u32 size_h265d_lb_fe_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE *
+ (ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS);
+}
+
+static inline
+u32 size_h265d_lb_fe_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS);
+}
+
+static inline
+u32 size_h265d_lb_se_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return (LCU_MAX_SIZE_PELS / 8 * (128 / 8)) * ((frame_width + 15) >> 4);
+}
+
+static inline
+u32 size_h265d_lb_se_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return max_t(u32, ((frame_height + 16 - 1) / 8) *
+ MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max_t(u32, ((frame_height + 32 - 1) / 8) *
+ MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 64 - 1) / 8) *
+ MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE));
+}
+
+static inline
+u32 size_h265d_lb_pe_top_data(u32 frame_width, u32 frame_height)
+{
+ return MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE *
+ (ALIGN(frame_width, LCU_MIN_SIZE_PELS) / LCU_MIN_SIZE_PELS);
+}
+
+static inline
+u32 size_h265d_lb_vsp_top(u32 frame_width, u32 frame_height)
+{
+ return ((frame_width + 63) >> 6) * 128;
+}
+
+static inline
+u32 size_h265d_lb_vsp_left(u32 frame_width, u32 frame_height)
+{
+ return ((frame_height + 63) >> 6) * 128;
+}
+
+static inline
+u32 size_h265d_lb_recon_dma_metadata_wr(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_lb_recon_dma_metadata_wr(frame_height);
+}
+
+static inline
+u32 size_h265d_qp(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_qp(frame_width, frame_height);
+}
+
+static inline
+u32 hfi_buffer_line_h265d(u32 frame_width, u32 frame_height, bool is_opb, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0, _size;
+
+ _size = ALIGN(size_h265d_lb_fe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_fe_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_se_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_pe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_vsp_left(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_recon_dma_metadata_wr(frame_width, frame_height),
+ DMA_ALIGNMENT) * 4 +
+ ALIGN(size_h265d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+ if (is_opb)
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+
+ return ALIGN((_size + vpss_lb_size), DMA_ALIGNMENT);
+}
+
+static inline
+u32 size_vpxd_lb_fe_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return max_t(u32, ((frame_height + 15) >> 4) *
+ MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max_t(u32, ((frame_height + 31) >> 5) *
+ MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 63) >> 6) *
+ MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE));
+}
+
+static inline
+u32 size_vpxd_lb_fe_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return ((ALIGN(frame_width, 64) + 8) * 10 * 2);
+}
+
+static inline
+u32 size_vpxd_lb_se_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return ((frame_width + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE;
+}
+
+static inline
+u32 size_vpxd_lb_se_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return max_t(u32, ((frame_height + 15) >> 4) *
+ MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max_t(u32, ((frame_height + 31) >> 5) *
+ MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 63) >> 6) *
+ MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE));
+}
+
+static inline
+u32 size_vpxd_lb_recon_dma_metadata_wr(u32 frame_width, u32 frame_height)
+{
+ return ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64,
+ BUFFER_ALIGNMENT_32_BYTES);
+}
+
+static inline __maybe_unused
+u32 size_mp2d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return ((ALIGN(frame_width, 16) + 8) * 10 * 2);
+}
+
+static inline
+u32 size_vp9d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return (ALIGN(ALIGN(frame_width, 8), 64) + 8) * 10 * 2;
+}
+
+static inline
+u32 size_vp9d_lb_pe_top_data(u32 frame_width, u32 frame_height)
+{
+ return ((ALIGN(ALIGN(frame_width, 8), 64) >> 6) * 176);
+}
+
+static inline
+u32 size_vp9d_lb_vsp_top(u32 frame_width, u32 frame_height)
+{
+ return (((ALIGN(ALIGN(frame_width, 8), 64) >> 6) * 64 * 8) + 256);
+}
+
+static inline
+u32 size_vp9d_qp(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_qp(frame_width, frame_height);
+}
+
+static inline
+u32 hfi_iris3_vp9d_lb_size(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ return ALIGN(size_vpxd_lb_fe_left_ctrl(frame_width, frame_height), DMA_ALIGNMENT) *
+ num_vpp_pipes +
+ ALIGN(size_vpxd_lb_se_left_ctrl(frame_width, frame_height), DMA_ALIGNMENT) *
+ num_vpp_pipes +
+ ALIGN(size_vp9d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vpxd_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ 2 * ALIGN(size_vpxd_lb_recon_dma_metadata_wr(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_vpxd_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vp9d_lb_pe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vp9d_lb_fe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vp9d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+}
+
+static inline
+u32 hfi_buffer_line_vp9d(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_min, bool is_opb,
+ u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0;
+ u32 _lb_size;
+
+ _lb_size = hfi_iris3_vp9d_lb_size(frame_width, frame_height, num_vpp_pipes);
+
+ if (is_opb)
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+
+ return _lb_size + vpss_lb_size + 4096;
+}
+
static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height,
bool is_opb, u32 num_vpp_pipes)
{
@@ -148,7 +458,14 @@ static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
- return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_bin_h265d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_buffer_bin_vp9d(width, height, num_vpp_pipes);
+
+ return 0;
}
static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
@@ -158,12 +475,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
- return hfi_buffer_comv_h264d(width, height, num_comv);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_comv_h264d(width, height, num_comv);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_comv_h265d(width, height, num_comv);
+
+ return 0;
}
static u32 iris_vpu_dec_persist_size(struct iris_inst *inst)
{
- return hfi_buffer_persist_h264d();
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_persist_h264d();
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_persist_h265d(0);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_buffer_persist_vp9d();
+
+ return 0;
}
static u32 iris_vpu_dec_dpb_size(struct iris_inst *inst)
@@ -181,7 +510,12 @@ static u32 iris_vpu_dec_non_comv_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
- return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_non_comv_h265d(width, height, num_vpp_pipes);
+
+ return 0;
}
static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
@@ -191,11 +525,20 @@ static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
bool is_opb = false;
+ u32 out_min_count = inst->buffers[BUF_OUTPUT].min_count;
if (iris_split_mode_enabled(inst))
is_opb = true;
- return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_line_h265d(width, height, is_opb, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_buffer_line_vp9d(width, height, out_min_count, is_opb,
+ num_vpp_pipes);
+
+ return 0;
}
static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
@@ -205,6 +548,24 @@ static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
iris_vpu_dec_line_size(inst);
}
+static int output_min_count(struct iris_inst *inst)
+{
+ int output_min_count = 4;
+
+ /* fw_min_count > 0 indicates reconfig event has already arrived */
+ if (inst->fw_min_count) {
+ if (iris_split_mode_enabled(inst) && inst->codec == V4L2_PIX_FMT_VP9)
+ return min_t(u32, 4, inst->fw_min_count);
+ else
+ return inst->fw_min_count;
+ }
+
+ if (inst->codec == V4L2_PIX_FMT_VP9)
+ output_min_count = 9;
+
+ return output_min_count;
+}
+
struct iris_vpu_buf_type_handle {
enum iris_buffer_type type;
u32 (*handle)(struct iris_inst *inst);
@@ -238,6 +599,19 @@ int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
return size;
}
+static u32 internal_buffer_count(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ if (buffer_type == BUF_BIN || buffer_type == BUF_LINE ||
+ buffer_type == BUF_PERSIST) {
+ return 1;
+ } else if (buffer_type == BUF_COMV || buffer_type == BUF_NON_COMV) {
+ if (inst->codec == V4L2_PIX_FMT_H264 || inst->codec == V4L2_PIX_FMT_HEVC)
+ return 1;
+ }
+ return 0;
+}
+
static inline int iris_vpu_dpb_count(struct iris_inst *inst)
{
if (iris_split_mode_enabled(inst)) {
@@ -254,12 +628,13 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type
case BUF_INPUT:
return MIN_BUFFERS;
case BUF_OUTPUT:
- return inst->fw_min_count;
+ return output_min_count(inst);
case BUF_BIN:
case BUF_COMV:
case BUF_NON_COMV:
case BUF_LINE:
case BUF_PERSIST:
+ return internal_buffer_count(inst, buffer_type);
case BUF_SCRATCH_1:
return 1; /* internal buffer count needed by firmware is 1 */
case BUF_DPB:
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
index 62af6ea6ba1f..ee95fd20b794 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
@@ -13,6 +13,10 @@ struct iris_inst;
#define DMA_ALIGNMENT 256
#define NUM_HW_PIC_BUF 32
+#define LCU_MAX_SIZE_PELS 64
+#define LCU_MIN_SIZE_PELS 16
+#define HDR10_HIST_EXTRADATA_SIZE (4 * 1024)
+
#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf))
#define MAX_TILE_COLUMNS 32
@@ -28,11 +32,47 @@ struct iris_inst;
#define SIZE_SLIST_BUF_H264 512
#define H264_DISPLAY_BUF_SIZE 3328
#define H264_NUM_FRM_INFO 66
-
-#define SIZE_SEI_USERDATA 4096
-
+#define H265_NUM_TILE_COL 32
+#define H265_NUM_TILE_ROW 128
+#define H265_NUM_TILE (H265_NUM_TILE_ROW * H265_NUM_TILE_COL + 1)
+#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32))
+
+#define NUM_SLIST_BUF_H265 (80 + 20)
+#define SIZE_SLIST_BUF_H265 (BIT(10))
+#define H265_DISPLAY_BUF_SIZE (3072)
+#define H265_NUM_FRM_INFO (48)
+
+#define VP9_NUM_FRAME_INFO_BUF 32
+#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4)
+#define VP9_PROB_TABLE_SIZE (3840)
+#define VP9_FRAME_INFO_BUF_SIZE (6144)
+#define BUFFER_ALIGNMENT_32_BYTES 32
+#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, BUFFER_ALIGNMENT_32_BYTES)
+#define MAX_SUPERFRAME_HEADER_LEN (34)
+#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE 64
+#define MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8)
+#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8)
+#define VP9_UDC_HEADER_BUF_SIZE (3 * 128)
+
+#define SIZE_SEI_USERDATA 4096
+#define SIZE_DOLBY_RPU_METADATA (41 * 1024)
#define H264_CABAC_HDR_RATIO_HD_TOT 1
#define H264_CABAC_RES_RATIO_HD_TOT 3
+#define H265D_MAX_SLICE 1200
+#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T
+#define H265_CABAC_HDR_RATIO_HD_TOT 2
+#define H265_CABAC_RES_RATIO_HD_TOT 2
+#define SIZE_H265D_VPP_CMD_PER_BUF (256)
+
+#define VPX_DECODER_FRAME_CONCURENCY_LVL (2)
+#define VPX_DECODER_FRAME_BIN_HDR_BUDGET 1
+#define VPX_DECODER_FRAME_BIN_RES_BUDGET 3
+#define VPX_DECODER_FRAME_BIN_DENOMINATOR 2
+
+#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO (3 / 2)
+
#define SIZE_H264D_HW_PIC_T (BIT(11))
#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index d305d74bb152..4c049c694d9c 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -424,13 +424,13 @@ static int venus_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&core->work, venus_sys_error_handler);
init_waitqueue_head(&core->sys_err_done);
- ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
- "venus", core);
+ ret = hfi_create(core, &venus_core_ops);
if (ret)
goto err_core_put;
- ret = hfi_create(core, &venus_core_ops);
+ ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "venus", core);
if (ret)
goto err_core_put;
@@ -709,11 +709,11 @@ static const struct venus_resources msm8996_res = {
};
static const struct freq_tbl msm8998_freq_table[] = {
- { 1944000, 465000000 }, /* 4k UHD @ 60 (decode only) */
- { 972000, 465000000 }, /* 4k UHD @ 30 */
- { 489600, 360000000 }, /* 1080p @ 60 */
- { 244800, 186000000 }, /* 1080p @ 30 */
- { 108000, 100000000 }, /* 720p @ 30 */
+ { 1728000, 533000000 }, /* 4k UHD @ 60 (decode only) */
+ { 1036800, 444000000 }, /* 2k @ 120 */
+ { 829440, 355200000 }, /* 4k @ 44 */
+ { 489600, 269330000 },/* 4k @ 30 */
+ { 108000, 200000000 }, /* 1080p @ 60 */
};
static const struct reg_val msm8998_reg_preset[] = {
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index b412e0c5515a..5b1ba1c69adb 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -28,6 +28,8 @@
#define VIDC_RESETS_NUM_MAX 2
#define VIDC_MAX_HIER_CODING_LAYER 6
+#define VENUS_MAX_FPS 240
+
extern int venus_fw_debug;
struct freq_tbl {
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index 0a041b4db9ef..cf0d97cbc463 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -33,8 +33,9 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
struct hfi_buffer_requirements *bufreq;
struct hfi_extradata_input_crop *crop;
struct hfi_dpb_counts *dpb_count;
+ u32 ptype, rem_bytes;
+ u32 size_read = 0;
u8 *data_ptr;
- u32 ptype;
inst->error = HFI_ERR_NONE;
@@ -44,86 +45,118 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
break;
default:
inst->error = HFI_ERR_SESSION_INVALID_PARAMETER;
- goto done;
+ inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
+ return;
}
event.event_type = pkt->event_data1;
num_properties_changed = pkt->event_data2;
- if (!num_properties_changed) {
- inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
- goto done;
- }
+ if (!num_properties_changed)
+ goto error;
data_ptr = (u8 *)&pkt->ext_event_data[0];
+ rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
+
do {
+ if (rem_bytes < sizeof(u32))
+ goto error;
ptype = *((u32 *)data_ptr);
+
+ data_ptr += sizeof(u32);
+ rem_bytes -= sizeof(u32);
+
switch (ptype) {
case HFI_PROPERTY_PARAM_FRAME_SIZE:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_framesize))
+ goto error;
+
frame_sz = (struct hfi_framesize *)data_ptr;
event.width = frame_sz->width;
event.height = frame_sz->height;
- data_ptr += sizeof(*frame_sz);
+ size_read = sizeof(struct hfi_framesize);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_profile_level))
+ goto error;
+
profile_level = (struct hfi_profile_level *)data_ptr;
event.profile = profile_level->profile;
event.level = profile_level->level;
- data_ptr += sizeof(*profile_level);
+ size_read = sizeof(struct hfi_profile_level);
break;
case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_bit_depth))
+ goto error;
+
pixel_depth = (struct hfi_bit_depth *)data_ptr;
event.bit_depth = pixel_depth->bit_depth;
- data_ptr += sizeof(*pixel_depth);
+ size_read = sizeof(struct hfi_bit_depth);
break;
case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_pic_struct))
+ goto error;
+
pic_struct = (struct hfi_pic_struct *)data_ptr;
event.pic_struct = pic_struct->progressive_only;
- data_ptr += sizeof(*pic_struct);
+ size_read = sizeof(struct hfi_pic_struct);
break;
case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_colour_space))
+ goto error;
+
colour_info = (struct hfi_colour_space *)data_ptr;
event.colour_space = colour_info->colour_space;
- data_ptr += sizeof(*colour_info);
+ size_read = sizeof(struct hfi_colour_space);
break;
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(u32))
+ goto error;
+
event.entropy_mode = *(u32 *)data_ptr;
- data_ptr += sizeof(u32);
+ size_read = sizeof(u32);
break;
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_buffer_requirements))
+ goto error;
+
bufreq = (struct hfi_buffer_requirements *)data_ptr;
event.buf_count = hfi_bufreq_get_count_min(bufreq, ver);
- data_ptr += sizeof(*bufreq);
+ size_read = sizeof(struct hfi_buffer_requirements);
break;
case HFI_INDEX_EXTRADATA_INPUT_CROP:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_extradata_input_crop))
+ goto error;
+
crop = (struct hfi_extradata_input_crop *)data_ptr;
event.input_crop.left = crop->left;
event.input_crop.top = crop->top;
event.input_crop.width = crop->width;
event.input_crop.height = crop->height;
- data_ptr += sizeof(*crop);
+ size_read = sizeof(struct hfi_extradata_input_crop);
break;
case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_dpb_counts))
+ goto error;
+
dpb_count = (struct hfi_dpb_counts *)data_ptr;
event.buf_count = dpb_count->fw_min_cnt;
- data_ptr += sizeof(*dpb_count);
+ size_read = sizeof(struct hfi_dpb_counts);
break;
default:
+ size_read = 0;
break;
}
+ data_ptr += size_read;
+ rem_bytes -= size_read;
num_properties_changed--;
} while (num_properties_changed > 0);
-done:
+ inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
+ return;
+
+error:
+ inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
}
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index b5f2ea879950..cec7f5964d3d 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -239,6 +239,7 @@ static int venus_write_queue(struct venus_hfi_device *hdev,
static int venus_read_queue(struct venus_hfi_device *hdev,
struct iface_queue *queue, void *pkt, u32 *tx_req)
{
+ struct hfi_pkt_hdr *pkt_hdr = NULL;
struct hfi_queue_header *qhdr;
u32 dwords, new_rd_idx;
u32 rd_idx, wr_idx, type, qsize;
@@ -304,6 +305,9 @@ static int venus_read_queue(struct venus_hfi_device *hdev,
memcpy(pkt, rd_ptr, len);
memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2);
}
+ pkt_hdr = (struct hfi_pkt_hdr *)(pkt);
+ if ((pkt_hdr->size >> 2) != dwords)
+ return -EINVAL;
} else {
/* bad packet received, dropping */
new_rd_idx = qhdr->write_idx;
@@ -1678,6 +1682,7 @@ void venus_hfi_destroy(struct venus_core *core)
venus_interface_queues_release(hdev);
mutex_destroy(&hdev->lock);
kfree(hdev);
+ disable_irq(core->irq);
core->ops = NULL;
}
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index 409aa9bd0b5d..8dd5a9b0d060 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -41,16 +41,14 @@ static int core_clks_get(struct venus_core *core)
static int core_clks_enable(struct venus_core *core)
{
const struct venus_resources *res = core->res;
- const struct freq_tbl *freq_tbl = core->res->freq_tbl;
- unsigned int freq_tbl_size = core->res->freq_tbl_size;
- unsigned long freq;
+ struct device *dev = core->dev;
+ unsigned long freq = 0;
+ struct dev_pm_opp *opp;
unsigned int i;
int ret;
- if (!freq_tbl)
- return -EINVAL;
-
- freq = freq_tbl[freq_tbl_size - 1].freq;
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ dev_pm_opp_put(opp);
for (i = 0; i < res->clks_num; i++) {
if (IS_V6(core)) {
@@ -636,7 +634,9 @@ static int decide_core(struct venus_inst *inst)
u32 min_coreid, min_load, cur_inst_load;
u32 min_lp_coreid, min_lp_load, cur_inst_lp_load;
struct hfi_videocores_usage_type cu;
- unsigned long max_freq;
+ unsigned long max_freq = ULONG_MAX;
+ struct device *dev = core->dev;
+ struct dev_pm_opp *opp;
int ret = 0;
if (legacy_binding) {
@@ -659,7 +659,8 @@ static int decide_core(struct venus_inst *inst)
cur_inst_lp_load *= inst->clk_data.low_power_freq;
/*TODO : divide this inst->load by work_route */
- max_freq = core->res->freq_tbl[0].freq;
+ opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
+ dev_pm_opp_put(opp);
min_loaded_core(inst, &min_coreid, &min_load, false);
min_loaded_core(inst, &min_lp_coreid, &min_lp_load, true);
@@ -949,7 +950,10 @@ static int core_resets_get(struct venus_core *core)
static int core_get_v4(struct venus_core *core)
{
struct device *dev = core->dev;
+ const struct freq_tbl *freq_tbl = core->res->freq_tbl;
+ unsigned int num_rows = core->res->freq_tbl_size;
const struct venus_resources *res = core->res;
+ unsigned int i;
int ret;
ret = core_clks_get(core);
@@ -986,9 +990,17 @@ static int core_get_v4(struct venus_core *core)
if (core->res->opp_pmdomain) {
ret = devm_pm_opp_of_add_table(dev);
- if (ret && ret != -ENODEV) {
- dev_err(dev, "invalid OPP table in device tree\n");
- return ret;
+ if (ret) {
+ if (ret == -ENODEV) {
+ for (i = 0; i < num_rows; i++) {
+ ret = dev_pm_opp_add(dev, freq_tbl[i].freq, 0);
+ if (ret)
+ return ret;
+ }
+ } else {
+ dev_err(dev, "invalid OPP table in device tree\n");
+ return ret;
+ }
}
}
@@ -1078,11 +1090,11 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst,
static int load_scale_v4(struct venus_inst *inst)
{
struct venus_core *core = inst->core;
- const struct freq_tbl *table = core->res->freq_tbl;
- unsigned int num_rows = core->res->freq_tbl_size;
struct device *dev = core->dev;
unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0;
+ unsigned long max_freq = ULONG_MAX;
unsigned long filled_len = 0;
+ struct dev_pm_opp *opp;
int i, ret = 0;
for (i = 0; i < inst->num_input_bufs; i++)
@@ -1108,20 +1120,18 @@ static int load_scale_v4(struct venus_inst *inst)
freq = max(freq_core1, freq_core2);
- if (freq > table[0].freq) {
- dev_dbg(dev, VDBGL "requested clock rate: %lu scaling clock rate : %lu\n",
- freq, table[0].freq);
+ opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
+ dev_pm_opp_put(opp);
- freq = table[0].freq;
+ if (freq > max_freq) {
+ dev_dbg(dev, VDBGL "requested clock rate: %lu scaling clock rate : %lu\n",
+ freq, max_freq);
+ freq = max_freq;
goto set_freq;
}
- for (i = num_rows - 1 ; i >= 0; i--) {
- if (freq <= table[i].freq) {
- freq = table[i].freq;
- break;
- }
- }
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ dev_pm_opp_put(opp);
set_freq:
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 99ce5fd41577..29b0d6a5303d 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -481,11 +481,9 @@ static int vdec_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
do_div(us_per_frame, timeperframe->denominator);
- if (!us_per_frame)
- return -EINVAL;
-
- fps = (u64)USEC_PER_SEC;
- do_div(fps, us_per_frame);
+ us_per_frame = clamp(us_per_frame, 1, USEC_PER_SEC);
+ fps = USEC_PER_SEC / (u32)us_per_frame;
+ fps = min(VENUS_MAX_FPS, fps);
inst->fps = fps;
inst->timeperframe = *timeperframe;
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index c7f8e37dba9b..c0a0ccdded80 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -411,11 +411,9 @@ static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
do_div(us_per_frame, timeperframe->denominator);
- if (!us_per_frame)
- return -EINVAL;
-
- fps = (u64)USEC_PER_SEC;
- do_div(fps, us_per_frame);
+ us_per_frame = clamp(us_per_frame, 1, USEC_PER_SEC);
+ fps = USEC_PER_SEC / (u32)us_per_frame;
+ fps = min(VENUS_MAX_FPS, fps);
inst->timeperframe = *timeperframe;
inst->fps = fps;
diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
index 46765a2e4c4d..a9e51fd94aad 100644
--- a/drivers/media/platform/raspberrypi/pisp_be/Kconfig
+++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_RASPBERRYPI_PISP_BE
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on PM
select VIDEO_V4L2_SUBDEV_API
select MEDIA_CONTROLLER
select VIDEOBUF2_DMA_CONTIG
diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
index 7596ae1f7de6..b30891718d8d 100644
--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
@@ -9,9 +9,11 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/lockdep.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
@@ -161,8 +163,6 @@ struct pispbe_node {
struct mutex node_lock;
/* vb2_queue lock */
struct mutex queue_lock;
- /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */
- spinlock_t ready_lock;
struct list_head ready_queue;
struct vb2_queue queue;
struct v4l2_format format;
@@ -190,6 +190,8 @@ struct pispbe_hw_enables {
/* Records a job configuration and memory addresses. */
struct pispbe_job_descriptor {
+ struct list_head queue;
+ struct pispbe_buffer *buffers[PISPBE_NUM_NODES];
dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
struct pisp_be_tiles_config *config;
struct pispbe_hw_enables hw_enables;
@@ -215,8 +217,10 @@ struct pispbe_dev {
unsigned int sequence;
u32 streaming_map;
struct pispbe_job queued_job, running_job;
- spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
+ /* protects "hw_busy" flag, streaming_map and job_queue */
+ spinlock_t hw_lock;
bool hw_busy; /* non-zero if a job is queued or is being started */
+ struct list_head job_queue;
int irq;
u32 hw_version;
u8 done, started;
@@ -368,10 +372,7 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE],
&pispbe->node[MAIN_INPUT_NODE]);
if (ret <= 0) {
- /*
- * This shouldn't happen; pispbe_schedule_internal should insist
- * on an input.
- */
+ /* Shouldn't happen, we have validated an input is available. */
dev_warn(pispbe->dev, "ISP-BE missing input\n");
hw_en->bayer_enables = 0;
hw_en->rgb_enables = 0;
@@ -443,42 +444,48 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
* For Output0, Output1, Tdn and Stitch, a buffer only needs to be
* available if the blocks are enabled in the config.
*
- * Needs to be called with hw_lock held.
+ * If all the buffers required to form a job are available, append the
+ * job descriptor to the job queue to be later queued to the HW.
*
* Returns 0 if a job has been successfully prepared, < 0 otherwise.
*/
-static int pispbe_prepare_job(struct pispbe_dev *pispbe,
- struct pispbe_job_descriptor *job)
+static int pispbe_prepare_job(struct pispbe_dev *pispbe)
{
+ struct pispbe_job_descriptor __free(kfree) *job = NULL;
struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {};
+ unsigned int streaming_map;
unsigned int config_index;
struct pispbe_node *node;
- unsigned long flags;
- lockdep_assert_held(&pispbe->hw_lock);
+ lockdep_assert_irqs_enabled();
- memset(job, 0, sizeof(struct pispbe_job_descriptor));
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ static const u32 mask = BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE);
- if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
- pispbe->streaming_map) !=
- (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)))
- return -ENODEV;
+ if ((pispbe->streaming_map & mask) != mask)
+ return -ENODEV;
+
+ /*
+ * Take a copy of streaming_map: nodes activated after this
+ * point are ignored when preparing this job.
+ */
+ streaming_map = pispbe->streaming_map;
+ }
+
+ job = kzalloc(sizeof(*job), GFP_KERNEL);
+ if (!job)
+ return -ENOMEM;
node = &pispbe->node[CONFIG_NODE];
- spin_lock_irqsave(&node->ready_lock, flags);
buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue,
struct pispbe_buffer,
ready_list);
- if (buf[CONFIG_NODE]) {
- list_del(&buf[CONFIG_NODE]->ready_list);
- pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE];
- }
- spin_unlock_irqrestore(&node->ready_lock, flags);
-
- /* Exit early if no config buffer has been queued. */
if (!buf[CONFIG_NODE])
return -ENODEV;
+ list_del(&buf[CONFIG_NODE]->ready_list);
+ job->buffers[CONFIG_NODE] = buf[CONFIG_NODE];
+
config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
job->config = &pispbe->config[config_index];
job->tiles = pispbe->config_dma_addr +
@@ -498,7 +505,7 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe,
continue;
buf[i] = NULL;
- if (!(pispbe->streaming_map & BIT(i)))
+ if (!(streaming_map & BIT(i)))
continue;
if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
@@ -525,25 +532,28 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe,
node = &pispbe->node[i];
/* Pull a buffer from each V4L2 queue to form the queued job */
- spin_lock_irqsave(&node->ready_lock, flags);
buf[i] = list_first_entry_or_null(&node->ready_queue,
struct pispbe_buffer,
ready_list);
if (buf[i]) {
list_del(&buf[i]->ready_list);
- pispbe->queued_job.buf[i] = buf[i];
+ job->buffers[i] = buf[i];
}
- spin_unlock_irqrestore(&node->ready_lock, flags);
if (!buf[i] && !ignore_buffers)
goto err_return_buffers;
}
- pispbe->queued_job.valid = true;
-
/* Convert buffers to DMA addresses for the hardware */
pispbe_xlate_addrs(pispbe, job, buf);
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ list_add_tail(&job->queue, &pispbe->job_queue);
+ }
+
+ /* Set job to NULL to avoid automatic release due to __free(). */
+ job = NULL;
+
return 0;
err_return_buffers:
@@ -554,33 +564,37 @@ err_return_buffers:
continue;
/* Return the buffer to the ready_list queue */
- spin_lock_irqsave(&n->ready_lock, flags);
list_add(&buf[i]->ready_list, &n->ready_queue);
- spin_unlock_irqrestore(&n->ready_lock, flags);
}
- memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
-
return -ENODEV;
}
static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy)
{
- struct pispbe_job_descriptor job;
- unsigned long flags;
- int ret;
+ struct pispbe_job_descriptor *job;
- spin_lock_irqsave(&pispbe->hw_lock, flags);
+ scoped_guard(spinlock_irqsave, &pispbe->hw_lock) {
+ if (clear_hw_busy)
+ pispbe->hw_busy = false;
- if (clear_hw_busy)
- pispbe->hw_busy = false;
+ if (pispbe->hw_busy)
+ return;
- if (pispbe->hw_busy)
- goto unlock_and_return;
+ job = list_first_entry_or_null(&pispbe->job_queue,
+ struct pispbe_job_descriptor,
+ queue);
+ if (!job)
+ return;
- ret = pispbe_prepare_job(pispbe, &job);
- if (ret)
- goto unlock_and_return;
+ list_del(&job->queue);
+
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++)
+ pispbe->queued_job.buf[i] = job->buffers[i];
+ pispbe->queued_job.valid = true;
+
+ pispbe->hw_busy = true;
+ }
/*
* We can kick the job off without the hw_lock, as this can
@@ -588,34 +602,8 @@ static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy)
* only when the following job has been queued and an interrupt
* is rised.
*/
- pispbe->hw_busy = true;
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
-
- if (job.config->num_tiles <= 0 ||
- job.config->num_tiles > PISP_BACK_END_NUM_TILES ||
- !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) &
- PISP_BE_BAYER_ENABLE_INPUT)) {
- /*
- * Bad job. We can't let it proceed as it could lock up
- * the hardware, or worse!
- *
- * For now, just force num_tiles to 0, which causes the
- * H/W to do something bizarre but survivable. It
- * increments (started,done) counters by more than 1,
- * but we seem to survive...
- */
- dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n",
- job.config->num_tiles);
- job.config->num_tiles = 0;
- }
-
- pispbe_queue_job(pispbe, &job);
-
- return;
-
-unlock_and_return:
- /* No job has been queued, just release the lock and return. */
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+ pispbe_queue_job(pispbe, job);
+ kfree(job);
}
static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
@@ -706,6 +694,13 @@ static int pisp_be_validate_config(struct pispbe_dev *pispbe,
return -EIO;
}
+ if (config->num_tiles == 0 ||
+ config->num_tiles > PISP_BACK_END_NUM_TILES) {
+ dev_dbg(dev, "%s: Invalid number of tiles: %d\n", __func__,
+ config->num_tiles);
+ return -EINVAL;
+ }
+
/* Ensure output config strides and buffer sizes match the V4L2 formats. */
fmt = &pispbe->node[TDN_OUTPUT_NODE].format;
if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) {
@@ -860,18 +855,16 @@ static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
container_of(vbuf, struct pispbe_buffer, vb);
struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue);
struct pispbe_dev *pispbe = node->pispbe;
- unsigned long flags;
dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
- spin_lock_irqsave(&node->ready_lock, flags);
list_add_tail(&buffer->ready_list, &node->ready_queue);
- spin_unlock_irqrestore(&node->ready_lock, flags);
/*
* Every time we add a buffer, check if there's now some work for the hw
* to do.
*/
- pispbe_schedule(pispbe, false);
+ if (!pispbe_prepare_job(pispbe))
+ pispbe_schedule(pispbe, false);
}
static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -879,17 +872,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
struct pispbe_node *node = vb2_get_drv_priv(q);
struct pispbe_dev *pispbe = node->pispbe;
struct pispbe_buffer *buf, *tmp;
- unsigned long flags;
int ret;
ret = pm_runtime_resume_and_get(pispbe->dev);
if (ret < 0)
goto err_return_buffers;
- spin_lock_irqsave(&pispbe->hw_lock, flags);
- node->pispbe->streaming_map |= BIT(node->id);
- node->pispbe->sequence = 0;
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ node->pispbe->streaming_map |= BIT(node->id);
+ node->pispbe->sequence = 0;
+ }
dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
__func__, NODE_NAME(node), count);
@@ -897,17 +889,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
node->pispbe->streaming_map);
/* Maybe we're ready to run. */
- pispbe_schedule(pispbe, false);
+ if (!pispbe_prepare_job(pispbe))
+ pispbe_schedule(pispbe, false);
return 0;
err_return_buffers:
- spin_lock_irqsave(&pispbe->hw_lock, flags);
list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) {
list_del(&buf->ready_list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
}
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
return ret;
}
@@ -916,8 +907,9 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
{
struct pispbe_node *node = vb2_get_drv_priv(q);
struct pispbe_dev *pispbe = node->pispbe;
+ struct pispbe_job_descriptor *job, *temp;
struct pispbe_buffer *buf;
- unsigned long flags;
+ LIST_HEAD(tmp_list);
/*
* Now this is a bit awkward. In a simple M2M device we could just wait
@@ -929,11 +921,7 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
* This may return buffers out of order.
*/
dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
- spin_lock_irqsave(&pispbe->hw_lock, flags);
do {
- unsigned long flags1;
-
- spin_lock_irqsave(&node->ready_lock, flags1);
buf = list_first_entry_or_null(&node->ready_queue,
struct pispbe_buffer,
ready_list);
@@ -941,15 +929,26 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
list_del(&buf->ready_list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
- spin_unlock_irqrestore(&node->ready_lock, flags1);
} while (buf);
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
vb2_wait_for_all_buffers(&node->queue);
- spin_lock_irqsave(&pispbe->hw_lock, flags);
+ spin_lock_irq(&pispbe->hw_lock);
pispbe->streaming_map &= ~BIT(node->id);
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ if (pispbe->streaming_map == 0) {
+ /*
+ * If all nodes have stopped streaming release all jobs
+ * without holding the lock.
+ */
+ list_splice_init(&pispbe->job_queue, &tmp_list);
+ }
+ spin_unlock_irq(&pispbe->hw_lock);
+
+ list_for_each_entry_safe(job, temp, &tmp_list, queue) {
+ list_del(&job->queue);
+ kfree(job);
+ }
pm_runtime_mark_last_busy(pispbe->dev);
pm_runtime_put_autosuspend(pispbe->dev);
@@ -1114,10 +1113,12 @@ static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node)
f->fmt.pix_mp.pixelformat = fmt->fourcc;
f->fmt.pix_mp.num_planes = fmt->num_planes;
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
- f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u),
- PISP_BACK_END_MIN_TILE_WIDTH);
- f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u),
- PISP_BACK_END_MIN_TILE_HEIGHT);
+ f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width,
+ PISP_BACK_END_MIN_TILE_WIDTH,
+ PISP_BACK_END_MAX_TILE_WIDTH);
+ f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height,
+ PISP_BACK_END_MIN_TILE_HEIGHT,
+ PISP_BACK_END_MAX_TILE_HEIGHT);
/*
* Fill in the actual colour space when the requested one was
@@ -1407,7 +1408,6 @@ static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id)
mutex_init(&node->node_lock);
mutex_init(&node->queue_lock);
INIT_LIST_HEAD(&node->ready_queue);
- spin_lock_init(&node->ready_lock);
node->format.type = node->buf_type;
pispbe_node_def_fmt(node);
@@ -1691,6 +1691,8 @@ static int pispbe_probe(struct platform_device *pdev)
if (!pispbe)
return -ENOMEM;
+ INIT_LIST_HEAD(&pispbe->job_queue);
+
dev_set_drvdata(&pdev->dev, pispbe);
pispbe->dev = &pdev->dev;
platform_set_drvdata(pdev, pispbe);
@@ -1726,7 +1728,7 @@ static int pispbe_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(pispbe->dev);
pm_runtime_enable(pispbe->dev);
- ret = pispbe_runtime_resume(pispbe->dev);
+ ret = pm_runtime_resume_and_get(pispbe->dev);
if (ret)
goto pm_runtime_disable_err;
@@ -1748,7 +1750,7 @@ static int pispbe_probe(struct platform_device *pdev)
disable_devs_err:
pispbe_destroy_devices(pispbe);
pm_runtime_suspend_err:
- pispbe_runtime_suspend(pispbe->dev);
+ pm_runtime_put(pispbe->dev);
pm_runtime_disable_err:
pm_runtime_dont_use_autosuspend(pispbe->dev);
pm_runtime_disable(pispbe->dev);
@@ -1762,7 +1764,6 @@ static void pispbe_remove(struct platform_device *pdev)
pispbe_destroy_devices(pispbe);
- pispbe_runtime_suspend(pispbe->dev);
pm_runtime_dont_use_autosuspend(pispbe->dev);
pm_runtime_disable(pispbe->dev);
}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
index fcadb2143c88..62dca76b468d 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
@@ -1024,9 +1024,6 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
node->buffer_queue.type);
- if (vq->max_num_buffers + *nbuffers < 3)
- *nbuffers = 3 - vq->max_num_buffers;
-
if (*nplanes) {
if (sizes[0] < size) {
cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size);
@@ -1998,6 +1995,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id)
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->lock;
q->min_queued_buffers = 1;
+ q->min_reqbufs_allocation = 3;
q->dev = &cfe->pdev->dev;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 9979de4f6ef1..d1b31ab8b8c4 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -172,20 +172,27 @@ struct rcar_csi2;
#define V4H_PPI_RW_LPDCOCAL_TWAIT_CONFIG_REG 0x21c0a
#define V4H_PPI_RW_LPDCOCAL_VT_CONFIG_REG 0x21c0c
#define V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG 0x21c10
+#define V4H_PPI_RW_DDLCAL_CFG_n_REG(n) (0x21c40 + ((n) * 2)) /* n = 0 - 7 */
#define V4H_PPI_RW_COMMON_CFG_REG 0x21c6c
#define V4H_PPI_RW_TERMCAL_CFG_0_REG 0x21c80
#define V4H_PPI_RW_OFFSETCAL_CFG_0_REG 0x21ca0
/* V4H CORE registers */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(n) (0x22040 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(n) (0x22440 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(n) (0x22840 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(n) (0x22c40 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(n) (0x23040 + ((n) * 2)) /* n = 0 - 15 */
+
+#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, n) (0x22040 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_3_REG(l, n) (0x22060 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_4_REG(l, n) (0x22080 + ((l) * 0x400) + ((n) * 2))
+
#define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */
#define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */
#define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */
-#define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c
+#define V4H_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM_REG 0x23fe0
+
+#define V4H_CORE_DIG_DLANE_l_RW_CFG_n_REG(l, n) (0x26000 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, n) (0x26080 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, n) (0x26100 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_DLANE_CLK_RW_LP_n_REG(n) V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(4, (n))
+#define V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(n) V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(4, (n))
/* V4H C-PHY */
#define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */
@@ -197,6 +204,7 @@ struct rcar_csi2;
#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400
#define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480
#define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */
+#define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c
#define V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG 0x2a800
#define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880
#define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */
@@ -954,6 +962,7 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
unsigned int lanes)
{
+ struct media_pad *remote_pad;
struct v4l2_subdev *source;
s64 freq;
u64 mbps;
@@ -962,8 +971,9 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
return -ENODEV;
source = priv->remote;
+ remote_pad = &source->entity.pads[priv->remote_pad];
- freq = v4l2_get_link_freq(source->ctrl_handler, bpp, 2 * lanes);
+ freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes);
if (freq < 0) {
int ret = (int)freq;
@@ -975,10 +985,6 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
mbps = div_u64(freq * 2, MEGA);
- /* Adjust for C-PHY, divide by 2.8. */
- if (priv->cphy)
- mbps = div_u64(mbps * 5, 14);
-
return mbps;
}
@@ -1203,9 +1209,14 @@ static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match)
return -ETIMEDOUT;
}
-static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
+static const struct rcsi2_cphy_setting *
+rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int mbps)
{
const struct rcsi2_cphy_setting *conf;
+ int msps;
+
+ /* Adjust for C-PHY symbols, divide by 2.8. */
+ msps = div_u64(mbps * 5, 14);
for (conf = cphy_setting_table_r8a779g0; conf->msps != 0; conf++) {
if (conf->msps > msps)
@@ -1214,7 +1225,7 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
if (!conf->msps) {
dev_err(priv->dev, "Unsupported PHY speed for msps setting (%u Msps)", msps);
- return -ERANGE;
+ return NULL;
}
/* C-PHY specific */
@@ -1246,11 +1257,11 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(2), conf->rx2);
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(2), conf->rx2);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(2), 0x0001);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(2), 0);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(2), 0x0001);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(2), 0x0001);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(2), 0);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 2), 0);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(3, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(4, 2), 0);
rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(0), conf->trio0);
rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(0), conf->trio0);
@@ -1267,30 +1278,198 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
/* Configure data line order. */
rsci2_set_line_order(priv, priv->line_orders[0],
V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG,
- V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9));
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 9));
rsci2_set_line_order(priv, priv->line_orders[1],
V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG,
- V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(9));
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 9));
rsci2_set_line_order(priv, priv->line_orders[2],
V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG,
- V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(9));
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 9));
/* TODO: This registers is not documented. */
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000);
- /* Leave Shutdown mode */
- rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0));
- rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0));
+ return conf;
+}
- /* Wait for calibration */
- if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) {
- dev_err(priv->dev, "PHY calibration failed\n");
- return -ETIMEDOUT;
+struct rcsi2_d_phy_setting_v4h_lut_value {
+ unsigned int mbps;
+ unsigned char cfg_1;
+ unsigned char cfg_5_94;
+ unsigned char cfg_5_30;
+ unsigned char lane_ctrl_2_8;
+ unsigned char rw_hs_rx_3_83;
+ unsigned char rw_hs_rx_3_20;
+ unsigned char rw_hs_rx_6;
+ unsigned char rw_hs_rx_1;
+};
+
+static const struct rcsi2_d_phy_setting_v4h_lut_value *
+rcsi2_d_phy_setting_v4h_lut_lookup(int mbps)
+{
+ static const struct rcsi2_d_phy_setting_v4h_lut_value values[] = {
+ { 4500, 0x3f, 0x07, 0x00, 0x01, 0x02, 0x01, 0x0d, 0x10 },
+ { 4000, 0x47, 0x08, 0x01, 0x01, 0x05, 0x01, 0x0f, 0x0d },
+ { 3600, 0x4f, 0x09, 0x01, 0x01, 0x06, 0x01, 0x10, 0x0b },
+ { 3230, 0x57, 0x0a, 0x01, 0x01, 0x06, 0x01, 0x12, 0x09 },
+ { 3000, 0x47, 0x08, 0x00, 0x00, 0x03, 0x01, 0x0f, 0x0c },
+ { 2700, 0x4f, 0x09, 0x01, 0x00, 0x06, 0x01, 0x10, 0x0b },
+ { 2455, 0x57, 0x0a, 0x01, 0x00, 0x06, 0x01, 0x12, 0x09 },
+ { 2250, 0x5f, 0x0b, 0x01, 0x00, 0x08, 0x01, 0x13, 0x08 },
+ { 2077, 0x67, 0x0c, 0x01, 0x00, 0x06, 0x02, 0x15, 0x0d },
+ { 1929, 0x6f, 0x0d, 0x02, 0x00, 0x06, 0x02, 0x17, 0x0d },
+ { 1800, 0x77, 0x0e, 0x02, 0x00, 0x06, 0x02, 0x18, 0x0d },
+ { 1688, 0x7f, 0x0f, 0x02, 0x00, 0x08, 0x02, 0x1a, 0x0d },
+ { 1588, 0x87, 0x10, 0x02, 0x00, 0x08, 0x02, 0x1b, 0x0d },
+ { 1500, 0x8f, 0x11, 0x03, 0x00, 0x08, 0x02, 0x1d, 0x0c },
+ };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(values); i++)
+ if (mbps >= values[i].mbps)
+ return &values[i];
+
+ return NULL;
+}
+
+static int rcsi2_d_phy_setting_v4h(struct rcar_csi2 *priv, int mbps)
+{
+ const struct rcsi2_d_phy_setting_v4h_lut_value *lut =
+ rcsi2_d_phy_setting_v4h_lut_lookup(mbps);
+ u16 val;
+
+ rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(7), 0x0000);
+ rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(7), mbps > 1500 ? 0x0028 : 0x0068);
+ rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(8), 0x0050);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(0), 0x0063);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(7), 0x1132);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(1), 0x1340);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(2), 0x4b13);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(4), 0x000a);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(6), 0x800a);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(7), 0x1109);
+
+ if (mbps > 1500) {
+ val = DIV_ROUND_UP(5 * mbps, 64);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(3), val);
+ }
+
+ if (lut) {
+ rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(1),
+ lut->cfg_1, 0x00ff);
+ rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(5),
+ lut->cfg_5_94 << 4, 0x03f0);
+ rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(5),
+ lut->cfg_5_30 << 0, 0x000f);
+
+ for (unsigned int l = 0; l < 5; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 8),
+ lut->lane_ctrl_2_8 << 12, 0x1000);
+ }
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, 0), 0x463c);
+
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 2), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 2), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(3, 2), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(4, 2), 0x0000);
+
+ rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(6), 0x0009);
+
+ val = mbps > 1500 ? 0x0800 : 0x0802;
+ for (unsigned int l = 0; l < 5; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 12), val);
+
+ val = mbps > 1500 ? 0x0000 : 0x0002;
+ for (unsigned int l = 0; l < 5; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 13), val);
+
+ if (mbps >= 80) {
+ /* 2560: 6, 1280: 5, 640: 4, 320: 3, 160: 2, 80: 1 */
+ val = ilog2(mbps / 80) + 1;
+ rcsi2_modify16(priv,
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 9),
+ val << 5, 0xe0);
+ }
+
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(0), 0x091c);
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(7), 0x3b06);
+
+ val = DIV_ROUND_UP(1200, mbps) + 12;
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 0), val << 8, 0xf0);
+
+ val = mbps > 1500 ? 0x0004 : 0x0008;
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_CFG_n_REG(l, 1), val);
+
+ val = mbps > 2500 ? 0x669a : mbps > 1500 ? 0xe69a : 0xe69b;
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 2), val);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, 0), 0x163c);
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_LP_n_REG(0), 0x163c);
+
+ if (lut) {
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 1),
+ lut->rw_hs_rx_1, 0xff);
+ }
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 3), 0x9209);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 4), 0x0096);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 5), 0x0100);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 6), 0x2d02);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 7), 0x1b06);
+
+ if (lut) {
+ /*
+ * Documentation LUT have two values but document writing both
+ * values in a single write.
+ */
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 3),
+ lut->rw_hs_rx_3_83 << 3 | lut->rw_hs_rx_3_20, 0x1ff);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 6),
+ lut->rw_hs_rx_6 << 8, 0xff00);
}
- /* C-PHY setting - analog programing*/
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9), conf->lane29);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(7), conf->lane27);
+ static const u16 deskew_fine[] = {
+ 0x0404, 0x040c, 0x0414, 0x041c, 0x0423, 0x0429, 0x0430, 0x043a,
+ 0x0445, 0x044a, 0x0450, 0x045a, 0x0465, 0x0469, 0x0472, 0x047a,
+ 0x0485, 0x0489, 0x0490, 0x049a, 0x04a4, 0x04ac, 0x04b4, 0x04bc,
+ 0x04c4, 0x04cc, 0x04d4, 0x04dc, 0x04e4, 0x04ec, 0x04f4, 0x04fc,
+ 0x0504, 0x050c, 0x0514, 0x051c, 0x0523, 0x0529, 0x0530, 0x053a,
+ 0x0545, 0x054a, 0x0550, 0x055a, 0x0565, 0x0569, 0x0572, 0x057a,
+ 0x0585, 0x0589, 0x0590, 0x059a, 0x05a4, 0x05ac, 0x05b4, 0x05bc,
+ 0x05c4, 0x05cc, 0x05d4, 0x05dc, 0x05e4, 0x05ec, 0x05f4, 0x05fc,
+ 0x0604, 0x060c, 0x0614, 0x061c, 0x0623, 0x0629, 0x0632, 0x063a,
+ 0x0645, 0x064a, 0x0650, 0x065a, 0x0665, 0x0669, 0x0672, 0x067a,
+ 0x0685, 0x0689, 0x0690, 0x069a, 0x06a4, 0x06ac, 0x06b4, 0x06bc,
+ 0x06c4, 0x06cc, 0x06d4, 0x06dc, 0x06e4, 0x06ec, 0x06f4, 0x06fc,
+ 0x0704, 0x070c, 0x0714, 0x071c, 0x0723, 0x072a, 0x0730, 0x073a,
+ 0x0745, 0x074a, 0x0750, 0x075a, 0x0765, 0x0769, 0x0772, 0x077a,
+ 0x0785, 0x0789, 0x0790, 0x079a, 0x07a4, 0x07ac, 0x07b4, 0x07bc,
+ 0x07c4, 0x07cc, 0x07d4, 0x07dc, 0x07e4, 0x07ec, 0x07f4, 0x07fc,
+ };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(deskew_fine); i++) {
+ rcsi2_write16(priv, V4H_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM_REG,
+ deskew_fine[i]);
+ }
return 0;
}
@@ -1298,10 +1477,11 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
struct v4l2_subdev_state *state)
{
+ const struct rcsi2_cphy_setting *cphy = NULL;
const struct rcar_csi2_format *format;
const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
- int msps;
+ int mbps;
int ret;
/* Use the format on the sink pad to compute the receiver config. */
@@ -1314,28 +1494,40 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
if (ret)
return ret;
- msps = rcsi2_calc_mbps(priv, format->bpp, lanes);
- if (msps < 0)
- return msps;
+ mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ if (mbps < 0)
+ return mbps;
- /* Reset LINK and PHY*/
+ /* T0: Reset LINK and PHY*/
rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0);
rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0);
rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0);
- /* PHY static setting */
- rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK);
+ /* T1: PHY static setting */
+ rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK |
+ V4H_PHY_EN_ENABLE_0 | V4H_PHY_EN_ENABLE_1 |
+ V4H_PHY_EN_ENABLE_2 | V4H_PHY_EN_ENABLE_3);
rcsi2_write(priv, V4H_FLDC_REG, 0);
rcsi2_write(priv, V4H_FLDD_REG, 0);
rcsi2_write(priv, V4H_IDIC_REG, 0);
- rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_CPHY);
+ rcsi2_write(priv, V4H_PHY_MODE_REG,
+ priv->cphy ? V4H_PHY_MODE_CPHY : V4H_PHY_MODE_DPHY);
rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1);
- /* Reset CSI2 */
+ rcsi2_write(priv, V4M_FRXM_REG,
+ V4M_FRXM_FORCERXMODE_0 | V4M_FRXM_FORCERXMODE_1 |
+ V4M_FRXM_FORCERXMODE_2 | V4M_FRXM_FORCERXMODE_3);
+ rcsi2_write(priv, V4M_OVR1_REG,
+ V4M_OVR1_FORCERXMODE_0 | V4M_OVR1_FORCERXMODE_1 |
+ V4M_OVR1_FORCERXMODE_2 | V4M_OVR1_FORCERXMODE_3);
+
+ /* T2: Reset CSI2 */
rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0));
/* Registers static setting through APB */
/* Common setting */
+ rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(10), 0x0030);
+ rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(2), 0x1444);
rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(0), 0x1bfd);
rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_STARTUP_1_1_REG, 0x0233);
rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(6), 0x0027);
@@ -1350,20 +1542,71 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG, 0x0105);
rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x1000);
rcsi2_write16(priv, V4H_PPI_RW_COMMON_CFG_REG, 0x0003);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(0), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(1), 0x0400);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(3), 0x41f6);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(0), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(3), 0x43f6);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x3000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(7), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x7000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(7), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(5), 0x4000);
+
+ /* T3: PHY settings */
+ if (priv->cphy) {
+ cphy = rcsi2_c_phy_setting_v4h(priv, mbps);
+ if (!cphy)
+ return -ERANGE;
+ } else {
+ ret = rcsi2_d_phy_setting_v4h(priv, mbps);
+ if (ret)
+ return ret;
+ }
- /* C-PHY settings */
- ret = rcsi2_c_phy_setting_v4h(priv, msps);
- if (ret)
- return ret;
+ /* T4: Leave Shutdown mode */
+ rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0));
+ rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0));
+
+ /* T5: Wait for calibration */
+ if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) {
+ dev_err(priv->dev, "PHY calibration failed\n");
+ return -ETIMEDOUT;
+ }
+
+ /* T6: Analog programming */
+ if (priv->cphy) {
+ for (unsigned int l = 0; l < 3; l++) {
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 9),
+ cphy->lane29);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 7),
+ cphy->lane27);
+ }
+ } else {
+ u16 val_2_9 = mbps > 2500 ? 0x14 : mbps > 1500 ? 0x04 : 0x00;
+ u16 val_2_15 = mbps > 1500 ? 0x03 : 0x00;
+
+ for (unsigned int l = 0; l < 5; l++) {
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 9),
+ val_2_9);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 15),
+ val_2_15);
+ }
+ }
+ /* T7: Wait for stop state */
rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_STOPSTATE_0 |
V4H_ST_PHYST_ST_STOPSTATE_1 |
- V4H_ST_PHYST_ST_STOPSTATE_2);
+ V4H_ST_PHYST_ST_STOPSTATE_2 |
+ V4H_ST_PHYST_ST_STOPSTATE_3);
+
+ /* T8: De-assert FRXM */
+ rcsi2_write(priv, V4M_FRXM_REG, 0);
return 0;
}
-static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int data_rate)
+static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int mbps)
{
unsigned int timeout;
int ret;
@@ -2213,6 +2456,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = {
.start_receiver = rcsi2_start_receiver_v4h,
.use_isp = true,
.support_cphy = true,
+ .support_dphy = true,
};
static const struct rcsi2_register_layout rcsi2_registers_v4m = {
diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c
index cee9bbce4e3a..f90c86bbce6e 100644
--- a/drivers/media/platform/renesas/rcar-fcp.c
+++ b/drivers/media/platform/renesas/rcar-fcp.c
@@ -9,6 +9,8 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@@ -19,14 +21,25 @@
#include <media/rcar-fcp.h>
+#define RCAR_FCP_REG_RST 0x0010
+#define RCAR_FCP_REG_RST_SOFTRST BIT(0)
+#define RCAR_FCP_REG_STA 0x0018
+#define RCAR_FCP_REG_STA_ACT BIT(0)
+
struct rcar_fcp_device {
struct list_head list;
struct device *dev;
+ void __iomem *base;
};
static LIST_HEAD(fcp_devices);
static DEFINE_MUTEX(fcp_lock);
+static inline void rcar_fcp_write(struct rcar_fcp_device *fcp, u32 reg, u32 val)
+{
+ iowrite32(val, fcp->base + reg);
+}
+
/* -----------------------------------------------------------------------------
* Public API
*/
@@ -117,6 +130,25 @@ void rcar_fcp_disable(struct rcar_fcp_device *fcp)
}
EXPORT_SYMBOL_GPL(rcar_fcp_disable);
+int rcar_fcp_soft_reset(struct rcar_fcp_device *fcp)
+{
+ u32 value;
+ int ret;
+
+ if (!fcp)
+ return 0;
+
+ rcar_fcp_write(fcp, RCAR_FCP_REG_RST, RCAR_FCP_REG_RST_SOFTRST);
+ ret = readl_poll_timeout(fcp->base + RCAR_FCP_REG_STA,
+ value, !(value & RCAR_FCP_REG_STA_ACT),
+ 1, 100);
+ if (ret)
+ dev_err(fcp->dev, "Failed to soft-reset\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_soft_reset);
+
/* -----------------------------------------------------------------------------
* Platform Driver
*/
@@ -131,6 +163,10 @@ static int rcar_fcp_probe(struct platform_device *pdev)
fcp->dev = &pdev->dev;
+ fcp->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(fcp->base))
+ return PTR_ERR(fcp->base);
+
dma_set_max_seg_size(fcp->dev, UINT_MAX);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index 846ae7989b1d..f73729f59671 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
@@ -2,14 +2,14 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
+#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
@@ -55,6 +55,7 @@
* be only one group for all instances.
*/
+static DEFINE_IDA(rvin_ida);
static DEFINE_MUTEX(rvin_group_lock);
static struct rvin_group *rvin_group_data;
@@ -65,7 +66,7 @@ static void rvin_group_cleanup(struct rvin_group *group)
}
static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin,
- int (*link_setup)(struct rvin_dev *),
+ int (*link_setup)(struct rvin_group *),
const struct media_device_ops *ops)
{
struct media_device *mdev = &group->mdev;
@@ -115,27 +116,12 @@ static void rvin_group_release(struct kref *kref)
}
static int rvin_group_get(struct rvin_dev *vin,
- int (*link_setup)(struct rvin_dev *),
+ int (*link_setup)(struct rvin_group *),
const struct media_device_ops *ops)
{
struct rvin_group *group;
- u32 id;
int ret;
- /* Make sure VIN id is present and sane */
- ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id);
- if (ret) {
- vin_err(vin, "%pOF: No renesas,id property found\n",
- vin->dev->of_node);
- return -EINVAL;
- }
-
- if (id >= RCAR_VIN_NUM) {
- vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
- vin->dev->of_node, id);
- return -EINVAL;
- }
-
/* Join or create a VIN group */
mutex_lock(&rvin_group_lock);
if (rvin_group_data) {
@@ -156,6 +142,7 @@ static int rvin_group_get(struct rvin_dev *vin,
}
kref_init(&group->refcount);
+ group->info = vin->info;
rvin_group_data = group;
}
@@ -164,16 +151,15 @@ static int rvin_group_get(struct rvin_dev *vin,
/* Add VIN to group */
mutex_lock(&group->lock);
- if (group->vin[id]) {
- vin_err(vin, "Duplicate renesas,id property value %u\n", id);
+ if (group->vin[vin->id]) {
+ vin_err(vin, "Duplicate renesas,id property value %u\n", vin->id);
mutex_unlock(&group->lock);
kref_put(&group->refcount, rvin_group_release);
return -EINVAL;
}
- group->vin[id] = vin;
+ group->vin[vin->id] = vin;
- vin->id = id;
vin->group = group;
vin->v4l2_dev.mdev = &group->mdev;
@@ -213,7 +199,7 @@ static int rvin_group_entity_to_remote_id(struct rvin_group *group,
sd = media_entity_to_v4l2_subdev(entity);
- for (i = 0; i < RVIN_REMOTES_MAX; i++)
+ for (i = 0; i < ARRAY_SIZE(group->remotes); i++)
if (group->remotes[i].subdev == sd)
return i;
@@ -246,7 +232,7 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
}
}
- return vin->group->link_setup(vin);
+ return vin->group->link_setup(vin->group);
}
static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -254,20 +240,32 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
struct v4l2_async_connection *asc)
{
struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- unsigned int i;
+ struct rvin_group *group = vin->group;
- for (i = 0; i < RCAR_VIN_NUM; i++)
- if (vin->group->vin[i])
- rvin_v4l2_unregister(vin->group->vin[i]);
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ if (group->vin[i])
+ rvin_v4l2_unregister(group->vin[i]);
+ }
mutex_lock(&vin->group->lock);
- for (i = 0; i < RVIN_CSI_MAX; i++) {
- if (vin->group->remotes[i].asc != asc)
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ if (!group->vin[i] || group->vin[i]->parallel.asc != asc)
continue;
- vin->group->remotes[i].subdev = NULL;
+
+ group->vin[i]->parallel.subdev = NULL;
+
+ vin_dbg(group->vin[i], "Unbind parallel subdev %s\n",
+ subdev->name);
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
+ if (group->remotes[i].asc != asc)
+ continue;
+
+ group->remotes[i].subdev = NULL;
+
vin_dbg(vin, "Unbind %s from slot %u\n", subdev->name, i);
- break;
}
mutex_unlock(&vin->group->lock);
@@ -280,21 +278,38 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_async_connection *asc)
{
struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- unsigned int i;
+ struct rvin_group *group = vin->group;
- mutex_lock(&vin->group->lock);
+ guard(mutex)(&group->lock);
- for (i = 0; i < RVIN_CSI_MAX; i++) {
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ struct rvin_dev *pvin = group->vin[i];
+
+ if (!pvin || pvin->parallel.asc != asc)
+ continue;
+
+ pvin->parallel.source_pad = 0;
+ for (unsigned int pad = 0; pad < subdev->entity.num_pads; pad++)
+ if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)
+ pvin->parallel.source_pad = pad;
+
+ pvin->parallel.subdev = subdev;
+ vin_dbg(pvin, "Bound subdev %s\n", subdev->name);
+
+ return 0;
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
if (vin->group->remotes[i].asc != asc)
continue;
+
vin->group->remotes[i].subdev = subdev;
vin_dbg(vin, "Bound %s to slot %u\n", subdev->name, i);
- break;
- }
- mutex_unlock(&vin->group->lock);
+ return 0;
+ }
- return 0;
+ return -ENODEV;
}
static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
@@ -343,12 +358,49 @@ out:
return ret;
}
-static void rvin_group_notifier_cleanup(struct rvin_dev *vin)
+static int rvin_parallel_parse_of(struct rvin_dev *vin)
{
- if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
- v4l2_async_nf_unregister(&vin->group->notifier);
- v4l2_async_nf_cleanup(&vin->group->notifier);
+ struct fwnode_handle *fwnode __free(fwnode_handle) = NULL;
+ struct fwnode_handle *ep __free(fwnode_handle) = NULL;
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_UNKNOWN,
+ };
+ struct v4l2_async_connection *asc;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0);
+ if (!ep)
+ return 0;
+
+ if (v4l2_fwnode_endpoint_parse(ep, &vep)) {
+ vin_err(vin, "Failed to parse %pOF\n", to_of_node(ep));
+ return -EINVAL;
}
+
+ switch (vep.bus_type) {
+ case V4L2_MBUS_PARALLEL:
+ case V4L2_MBUS_BT656:
+ vin_dbg(vin, "Found %s media bus\n",
+ vep.bus_type == V4L2_MBUS_PARALLEL ?
+ "PARALLEL" : "BT656");
+ vin->parallel.mbus_type = vep.bus_type;
+ vin->parallel.bus = vep.bus.parallel;
+ break;
+ default:
+ vin_err(vin, "Unknown media bus type\n");
+ return -EINVAL;
+ }
+
+ fwnode = fwnode_graph_get_remote_endpoint(ep);
+ asc = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc))
+ return PTR_ERR(asc);
+
+ vin->parallel.asc = asc;
+
+ vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode));
+
+ return 0;
}
static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port,
@@ -385,6 +437,12 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port,
if (!(vin_mask & BIT(i)))
continue;
+ /* Parse local subdevice. */
+ ret = rvin_parallel_parse_of(vin->group->vin[i]);
+ if (ret)
+ return ret;
+
+ /* Parse shared subdevices. */
for (id = 0; id < max_id; id++) {
if (vin->group->remotes[id].asc)
continue;
@@ -437,11 +495,11 @@ static void rvin_free_controls(struct rvin_dev *vin)
vin->vdev.ctrl_handler = NULL;
}
-static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev)
+static int rvin_create_controls(struct rvin_dev *vin)
{
int ret;
- ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
+ ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 1);
if (ret < 0)
return ret;
@@ -455,287 +513,12 @@ static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev
return ret;
}
- /* For the non-MC mode add controls from the subdevice. */
- if (subdev) {
- ret = v4l2_ctrl_add_handler(&vin->ctrl_handler,
- subdev->ctrl_handler, NULL, true);
- if (ret < 0) {
- rvin_free_controls(vin);
- return ret;
- }
- }
-
vin->vdev.ctrl_handler = &vin->ctrl_handler;
return 0;
}
/* -----------------------------------------------------------------------------
- * Async notifier
- */
-
-static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
-{
- unsigned int pad;
-
- if (sd->entity.num_pads <= 1)
- return 0;
-
- for (pad = 0; pad < sd->entity.num_pads; pad++)
- if (sd->entity.pads[pad].flags & direction)
- return pad;
-
- return -EINVAL;
-}
-
-/* -----------------------------------------------------------------------------
- * Parallel async notifier
- */
-
-/* The vin lock should be held when calling the subdevice attach and detach */
-static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
- struct v4l2_subdev *subdev)
-{
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret;
-
- /* Find source and sink pad of remote subdevice */
- ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
- if (ret < 0)
- return ret;
- vin->parallel.source_pad = ret;
-
- ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
- vin->parallel.sink_pad = ret < 0 ? 0 : ret;
-
- if (vin->info->use_mc) {
- vin->parallel.subdev = subdev;
- return 0;
- }
-
- /* Find compatible subdevices mbus format */
- vin->mbus_code = 0;
- code.index = 0;
- code.pad = vin->parallel.source_pad;
- while (!vin->mbus_code &&
- !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) {
- code.index++;
- switch (code.code) {
- case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_UYVY8_2X8:
- case MEDIA_BUS_FMT_UYVY10_2X10:
- case MEDIA_BUS_FMT_RGB888_1X24:
- vin->mbus_code = code.code;
- vin_dbg(vin, "Found media bus format for %s: %d\n",
- subdev->name, vin->mbus_code);
- break;
- default:
- break;
- }
- }
-
- if (!vin->mbus_code) {
- vin_err(vin, "Unsupported media bus format for %s\n",
- subdev->name);
- return -EINVAL;
- }
-
- /* Read tvnorms */
- ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- /* Read standard */
- vin->std = V4L2_STD_UNKNOWN;
- ret = v4l2_subdev_call(subdev, video, g_std, &vin->std);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- /* Add the controls */
- ret = rvin_create_controls(vin, subdev);
- if (ret < 0)
- return ret;
-
- vin->parallel.subdev = subdev;
-
- return 0;
-}
-
-static void rvin_parallel_subdevice_detach(struct rvin_dev *vin)
-{
- rvin_v4l2_unregister(vin);
- vin->parallel.subdev = NULL;
-
- if (!vin->info->use_mc)
- rvin_free_controls(vin);
-}
-
-static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier)
-{
- struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- struct media_entity *source;
- struct media_entity *sink;
- int ret;
-
- ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
- if (ret < 0) {
- vin_err(vin, "Failed to register subdev nodes\n");
- return ret;
- }
-
- if (!video_is_registered(&vin->vdev)) {
- ret = rvin_v4l2_register(vin);
- if (ret < 0)
- return ret;
- }
-
- if (!vin->info->use_mc)
- return 0;
-
- /* If we're running with media-controller, link the subdevs. */
- source = &vin->parallel.subdev->entity;
- sink = &vin->vdev.entity;
-
- ret = media_create_pad_link(source, vin->parallel.source_pad,
- sink, vin->parallel.sink_pad, 0);
- if (ret)
- vin_err(vin, "Error adding link from %s to %s: %d\n",
- source->name, sink->name, ret);
-
- return ret;
-}
-
-static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asc)
-{
- struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
-
- vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name);
-
- mutex_lock(&vin->lock);
- rvin_parallel_subdevice_detach(vin);
- mutex_unlock(&vin->lock);
-}
-
-static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asc)
-{
- struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- int ret;
-
- mutex_lock(&vin->lock);
- ret = rvin_parallel_subdevice_attach(vin, subdev);
- mutex_unlock(&vin->lock);
- if (ret)
- return ret;
-
- v4l2_set_subdev_hostdata(subdev, vin);
-
- vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
- subdev->name, vin->parallel.source_pad,
- vin->parallel.sink_pad);
-
- return 0;
-}
-
-static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = {
- .bound = rvin_parallel_notify_bound,
- .unbind = rvin_parallel_notify_unbind,
- .complete = rvin_parallel_notify_complete,
-};
-
-static int rvin_parallel_parse_of(struct rvin_dev *vin)
-{
- struct fwnode_handle *ep, *fwnode;
- struct v4l2_fwnode_endpoint vep = {
- .bus_type = V4L2_MBUS_UNKNOWN,
- };
- struct v4l2_async_connection *asc;
- int ret;
-
- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0);
- if (!ep)
- return 0;
-
- fwnode = fwnode_graph_get_remote_endpoint(ep);
- ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- fwnode_handle_put(ep);
- if (ret) {
- vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode));
- ret = -EINVAL;
- goto out;
- }
-
- switch (vep.bus_type) {
- case V4L2_MBUS_PARALLEL:
- case V4L2_MBUS_BT656:
- vin_dbg(vin, "Found %s media bus\n",
- vep.bus_type == V4L2_MBUS_PARALLEL ?
- "PARALLEL" : "BT656");
- vin->parallel.mbus_type = vep.bus_type;
- vin->parallel.bus = vep.bus.parallel;
- break;
- default:
- vin_err(vin, "Unknown media bus type\n");
- ret = -EINVAL;
- goto out;
- }
-
- asc = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode,
- struct v4l2_async_connection);
- if (IS_ERR(asc)) {
- ret = PTR_ERR(asc);
- goto out;
- }
-
- vin->parallel.asc = asc;
-
- vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode));
-out:
- fwnode_handle_put(fwnode);
-
- return ret;
-}
-
-static void rvin_parallel_cleanup(struct rvin_dev *vin)
-{
- v4l2_async_nf_unregister(&vin->notifier);
- v4l2_async_nf_cleanup(&vin->notifier);
-}
-
-static int rvin_parallel_init(struct rvin_dev *vin)
-{
- int ret;
-
- v4l2_async_nf_init(&vin->notifier, &vin->v4l2_dev);
-
- ret = rvin_parallel_parse_of(vin);
- if (ret)
- return ret;
-
- if (!vin->parallel.asc)
- return -ENODEV;
-
- vin_dbg(vin, "Found parallel subdevice %pOF\n",
- to_of_node(vin->parallel.asc->match.fwnode));
-
- vin->notifier.ops = &rvin_parallel_notify_ops;
- ret = v4l2_async_nf_register(&vin->notifier);
- if (ret < 0) {
- vin_err(vin, "Notifier registration failed\n");
- v4l2_async_nf_cleanup(&vin->notifier);
- return ret;
- }
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
* CSI-2
*/
@@ -909,80 +692,91 @@ static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id,
return 0;
}
-static int rvin_csi2_setup_links(struct rvin_dev *vin)
+static int rvin_parallel_setup_links(struct rvin_group *group)
+{
+ u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
+
+ guard(mutex)(&group->lock);
+
+ /* If the group also has links don't enable the link. */
+ for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
+ if (group->remotes[i].subdev) {
+ flags = 0;
+ break;
+ }
+ }
+
+ /* Create links. */
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ struct rvin_dev *vin = group->vin[i];
+ struct media_entity *source;
+ struct media_entity *sink;
+ int ret;
+
+ /* Nothing to do if there is no VIN or parallel subdev. */
+ if (!vin || !vin->parallel.subdev)
+ continue;
+
+ source = &vin->parallel.subdev->entity;
+ sink = &vin->vdev.entity;
+
+ ret = media_create_pad_link(source, vin->parallel.source_pad,
+ sink, 0, flags);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rvin_csi2_setup_links(struct rvin_group *group)
{
const struct rvin_group_route *route;
unsigned int id;
- int ret = -EINVAL;
+ int ret;
+
+ ret = rvin_parallel_setup_links(group);
+ if (ret)
+ return ret;
/* Create all media device links between VINs and CSI-2's. */
- mutex_lock(&vin->group->lock);
- for (route = vin->info->routes; route->chsel; route++) {
+ mutex_lock(&group->lock);
+ for (route = group->info->routes; route->chsel; route++) {
/* Check that VIN' master is part of the group. */
- if (!vin->group->vin[route->master])
+ if (!group->vin[route->master])
continue;
/* Check that CSI-2 is part of the group. */
- if (!vin->group->remotes[route->csi].subdev)
+ if (!group->remotes[route->csi].subdev)
continue;
for (id = route->master; id < route->master + 4; id++) {
/* Check that VIN is part of the group. */
- if (!vin->group->vin[id])
+ if (!group->vin[id])
continue;
- ret = rvin_csi2_create_link(vin->group, id, route);
+ ret = rvin_csi2_create_link(group, id, route);
if (ret)
goto out;
}
}
out:
- mutex_unlock(&vin->group->lock);
+ mutex_unlock(&group->lock);
return ret;
}
-static void rvin_csi2_cleanup(struct rvin_dev *vin)
-{
- rvin_parallel_cleanup(vin);
- rvin_group_notifier_cleanup(vin);
- rvin_group_put(vin);
- rvin_free_controls(vin);
-}
-
static int rvin_csi2_init(struct rvin_dev *vin)
{
int ret;
- vin->pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
- if (ret)
- return ret;
-
- ret = rvin_create_controls(vin, NULL);
- if (ret < 0)
- return ret;
-
ret = rvin_group_get(vin, rvin_csi2_setup_links, &rvin_csi2_media_ops);
if (ret)
- goto err_controls;
-
- /* It's OK to not have a parallel subdevice. */
- ret = rvin_parallel_init(vin);
- if (ret && ret != -ENODEV)
- goto err_group;
+ return ret;
ret = rvin_group_notifier_init(vin, 1, RVIN_CSI_MAX);
if (ret)
- goto err_parallel;
-
- return 0;
-err_parallel:
- rvin_parallel_cleanup(vin);
-err_group:
- rvin_group_put(vin);
-err_controls:
- rvin_free_controls(vin);
+ rvin_group_put(vin);
return ret;
}
@@ -991,30 +785,31 @@ err_controls:
* ISP
*/
-static int rvin_isp_setup_links(struct rvin_dev *vin)
+static int rvin_isp_setup_links(struct rvin_group *group)
{
unsigned int i;
int ret = -EINVAL;
/* Create all media device links between VINs and ISP's. */
- mutex_lock(&vin->group->lock);
+ mutex_lock(&group->lock);
for (i = 0; i < RCAR_VIN_NUM; i++) {
struct media_pad *source_pad, *sink_pad;
struct media_entity *source, *sink;
unsigned int source_slot = i / 8;
unsigned int source_idx = i % 8 + 1;
+ struct rvin_dev *vin = group->vin[i];
- if (!vin->group->vin[i])
+ if (!vin)
continue;
/* Check that ISP is part of the group. */
- if (!vin->group->remotes[source_slot].subdev)
+ if (!group->remotes[source_slot].subdev)
continue;
- source = &vin->group->remotes[source_slot].subdev->entity;
+ source = &group->remotes[source_slot].subdev->entity;
source_pad = &source->pads[source_idx];
- sink = &vin->group->vin[i]->vdev.entity;
+ sink = &vin->vdev.entity;
sink_pad = &sink->pads[0];
/* Skip if link already exists. */
@@ -1030,44 +825,22 @@ static int rvin_isp_setup_links(struct rvin_dev *vin)
break;
}
}
- mutex_unlock(&vin->group->lock);
+ mutex_unlock(&group->lock);
return ret;
}
-static void rvin_isp_cleanup(struct rvin_dev *vin)
-{
- rvin_group_notifier_cleanup(vin);
- rvin_group_put(vin);
- rvin_free_controls(vin);
-}
-
static int rvin_isp_init(struct rvin_dev *vin)
{
int ret;
- vin->pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
- if (ret)
- return ret;
-
- ret = rvin_create_controls(vin, NULL);
- if (ret < 0)
- return ret;
-
ret = rvin_group_get(vin, rvin_isp_setup_links, NULL);
if (ret)
- goto err_controls;
+ return ret;
ret = rvin_group_notifier_init(vin, 2, RVIN_ISP_MAX);
if (ret)
- goto err_group;
-
- return 0;
-err_group:
- rvin_group_put(vin);
-err_controls:
- rvin_free_controls(vin);
+ rvin_group_put(vin);
return ret;
}
@@ -1102,7 +875,7 @@ static int __maybe_unused rvin_resume(struct device *dev)
* as we don't know if and in which order the master VINs will
* be resumed.
*/
- if (vin->info->use_mc) {
+ if (vin->info->model == RCAR_GEN3) {
unsigned int master_id = rvin_group_id_to_master(vin->id);
struct rvin_dev *master = vin->group->vin[master_id];
int ret;
@@ -1124,7 +897,6 @@ static int __maybe_unused rvin_resume(struct device *dev)
static const struct rvin_info rcar_info_h1 = {
.model = RCAR_H1,
- .use_mc = false,
.max_width = 2048,
.max_height = 2048,
.scaler = rvin_scaler_gen2,
@@ -1132,7 +904,6 @@ static const struct rvin_info rcar_info_h1 = {
static const struct rvin_info rcar_info_m1 = {
.model = RCAR_M1,
- .use_mc = false,
.max_width = 2048,
.max_height = 2048,
.scaler = rvin_scaler_gen2,
@@ -1140,7 +911,6 @@ static const struct rvin_info rcar_info_m1 = {
static const struct rvin_info rcar_info_gen2 = {
.model = RCAR_GEN2,
- .use_mc = false,
.max_width = 2048,
.max_height = 2048,
.scaler = rvin_scaler_gen2,
@@ -1155,7 +925,6 @@ static const struct rvin_group_route rcar_info_r8a774e1_routes[] = {
static const struct rvin_info rcar_info_r8a774e1 = {
.model = RCAR_GEN3,
- .use_mc = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a774e1_routes,
@@ -1171,7 +940,6 @@ static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
static const struct rvin_info rcar_info_r8a7795 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1189,7 +957,6 @@ static const struct rvin_group_route rcar_info_r8a7796_routes[] = {
static const struct rvin_info rcar_info_r8a7796 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1207,7 +974,6 @@ static const struct rvin_group_route rcar_info_r8a77965_routes[] = {
static const struct rvin_info rcar_info_r8a77965 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1222,7 +988,6 @@ static const struct rvin_group_route rcar_info_r8a77970_routes[] = {
static const struct rvin_info rcar_info_r8a77970 = {
.model = RCAR_GEN3,
- .use_mc = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a77970_routes,
@@ -1236,7 +1001,6 @@ static const struct rvin_group_route rcar_info_r8a77980_routes[] = {
static const struct rvin_info rcar_info_r8a77980 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1250,7 +1014,6 @@ static const struct rvin_group_route rcar_info_r8a77990_routes[] = {
static const struct rvin_info rcar_info_r8a77990 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1264,7 +1027,6 @@ static const struct rvin_group_route rcar_info_r8a77995_routes[] = {
static const struct rvin_info rcar_info_r8a77995 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1274,7 +1036,6 @@ static const struct rvin_info rcar_info_r8a77995 = {
static const struct rvin_info rcar_info_gen4 = {
.model = RCAR_GEN4,
- .use_mc = true,
.use_isp = true,
.nv12 = true,
.raw10 = true,
@@ -1361,6 +1122,56 @@ static const struct of_device_id rvin_of_id_table[] = {
};
MODULE_DEVICE_TABLE(of, rvin_of_id_table);
+static int rvin_id_get(struct rvin_dev *vin)
+{
+ u32 oid;
+ int id;
+
+ switch (vin->info->model) {
+ case RCAR_GEN3:
+ case RCAR_GEN4:
+ if (of_property_read_u32(vin->dev->of_node, "renesas,id", &oid)) {
+ vin_err(vin, "%pOF: No renesas,id property found\n",
+ vin->dev->of_node);
+ return -EINVAL;
+ }
+
+ if (oid < 0 || oid >= RCAR_VIN_NUM) {
+ vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
+ vin->dev->of_node, oid);
+ return -EINVAL;
+ }
+
+ vin->id = oid;
+ break;
+ default:
+ id = ida_alloc_range(&rvin_ida, 0, RCAR_VIN_NUM - 1,
+ GFP_KERNEL);
+ if (id < 0) {
+ vin_err(vin, "%pOF: Failed to allocate VIN group ID\n",
+ vin->dev->of_node);
+ return -EINVAL;
+ }
+
+ vin->id = id;
+ break;
+ }
+
+ return 0;
+}
+
+static void rvin_id_put(struct rvin_dev *vin)
+{
+ switch (vin->info->model) {
+ case RCAR_GEN3:
+ case RCAR_GEN4:
+ break;
+ default:
+ ida_free(&rvin_ida, vin->id);
+ break;
+ }
+}
+
static int rcar_vin_probe(struct platform_device *pdev)
{
struct rvin_dev *vin;
@@ -1388,30 +1199,59 @@ static int rcar_vin_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, vin);
- if (vin->info->use_isp) {
- ret = rvin_isp_init(vin);
- } else if (vin->info->use_mc) {
- ret = rvin_csi2_init(vin);
+ if (rvin_id_get(vin)) {
+ ret = -EINVAL;
+ goto err_dma;
+ }
- if (vin->info->scaler &&
- rvin_group_id_to_master(vin->id) == vin->id)
- vin->scaler = vin->info->scaler;
- } else {
- ret = rvin_parallel_init(vin);
+ vin->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
+ if (ret)
+ goto err_id;
+
+ ret = rvin_create_controls(vin);
+ if (ret < 0)
+ goto err_id;
+
+ switch (vin->info->model) {
+ case RCAR_GEN3:
+ case RCAR_GEN4:
+ if (vin->info->use_isp) {
+ ret = rvin_isp_init(vin);
+ } else {
+ ret = rvin_csi2_init(vin);
+
+ if (vin->info->scaler &&
+ rvin_group_id_to_master(vin->id) == vin->id)
+ vin->scaler = vin->info->scaler;
+ }
+ break;
+ default:
+ ret = rvin_group_get(vin, rvin_parallel_setup_links, NULL);
+ if (!ret)
+ ret = rvin_group_notifier_init(vin, 0, 0);
if (vin->info->scaler)
vin->scaler = vin->info->scaler;
+ break;
}
- if (ret) {
- rvin_dma_unregister(vin);
- return ret;
- }
+ if (ret)
+ goto err_ctrl;
pm_suspend_ignore_children(&pdev->dev, true);
pm_runtime_enable(&pdev->dev);
return 0;
+
+err_ctrl:
+ rvin_free_controls(vin);
+err_id:
+ rvin_id_put(vin);
+err_dma:
+ rvin_dma_unregister(vin);
+
+ return ret;
}
static void rcar_vin_remove(struct platform_device *pdev)
@@ -1422,12 +1262,16 @@ static void rcar_vin_remove(struct platform_device *pdev)
rvin_v4l2_unregister(vin);
- if (vin->info->use_isp)
- rvin_isp_cleanup(vin);
- else if (vin->info->use_mc)
- rvin_csi2_cleanup(vin);
- else
- rvin_parallel_cleanup(vin);
+ if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
+ v4l2_async_nf_unregister(&vin->group->notifier);
+ v4l2_async_nf_cleanup(&vin->group->notifier);
+ }
+
+ rvin_group_put(vin);
+
+ rvin_free_controls(vin);
+
+ rvin_id_put(vin);
rvin_dma_unregister(vin);
}
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index 5c08ee2c9807..b619d1436a41 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -2,18 +2,18 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
+#include <media/v4l2-event.h>
#include <media/videobuf2-dma-contig.h>
#include "rcar-vin.h"
@@ -115,11 +115,16 @@
#define VNFC_S_FRAME (1 << 0)
/* Video n Interrupt Enable Register bits */
-#define VNIE_FIE (1 << 4)
-#define VNIE_EFE (1 << 1)
+#define VNIE_VFE BIT(17)
+#define VNIE_VRE BIT(16)
+#define VNIE_FIE BIT(4)
+#define VNIE_EFE BIT(1)
/* Video n Interrupt Status Register bits */
-#define VNINTS_FIS (1 << 4)
+#define VNINTS_VFS BIT(17)
+#define VNINTS_VRS BIT(16)
+#define VNINTS_FIS BIT(4)
+#define VNINTS_EFS BIT(1)
/* Video n Data Mode Register bits */
#define VNDMR_A8BIT(n) (((n) & 0xff) << 24)
@@ -555,17 +560,12 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
void rvin_scaler_gen2(struct rvin_dev *vin)
{
- unsigned int crop_height;
u32 xs, ys;
/* Set scaling coefficient */
- crop_height = vin->crop.height;
- if (V4L2_FIELD_HAS_BOTH(vin->format.field))
- crop_height *= 2;
-
ys = 0;
- if (crop_height != vin->compose.height)
- ys = (4096 * crop_height) / vin->compose.height;
+ if (vin->crop.height != vin->compose.height)
+ ys = (4096 * vin->crop.height) / vin->compose.height;
rvin_write(vin, ys, VNYS_REG);
xs = 0;
@@ -700,9 +700,6 @@ static int rvin_setup(struct rvin_dev *vin)
case V4L2_FIELD_INTERLACED:
/* Default to TB */
vnmc = VNMC_IM_FULL;
- /* Use BT if video standard can be read and is 60 Hz format */
- if (!vin->info->use_mc && vin->std & V4L2_STD_525_60)
- vnmc = VNMC_IM_FULL | VNMC_FOC;
break;
case V4L2_FIELD_INTERLACED_TB:
vnmc = VNMC_IM_FULL;
@@ -897,6 +894,8 @@ static int rvin_setup(struct rvin_dev *vin)
/* Progressive or interlaced mode */
interrupts = progressive ? VNIE_FIE : VNIE_EFE;
+ /* Enable VSYNC Rising Edge Detection. */
+ interrupts |= VNIE_VRE;
/* Ack interrupts */
rvin_write(vin, interrupts, VNINTS_REG);
@@ -912,21 +911,6 @@ static int rvin_setup(struct rvin_dev *vin)
return 0;
}
-static void rvin_disable_interrupts(struct rvin_dev *vin)
-{
- rvin_write(vin, 0, VNIE_REG);
-}
-
-static u32 rvin_get_interrupt_status(struct rvin_dev *vin)
-{
- return rvin_read(vin, VNINTS_REG);
-}
-
-static void rvin_ack_interrupt(struct rvin_dev *vin)
-{
- rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG);
-}
-
static bool rvin_capture_active(struct rvin_dev *vin)
{
return rvin_read(vin, VNMS_REG) & VNMS_CA;
@@ -1049,22 +1033,35 @@ static void rvin_capture_stop(struct rvin_dev *vin)
static irqreturn_t rvin_irq(int irq, void *data)
{
struct rvin_dev *vin = data;
- u32 int_status, vnms;
+ u32 capture, status, vnms;
int slot;
unsigned int handled = 0;
unsigned long flags;
spin_lock_irqsave(&vin->qlock, flags);
- int_status = rvin_get_interrupt_status(vin);
- if (!int_status)
+ status = rvin_read(vin, VNINTS_REG);
+ if (!status)
goto done;
- rvin_ack_interrupt(vin);
+ rvin_write(vin, status, VNINTS_REG);
handled = 1;
+ /* Signal Start of Frame. */
+ if (status & VNINTS_VRS) {
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence = vin->sequence,
+ };
+
+ v4l2_event_queue(&vin->vdev, &event);
+ }
+
/* Nothing to do if nothing was captured. */
- if (!(int_status & VNINTS_FIS))
+ capture = vin->format.field == V4L2_FIELD_NONE ||
+ vin->format.field == V4L2_FIELD_ALTERNATE ?
+ VNINTS_FIS : VNINTS_EFS;
+ if (!(status & capture))
goto done;
/* Nothing to do if not running. */
@@ -1297,14 +1294,6 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
struct media_pad *pad;
int ret;
- /* No media controller used, simply pass operation to subdevice. */
- if (!vin->info->use_mc) {
- ret = v4l2_subdev_call(vin->parallel.subdev, video, s_stream,
- on);
-
- return ret == -ENOIOCTLCMD ? 0 : ret;
- }
-
pad = media_pad_remote_pad_first(&vin->pad);
if (!pad)
return -EPIPE;
@@ -1417,7 +1406,7 @@ void rvin_stop_streaming(struct rvin_dev *vin)
rvin_set_stream(vin, 0);
/* disable interrupts */
- rvin_disable_interrupts(vin);
+ rvin_write(vin, 0, VNIE_REG);
/* Return unprocessed buffers from hardware. */
for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) {
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index db091af57c19..62eddf3a35fc 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
@@ -2,12 +2,11 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
#include <linux/pm_runtime.h>
@@ -230,101 +229,6 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
* V4L2
*/
-static int rvin_reset_format(struct rvin_dev *vin)
-{
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = vin->parallel.source_pad,
- };
- int ret;
-
- ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
- if (ret)
- return ret;
-
- v4l2_fill_pix_format(&vin->format, &fmt.format);
-
- vin->crop.top = 0;
- vin->crop.left = 0;
- vin->crop.width = vin->format.width;
- vin->crop.height = vin->format.height;
-
- /* Make use of the hardware interlacer by default. */
- if (vin->format.field == V4L2_FIELD_ALTERNATE) {
- vin->format.field = V4L2_FIELD_INTERLACED;
- vin->format.height *= 2;
- }
-
- rvin_format_align(vin, &vin->format);
-
- vin->compose.top = 0;
- vin->compose.left = 0;
- vin->compose.width = vin->format.width;
- vin->compose.height = vin->format.height;
-
- return 0;
-}
-
-static int rvin_try_format(struct rvin_dev *vin, u32 which,
- struct v4l2_pix_format *pix,
- struct v4l2_rect *src_rect)
-{
- struct v4l2_subdev *sd = vin_to_source(vin);
- struct v4l2_subdev_state *sd_state;
- static struct lock_class_key key;
- struct v4l2_subdev_format format = {
- .which = which,
- .pad = vin->parallel.source_pad,
- };
- enum v4l2_field field;
- u32 width, height;
- int ret;
-
- /*
- * FIXME: Drop this call, drivers are not supposed to use
- * __v4l2_subdev_state_alloc().
- */
- sd_state = __v4l2_subdev_state_alloc(sd, "rvin:state->lock", &key);
- if (IS_ERR(sd_state))
- return PTR_ERR(sd_state);
-
- if (!rvin_format_from_pixel(vin, pix->pixelformat))
- pix->pixelformat = RVIN_DEFAULT_FORMAT;
-
- v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code);
-
- /* Allow the video device to override field and to scale */
- field = pix->field;
- width = pix->width;
- height = pix->height;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, sd_state, &format);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- goto done;
- ret = 0;
-
- v4l2_fill_pix_format(pix, &format.format);
-
- if (src_rect) {
- src_rect->top = 0;
- src_rect->left = 0;
- src_rect->width = pix->width;
- src_rect->height = pix->height;
- }
-
- if (field != V4L2_FIELD_ANY)
- pix->field = field;
-
- pix->width = width;
- pix->height = height;
-
- rvin_format_align(vin, pix);
-done:
- __v4l2_subdev_state_free(sd_state);
-
- return ret;
-}
-
static int rvin_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -333,42 +237,6 @@ static int rvin_querycap(struct file *file, void *priv,
return 0;
}
-static int rvin_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct rvin_dev *vin = video_drvdata(file);
-
- return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL);
-}
-
-static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_rect fmt_rect, src_rect;
- int ret;
-
- if (vb2_is_busy(&vin->queue))
- return -EBUSY;
-
- ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
- &src_rect);
- if (ret)
- return ret;
-
- vin->format = f->fmt.pix;
-
- fmt_rect.top = 0;
- fmt_rect.left = 0;
- fmt_rect.width = vin->format.width;
- fmt_rect.height = vin->format.height;
-
- v4l2_rect_map_inside(&vin->crop, &src_rect);
- v4l2_rect_map_inside(&vin->compose, &fmt_rect);
-
- return 0;
-}
-
static int rvin_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -465,6 +333,7 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
{
+ struct media_pad *pad = media_pad_remote_pad_first(&vin->pad);
struct v4l2_subdev_format fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
@@ -472,18 +341,11 @@ static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
unsigned int index;
int ret;
- if (vin->info->use_mc) {
- struct media_pad *pad = media_pad_remote_pad_first(&vin->pad);
-
- if (!pad)
- return -EINVAL;
+ if (!pad)
+ return -EINVAL;
- sd = media_entity_to_v4l2_subdev(pad->entity);
- index = pad->index;
- } else {
- sd = vin_to_source(vin);
- index = vin->parallel.source_pad;
- }
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ index = pad->index;
fmt.pad = index;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
@@ -623,284 +485,18 @@ static int rvin_s_selection(struct file *file, void *fh,
return 0;
}
-static int rvin_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_g_parm_cap(&vin->vdev, sd, parm);
-}
-
-static int rvin_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_s_parm_cap(&vin->vdev, sd, parm);
-}
-
-static int rvin_g_pixelaspect(struct file *file, void *priv,
- int type, struct v4l2_fract *f)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, video, g_pixelaspect, f);
-}
-
-static int rvin_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (i->index != 0)
- return -EINVAL;
-
- ret = v4l2_subdev_call(sd, video, g_input_status, &i->status);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
- if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) {
- i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
- i->std = 0;
- } else {
- i->capabilities = V4L2_IN_CAP_STD;
- i->std = vin->vdev.tvnorms;
- }
-
- strscpy(i->name, "Camera", sizeof(i->name));
-
- return 0;
-}
-
-static int rvin_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int rvin_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
- return 0;
-}
-
-static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_subdev_call(sd, video, querystd, a);
-}
-
-static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
-{
- struct rvin_dev *vin = video_drvdata(file);
- int ret;
-
- ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a);
- if (ret < 0)
- return ret;
-
- vin->std = a;
-
- /* Changing the standard will change the width/height */
- return rvin_reset_format(vin);
-}
-
-static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct rvin_dev *vin = video_drvdata(file);
-
- if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap))
- return -ENOIOCTLCMD;
-
- *a = vin->std;
-
- return 0;
-}
-
static int rvin_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
case V4L2_EVENT_SOURCE_CHANGE:
return v4l2_event_subscribe(fh, sub, 4, NULL);
}
return v4l2_ctrl_subscribe_event(fh, sub);
}
-static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_enum_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (timings->pad)
- return -EINVAL;
-
- timings->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
-
- timings->pad = 0;
-
- return ret;
-}
-
-static int rvin_s_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- ret = v4l2_subdev_call(sd, pad, s_dv_timings,
- vin->parallel.sink_pad, timings);
- if (ret)
- return ret;
-
- /* Changing the timings will change the width/height */
- return rvin_reset_format(vin);
-}
-
-static int rvin_g_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_subdev_call(sd, pad, g_dv_timings,
- vin->parallel.sink_pad, timings);
-}
-
-static int rvin_query_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_subdev_call(sd, pad, query_dv_timings,
- vin->parallel.sink_pad, timings);
-}
-
-static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
- struct v4l2_dv_timings_cap *cap)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (cap->pad)
- return -EINVAL;
-
- cap->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
-
- cap->pad = 0;
-
- return ret;
-}
-
-static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (edid->pad)
- return -EINVAL;
-
- edid->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, get_edid, edid);
-
- edid->pad = 0;
-
- return ret;
-}
-
-static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (edid->pad)
- return -EINVAL;
-
- edid->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, set_edid, edid);
-
- edid->pad = 0;
-
- return ret;
-}
-
-static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
- .vidioc_querycap = rvin_querycap,
- .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap,
-
- .vidioc_g_selection = rvin_g_selection,
- .vidioc_s_selection = rvin_s_selection,
-
- .vidioc_g_parm = rvin_g_parm,
- .vidioc_s_parm = rvin_s_parm,
-
- .vidioc_g_pixelaspect = rvin_g_pixelaspect,
-
- .vidioc_enum_input = rvin_enum_input,
- .vidioc_g_input = rvin_g_input,
- .vidioc_s_input = rvin_s_input,
-
- .vidioc_dv_timings_cap = rvin_dv_timings_cap,
- .vidioc_enum_dv_timings = rvin_enum_dv_timings,
- .vidioc_g_dv_timings = rvin_g_dv_timings,
- .vidioc_s_dv_timings = rvin_s_dv_timings,
- .vidioc_query_dv_timings = rvin_query_dv_timings,
-
- .vidioc_g_edid = rvin_g_edid,
- .vidioc_s_edid = rvin_s_edid,
-
- .vidioc_querystd = rvin_querystd,
- .vidioc_g_std = rvin_g_std,
- .vidioc_s_std = rvin_s_std,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
-
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = rvin_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 Media Controller
- */
-
static void rvin_mc_try_format(struct rvin_dev *vin,
struct v4l2_pix_format *pix)
{
@@ -979,19 +575,6 @@ static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = {
* File Operations
*/
-static int rvin_power_parallel(struct rvin_dev *vin, bool on)
-{
- struct v4l2_subdev *sd = vin_to_source(vin);
- int power = on ? 1 : 0;
- int ret;
-
- ret = v4l2_subdev_call(sd, core, s_power, power);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- return 0;
-}
-
static int rvin_open(struct file *file)
{
struct rvin_dev *vin = video_drvdata(file);
@@ -1011,11 +594,7 @@ static int rvin_open(struct file *file)
if (ret)
goto err_unlock;
- if (vin->info->use_mc)
- ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
- else if (v4l2_fh_is_singular_file(file))
- ret = rvin_power_parallel(vin, true);
-
+ ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
if (ret < 0)
goto err_open;
@@ -1027,10 +606,7 @@ static int rvin_open(struct file *file)
return 0;
err_power:
- if (vin->info->use_mc)
- v4l2_pipeline_pm_put(&vin->vdev.entity);
- else if (v4l2_fh_is_singular_file(file))
- rvin_power_parallel(vin, false);
+ v4l2_pipeline_pm_put(&vin->vdev.entity);
err_open:
v4l2_fh_release(file);
err_unlock:
@@ -1044,23 +620,14 @@ err_pm:
static int rvin_release(struct file *file)
{
struct rvin_dev *vin = video_drvdata(file);
- bool fh_singular;
int ret;
mutex_lock(&vin->lock);
- /* Save the singular status before we call the clean-up helper */
- fh_singular = v4l2_fh_is_singular_file(file);
-
/* the release helper will cleanup any on-going streaming */
ret = _vb2_fop_release(file, NULL);
- if (vin->info->use_mc) {
- v4l2_pipeline_pm_put(&vin->vdev.entity);
- } else {
- if (fh_singular)
- rvin_power_parallel(vin, false);
- }
+ v4l2_pipeline_pm_put(&vin->vdev.entity);
mutex_unlock(&vin->lock);
@@ -1091,18 +658,6 @@ void rvin_v4l2_unregister(struct rvin_dev *vin)
video_unregister_device(&vin->vdev);
}
-static void rvin_notify_video_device(struct rvin_dev *vin,
- unsigned int notification, void *arg)
-{
- switch (notification) {
- case V4L2_DEVICE_NOTIFY_EVENT:
- v4l2_event_queue(&vin->vdev, arg);
- break;
- default:
- break;
- }
-}
-
static void rvin_notify(struct v4l2_subdev *sd,
unsigned int notification, void *arg)
{
@@ -1113,12 +668,6 @@ static void rvin_notify(struct v4l2_subdev *sd,
container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
unsigned int i;
- /* If no media controller, no need to route the event. */
- if (!vin->info->use_mc) {
- rvin_notify_video_device(vin, notification, arg);
- return;
- }
-
group = vin->group;
for (i = 0; i < RCAR_VIN_NUM; i++) {
@@ -1134,7 +683,13 @@ static void rvin_notify(struct v4l2_subdev *sd,
if (remote != sd)
continue;
- rvin_notify_video_device(vin, notification, arg);
+ switch (notification) {
+ case V4L2_DEVICE_NOTIFY_EVENT:
+ v4l2_event_queue(&vin->vdev, arg);
+ break;
+ default:
+ break;
+ }
}
}
@@ -1153,7 +708,8 @@ int rvin_v4l2_register(struct rvin_dev *vin)
vdev->lock = &vin->lock;
vdev->fops = &rvin_fops;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
+ V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
+ vdev->ioctl_ops = &rvin_mc_ioctl_ops;
/* Set a default format */
vin->format.pixelformat = RVIN_DEFAULT_FORMAT;
@@ -1162,14 +718,6 @@ int rvin_v4l2_register(struct rvin_dev *vin)
vin->format.field = RVIN_DEFAULT_FIELD;
vin->format.colorspace = RVIN_DEFAULT_COLORSPACE;
- if (vin->info->use_mc) {
- vdev->device_caps |= V4L2_CAP_IO_MC;
- vdev->ioctl_ops = &rvin_mc_ioctl_ops;
- } else {
- vdev->ioctl_ops = &rvin_ioctl_ops;
- rvin_reset_format(vin);
- }
-
rvin_format_align(vin, &vin->format);
ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
index 83d1b2734c41..74bef5b8adad 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
@@ -2,12 +2,11 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
#ifndef __RCAR_VIN__
@@ -79,8 +78,6 @@ struct rvin_video_format {
* @mbus_type: media bus type
* @bus: media bus parallel configuration
* @source_pad: source pad of remote subdevice
- * @sink_pad: sink pad of remote subdevice
- *
*/
struct rvin_parallel_entity {
struct v4l2_async_connection *asc;
@@ -90,7 +87,6 @@ struct rvin_parallel_entity {
struct v4l2_mbus_config_parallel bus;
unsigned int source_pad;
- unsigned int sink_pad;
};
/**
@@ -117,7 +113,6 @@ struct rvin_group_route {
/**
* struct rvin_info - Information about the particular VIN implementation
* @model: VIN model
- * @use_mc: use media controller instead of controlling subdevice
* @use_isp: the VIN is connected to the ISP and not to the CSI-2
* @nv12: support outputting NV12 pixel format
* @raw10: support outputting RAW10 pixel format
@@ -129,7 +124,6 @@ struct rvin_group_route {
*/
struct rvin_info {
enum model_id model;
- bool use_mc;
bool use_isp;
bool nv12;
bool raw10;
@@ -149,7 +143,6 @@ struct rvin_info {
* @vdev: V4L2 video device associated with VIN
* @v4l2_dev: V4L2 device
* @ctrl_handler: V4L2 control handler
- * @notifier: V4L2 asynchronous subdevs notifier
*
* @parallel: parallel input subdevice descriptor
*
@@ -177,7 +170,6 @@ struct rvin_info {
* @crop: active cropping
* @compose: active composing
* @scaler: Optional scaler
- * @std: active video standard of the video source
*
* @alpha: Alpha component to fill in for supported pixel formats
*/
@@ -189,7 +181,6 @@ struct rvin_dev {
struct video_device vdev;
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_async_notifier notifier;
struct rvin_parallel_entity parallel;
@@ -220,7 +211,6 @@ struct rvin_dev {
struct v4l2_rect crop;
struct v4l2_rect compose;
void (*scaler)(struct rvin_dev *vin);
- v4l2_std_id std;
unsigned int alpha;
};
@@ -242,6 +232,7 @@ struct rvin_dev {
* @lock: protects the count, notifier, vin and csi members
* @count: number of enabled VIN instances found in DT
* @notifier: group notifier for CSI-2 async connections
+ * @info: Platform dependent information about the VIN instances
* @vin: VIN instances which are part of the group
* @link_setup: Callback to create all links for the media graph
* @remotes: array of pairs of async connection and subdev pointers
@@ -255,9 +246,10 @@ struct rvin_group {
struct mutex lock;
unsigned int count;
struct v4l2_async_notifier notifier;
+ const struct rvin_info *info;
struct rvin_dev *vin[RCAR_VIN_NUM];
- int (*link_setup)(struct rvin_dev *vin);
+ int (*link_setup)(struct rvin_group *group);
struct {
struct v4l2_async_connection *asc;
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
index 5fa73ab2db53..806acc8f9728 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
@@ -366,7 +366,7 @@ static const struct rzg2l_cru_info rzg3e_cru_info = {
.irq_handler = rzg3e_cru_irq,
.enable_interrupts = rzg3e_cru_enable_interrupts,
.disable_interrupts = rzg3e_cru_disable_interrupts,
- .fifo_empty = rz3e_fifo_empty,
+ .fifo_empty = rzg3e_fifo_empty,
.csi_setup = rzg3e_cru_csi2_setup,
};
@@ -403,7 +403,7 @@ static const u16 rzg2l_cru_regs[] = {
[ICnDMR] = 0x26c,
};
-static const struct rzg2l_cru_info rzgl2_cru_info = {
+static const struct rzg2l_cru_info rzg2l_cru_info = {
.max_width = 2800,
.max_height = 4095,
.image_conv = ICnMC,
@@ -422,7 +422,7 @@ static const struct of_device_id rzg2l_cru_of_id_table[] = {
},
{
.compatible = "renesas,rzg2l-cru",
- .data = &rzgl2_cru_info,
+ .data = &rzg2l_cru_info,
},
{ /* sentinel */ }
};
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index c30f3b281284..be95b41c37df 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -64,19 +64,21 @@ struct rzg2l_cru_ip {
/**
* struct rzg2l_cru_ip_format - CRU IP format
- * @code: Media bus code
+ * @codes: Array of up to four media bus codes
* @datatype: MIPI CSI2 data type
* @format: 4CC format identifier (V4L2_PIX_FMT_*)
* @icndmr: ICnDMR register value
- * @bpp: bytes per pixel
* @yuv: Flag to indicate whether the format is YUV-based.
*/
struct rzg2l_cru_ip_format {
- u32 code;
+ /*
+ * RAW output formats might be produced by RAW media codes with any one
+ * of the 4 common bayer patterns.
+ */
+ u32 codes[4];
u32 datatype;
u32 format;
u32 icndmr;
- u8 bpp;
bool yuv;
};
@@ -192,6 +194,8 @@ struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru);
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code);
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format);
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index);
+bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt,
+ unsigned int code);
void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
@@ -199,7 +203,7 @@ void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru);
-bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru);
+bool rzg3e_fifo_empty(struct rzg2l_cru_dev *cru);
void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
const struct rzg2l_cru_ip_format *ip_fmt,
u8 csi_vc);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index 9243306e2aa9..1520211e7418 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -232,6 +232,18 @@ static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = {
{ .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, },
{ .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, },
{ .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, },
+ { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14, },
+ { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14, },
+ { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14, },
+ { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14, },
};
static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd)
@@ -282,15 +294,18 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2)
const struct rzg2l_csi2_format *format;
const struct v4l2_mbus_framefmt *fmt;
struct v4l2_subdev_state *state;
- struct v4l2_ctrl *ctrl;
+ struct media_pad *remote_pad;
u64 mbps;
+ s64 ret;
- /* Read the pixel rate control from remote. */
- ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE);
- if (!ctrl) {
- dev_err(csi2->dev, "no pixel rate control in subdev %s\n",
- source->name);
- return -EINVAL;
+ if (!csi2->remote_source)
+ return -ENODEV;
+
+ remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]);
+ if (IS_ERR(remote_pad)) {
+ dev_err(csi2->dev, "can't get source pad of %s (%ld)\n",
+ csi2->remote_source->name, PTR_ERR(remote_pad));
+ return PTR_ERR(remote_pad);
}
state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev);
@@ -298,12 +313,16 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2)
format = rzg2l_csi2_code_to_fmt(fmt->code);
v4l2_subdev_unlock_state(state);
- /*
- * Calculate hsfreq in Mbps
- * hsfreq = (pixel_rate * bits_per_sample) / number_of_lanes
- */
- mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * format->bpp;
- do_div(mbps, csi2->lanes * 1000000);
+ /* Read the link frequency from remote subdevice. */
+ ret = v4l2_get_link_freq(remote_pad, format->bpp, csi2->lanes * 2);
+ if (ret < 0) {
+ dev_err(csi2->dev, "can't retrieve link freq from subdev %s\n",
+ source->name);
+ return -EINVAL;
+ }
+
+ mbps = ret * 2;
+ do_div(mbps, 1000000);
return mbps;
}
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
index 7836c7cd53dc..5f2c87858bfe 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
@@ -13,42 +13,83 @@
static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {
{
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .codes = {
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ },
.datatype = MIPI_CSI2_DT_YUV422_8B,
.format = V4L2_PIX_FMT_UYVY,
- .bpp = 2,
.icndmr = ICnDMR_YCMODE_UYVY,
.yuv = true,
},
{
- .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ },
.format = V4L2_PIX_FMT_SBGGR8,
.datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 1,
.icndmr = 0,
.yuv = false,
},
{
- .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .codes = {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ },
.format = V4L2_PIX_FMT_SGBRG8,
.datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 1,
.icndmr = 0,
.yuv = false,
},
{
- .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .codes = {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ },
.format = V4L2_PIX_FMT_SGRBG8,
.datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 1,
.icndmr = 0,
.yuv = false,
},
{
- .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .codes = {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ },
.format = V4L2_PIX_FMT_SRGGB8,
.datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 1,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10
+ },
+ .format = V4L2_PIX_FMT_RAW_CRU10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12
+ },
+ .format = V4L2_PIX_FMT_RAW_CRU12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MEDIA_BUS_FMT_SRGGB14_1X14
+ },
+ .format = V4L2_PIX_FMT_RAW_CRU14,
+ .datatype = MIPI_CSI2_DT_RAW14,
.icndmr = 0,
.yuv = false,
},
@@ -56,11 +97,14 @@ static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code)
{
- unsigned int i;
+ unsigned int i, j;
- for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++)
- if (rzg2l_cru_ip_formats[i].code == code)
- return &rzg2l_cru_ip_formats[i];
+ for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
+ for (j = 0; j < ARRAY_SIZE(rzg2l_cru_ip_formats[i].codes); j++) {
+ if (rzg2l_cru_ip_formats[i].codes[j] == code)
+ return &rzg2l_cru_ip_formats[i];
+ }
+ }
return NULL;
}
@@ -85,6 +129,17 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index)
return &rzg2l_cru_ip_formats[index];
}
+bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt,
+ unsigned int code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fmt->codes); i++)
+ if (fmt->codes[i] == code)
+ return true;
+
+ return false;
+}
struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru)
{
struct v4l2_subdev_state *state;
@@ -162,7 +217,7 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
sink_format = v4l2_subdev_state_get_format(state, fmt->pad);
if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code))
- sink_format->code = rzg2l_cru_ip_formats[0].code;
+ sink_format->code = rzg2l_cru_ip_formats[0].codes[0];
else
sink_format->code = fmt->format.code;
@@ -188,11 +243,26 @@ static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->index >= ARRAY_SIZE(rzg2l_cru_ip_formats))
- return -EINVAL;
+ unsigned int index = code->index;
+ unsigned int i, j;
- code->code = rzg2l_cru_ip_formats[code->index].code;
- return 0;
+ for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
+ const struct rzg2l_cru_ip_format *fmt = &rzg2l_cru_ip_formats[i];
+
+ for (j = 0; j < ARRAY_SIZE(fmt->codes); j++) {
+ if (!fmt->codes[j])
+ continue;
+
+ if (!index) {
+ code->code = fmt->codes[j];
+ return 0;
+ }
+
+ index--;
+ }
+ }
+
+ return -EINVAL;
}
static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 067c6af14e95..a8817a7066b2 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -323,7 +323,7 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru,
return 0;
}
-bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru)
+bool rzg3e_fifo_empty(struct rzg2l_cru_dev *cru)
{
u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
@@ -345,8 +345,6 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru)
amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR;
amnfifopntr_r_y =
(amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16;
- if (amnfifopntr_w == amnfifopntr_r_y)
- return true;
return amnfifopntr_w == amnfifopntr_r_y;
}
@@ -941,15 +939,7 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
v4l_bound_align_image(&pix->width, 320, info->max_width, 1,
&pix->height, 240, info->max_height, 2, 0);
- if (info->has_stride) {
- u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp,
- RZG2L_CRU_STRIDE_MAX);
- pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN);
- } else {
- pix->bytesperline = pix->width * fmt->bpp;
- }
-
- pix->sizeimage = pix->bytesperline * pix->height;
+ v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height);
dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
pix->width, pix->height, pix->bytesperline, pix->sizeimage);
@@ -1031,6 +1021,31 @@ static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static int rzg2l_cru_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct rzg2l_cru_dev *cru = video_drvdata(file);
+ const struct rzg2l_cru_info *info = cru->info;
+ const struct rzg2l_cru_ip_format *fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fmt = rzg2l_cru_ip_format_to_fmt(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = RZG2L_CRU_MIN_INPUT_WIDTH;
+ fsize->stepwise.max_width = info->max_width;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = RZG2L_CRU_MIN_INPUT_HEIGHT;
+ fsize->stepwise.max_height = info->max_height;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = {
.vidioc_querycap = rzg2l_cru_querycap,
.vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap,
@@ -1047,6 +1062,7 @@ static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = {
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_framesizes = rzg2l_cru_enum_framesizes,
};
/* -----------------------------------------------------------------------------
@@ -1129,7 +1145,7 @@ static int rzg2l_cru_video_link_validate(struct media_link *link)
if (fmt.format.width != cru->format.width ||
fmt.format.height != cru->format.height ||
fmt.format.field != cru->format.field ||
- video_fmt->code != fmt.format.code)
+ !rzg2l_cru_ip_fmt_supports_mbus_code(video_fmt, fmt.format.code))
return -EPIPE;
return 0;
diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile
index de8c802e1d1a..2057c8f7be47 100644
--- a/drivers/media/platform/renesas/vsp1/Makefile
+++ b/drivers/media/platform/renesas/vsp1/Makefile
@@ -6,5 +6,6 @@ vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o
vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o
vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
vsp1-y += vsp1_iif.o vsp1_lif.o vsp1_uif.o
+vsp1-y += vsp1_vspx.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o
diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h
index f97a1a31bfab..94de2e85792e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1.h
@@ -111,6 +111,7 @@ struct vsp1_device {
struct media_entity_operations media_ops;
struct vsp1_drm *drm;
+ struct vsp1_vspx *vspx;
};
int vsp1_device_get(struct vsp1_device *vsp1);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index bb8228b19824..d732b4ed1180 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
+#include <linux/lockdep.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -176,6 +177,7 @@ struct vsp1_dl_cmd_pool {
* @bodies: list of extra display list bodies
* @pre_cmd: pre command to be issued through extended dl header
* @post_cmd: post command to be issued through extended dl header
+ * @allocated: flag to detect double list release
* @has_chain: if true, indicates that there's a partition chain
* @chain: entry in the display list partition chain
* @flags: display list flags, a combination of VSP1_DL_FRAME_END_*
@@ -194,6 +196,8 @@ struct vsp1_dl_list {
struct vsp1_dl_ext_cmd *pre_cmd;
struct vsp1_dl_ext_cmd *post_cmd;
+ bool allocated;
+
bool has_chain;
struct list_head chain;
@@ -212,6 +216,7 @@ struct vsp1_dl_list {
* @pending: list waiting to be queued to the hardware
* @pool: body pool for the display list bodies
* @cmdpool: commands pool for extended display list
+ * @list_count: number of allocated display lists
*/
struct vsp1_dl_manager {
unsigned int index;
@@ -226,6 +231,8 @@ struct vsp1_dl_manager {
struct vsp1_dl_body_pool *pool;
struct vsp1_dl_cmd_pool *cmdpool;
+
+ size_t list_count;
};
/* -----------------------------------------------------------------------------
@@ -606,6 +613,8 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
struct vsp1_dl_list *dl = NULL;
unsigned long flags;
+ lockdep_assert_not_held(&dlm->lock);
+
spin_lock_irqsave(&dlm->lock, flags);
if (!list_empty(&dlm->free)) {
@@ -617,6 +626,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
* display list can assert list_empty() if it is not in a chain.
*/
INIT_LIST_HEAD(&dl->chain);
+ dl->allocated = true;
}
spin_unlock_irqrestore(&dlm->lock, flags);
@@ -632,6 +642,8 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
if (!dl)
return;
+ lockdep_assert_held(&dl->dlm->lock);
+
/*
* Release any linked display-lists which were chained for a single
* hardware operation.
@@ -657,6 +669,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
*/
dl->body0->num_entries = 0;
+ /*
+ * Return the display list to the 'free' pool. If the list had already
+ * been returned be loud about it.
+ */
+ WARN_ON_ONCE(!dl->allocated);
+ dl->allocated = false;
+
list_add_tail(&dl->list, &dl->dlm->free);
}
@@ -1067,6 +1086,7 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1)
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
{
unsigned long flags;
+ size_t list_count;
spin_lock_irqsave(&dlm->lock, flags);
@@ -1074,8 +1094,11 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
__vsp1_dl_list_put(dlm->queued);
__vsp1_dl_list_put(dlm->pending);
+ list_count = list_count_nodes(&dlm->free);
spin_unlock_irqrestore(&dlm->lock, flags);
+ WARN_ON_ONCE(list_count != dlm->list_count);
+
dlm->active = NULL;
dlm->queued = NULL;
dlm->pending = NULL;
@@ -1145,6 +1168,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
list_add_tail(&dl->list, &dlm->free);
}
+ dlm->list_count = prealloc;
+
if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) {
dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1,
VSP1_EXTCMD_AUTOFLD, prealloc);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index fe55e8747b05..15d266439564 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <media/media-entity.h>
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 8270a9d207cb..b8d06e88c475 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -33,11 +33,13 @@
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_pipe.h"
+#include "vsp1_regs.h"
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
#include "vsp1_uif.h"
#include "vsp1_video.h"
+#include "vsp1_vspx.h"
/* -----------------------------------------------------------------------------
* Interrupt Handling
@@ -490,7 +492,10 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
ret = media_device_register(mdev);
} else {
- ret = vsp1_drm_init(vsp1);
+ if (vsp1->info->version == VI6_IP_VERSION_MODEL_VSPX_GEN4)
+ ret = vsp1_vspx_init(vsp1);
+ else
+ ret = vsp1_drm_init(vsp1);
}
done:
@@ -502,7 +507,9 @@ done:
int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
{
+ u32 version = vsp1->version & VI6_IP_VERSION_MODEL_MASK;
unsigned int timeout;
+ int ret = 0;
u32 status;
status = vsp1_read(vsp1, VI6_STATUS);
@@ -523,7 +530,11 @@ int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
return -ETIMEDOUT;
}
- return 0;
+ if (version == VI6_IP_VERSION_MODEL_VSPD_GEN3 ||
+ version == VI6_IP_VERSION_MODEL_VSPD_GEN4)
+ ret = rcar_fcp_soft_reset(vsp1->fcp);
+
+ return ret;
}
static int vsp1_device_init(struct vsp1_device *vsp1)
@@ -851,6 +862,13 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uif_count = 2,
.wpf_count = 1,
.num_bru_inputs = 5,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPX_GEN4,
+ .model = "VSP2-X",
+ .gen = 4,
+ .features = VSP1_HAS_IIF,
+ .rpf_count = 2,
+ .wpf_count = 1,
},
};
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index 3cbb768cf6ad..5d769cc42fe1 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
@@ -9,6 +9,7 @@
#include <linux/delay.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/sched.h>
#include <linux/wait.h>
@@ -473,6 +474,8 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ lockdep_assert_held(&pipe->irqlock);
+
if (pipe->state == VSP1_PIPELINE_STOPPED) {
vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index),
VI6_CMD_STRCMD);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
index 86e47c2d991f..10cfbcd1b6e0 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
@@ -799,6 +799,7 @@
#define VI6_IP_VERSION_MODEL_VSPDL_GEN3 (0x19 << 8)
#define VI6_IP_VERSION_MODEL_VSPBS_GEN3 (0x1a << 8)
#define VI6_IP_VERSION_MODEL_VSPD_GEN4 (0x1c << 8)
+#define VI6_IP_VERSION_MODEL_VSPX_GEN4 (0x1d << 8)
/* RZ/G2L SoCs have no version register, So use 0x80 as the model version */
#define VI6_IP_VERSION_MODEL_VSPD_RZG2L (0x80 << 8)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.c b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
new file mode 100644
index 000000000000..a754b92232bd
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vsp1_vspx.c -- R-Car Gen 4 VSPX
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+
+#include "vsp1_vspx.h"
+
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+#include <media/vsp1.h>
+
+#include "vsp1_dl.h"
+#include "vsp1_iif.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
+
+/*
+ * struct vsp1_vspx_pipeline - VSPX pipeline
+ * @pipe: the VSP1 pipeline
+ * @partition: the pre-calculated partition used by the pipeline
+ * @mutex: protects the streaming start/stop sequences
+ * @lock: protect access to the enabled flag
+ * @enabled: the enable flag
+ * @vspx_frame_end: frame end callback
+ * @frame_end_data: data for the frame end callback
+ */
+struct vsp1_vspx_pipeline {
+ struct vsp1_pipeline pipe;
+ struct vsp1_partition partition;
+
+ /*
+ * Protects the streaming start/stop sequences.
+ *
+ * The start/stop sequences cannot be locked with the 'lock' spinlock
+ * as they acquire mutexes when handling the pm runtime and the vsp1
+ * pipe start/stop operations. Provide a dedicated mutex for this
+ * reason.
+ */
+ struct mutex mutex;
+
+ /*
+ * Protects the enable flag.
+ *
+ * The enabled flag is contended between the start/stop streaming
+ * routines and the job_run one, which cannot take a mutex as it is
+ * called from the ISP irq context.
+ */
+ spinlock_t lock;
+ bool enabled;
+
+ void (*vspx_frame_end)(void *frame_end_data);
+ void *frame_end_data;
+};
+
+static inline struct vsp1_vspx_pipeline *
+to_vsp1_vspx_pipeline(struct vsp1_pipeline *pipe)
+{
+ return container_of(pipe, struct vsp1_vspx_pipeline, pipe);
+}
+
+/*
+ * struct vsp1_vspx - VSPX device
+ * @vsp1: the VSP1 device
+ * @pipe: the VSPX pipeline
+ */
+struct vsp1_vspx {
+ struct vsp1_device *vsp1;
+ struct vsp1_vspx_pipeline pipe;
+};
+
+/* Apply the given width, height and fourcc to the RWPF's subdevice */
+static int vsp1_vspx_rwpf_set_subdev_fmt(struct vsp1_device *vsp1,
+ struct vsp1_rwpf *rwpf,
+ u32 isp_fourcc,
+ unsigned int width,
+ unsigned int height)
+{
+ struct vsp1_entity *ent = &rwpf->entity;
+ struct v4l2_subdev_format format = {};
+ u32 vspx_fourcc;
+
+ switch (isp_fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ /* 8 bit RAW Bayer image. */
+ vspx_fourcc = V4L2_PIX_FMT_RGB332;
+ break;
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y12:
+ case V4L2_PIX_FMT_Y16:
+ /* 10, 12 and 16 bit RAW Bayer image. */
+ vspx_fourcc = V4L2_PIX_FMT_RGB565;
+ break;
+ case V4L2_META_FMT_GENERIC_8:
+ /* ConfigDMA parameters buffer. */
+ vspx_fourcc = V4L2_PIX_FMT_XBGR32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rwpf->fmtinfo = vsp1_get_format_info(vsp1, vspx_fourcc);
+
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = width;
+ format.format.height = height;
+ format.format.field = V4L2_FIELD_NONE;
+ format.format.code = rwpf->fmtinfo->mbus;
+
+ return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, &format);
+}
+
+/* Configure the RPF->IIF->WPF pipeline for ConfigDMA or RAW image transfer. */
+static int vsp1_vspx_pipeline_configure(struct vsp1_device *vsp1,
+ dma_addr_t addr, u32 isp_fourcc,
+ unsigned int width, unsigned int height,
+ unsigned int stride,
+ unsigned int iif_sink_pad,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ struct vsp1_rwpf *rpf0 = pipe->inputs[0];
+ int ret;
+
+ ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, rpf0, isp_fourcc, width,
+ height);
+ if (ret)
+ return ret;
+
+ ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, pipe->output, isp_fourcc,
+ width, height);
+ if (ret)
+ return ret;
+
+ vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], width, 0);
+ rpf0->format.plane_fmt[0].bytesperline = stride;
+ rpf0->format.num_planes = 1;
+ rpf0->mem.addr[0] = addr;
+
+ /*
+ * Connect RPF0 to the IIF sink pad corresponding to the config or image
+ * path.
+ */
+ rpf0->entity.sink_pad = iif_sink_pad;
+
+ vsp1_entity_route_setup(&rpf0->entity, pipe, dlb);
+ vsp1_entity_configure_stream(&rpf0->entity, rpf0->entity.state, pipe,
+ dl, dlb);
+ vsp1_entity_configure_partition(&rpf0->entity, pipe,
+ &pipe->part_table[0], dl, dlb);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void vsp1_vspx_pipeline_frame_end(struct vsp1_pipeline *pipe,
+ unsigned int completion)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe = to_vsp1_vspx_pipeline(pipe);
+
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ /*
+ * Operating the vsp1_pipe in singleshot mode requires to
+ * manually set the pipeline state to stopped when a transfer
+ * is completed.
+ */
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ }
+
+ if (vspx_pipe->vspx_frame_end)
+ vspx_pipe->vspx_frame_end(vspx_pipe->frame_end_data);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP Driver API (include/media/vsp1.h)
+ */
+
+/**
+ * vsp1_isp_init() - Initialize the VSPX
+ * @dev: The VSP1 struct device
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_init(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ if (!vsp1)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_init);
+
+/**
+ * vsp1_isp_get_bus_master - Get VSPX bus master
+ * @dev: The VSP1 struct device
+ *
+ * The VSPX accesses memory through an FCPX instance. When allocating memory
+ * buffers that will have to be accessed by the VSPX the 'struct device' of
+ * the FCPX should be used. Use this function to get a reference to it.
+ *
+ * Return: a pointer to the bus master's device
+ */
+struct device *vsp1_isp_get_bus_master(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ if (!vsp1)
+ return ERR_PTR(-ENODEV);
+
+ return vsp1->bus_master;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_get_bus_master);
+
+/**
+ * vsp1_isp_alloc_buffer - Allocate a buffer in the VSPX address space
+ * @dev: The VSP1 struct device
+ * @size: The size of the buffer to be allocated by the VSPX
+ * @buffer_desc: The buffer descriptor. Will be filled with the buffer
+ * CPU-mapped address, the bus address and the size of the
+ * allocated buffer
+ *
+ * Allocate a buffer that will be later accessed by the VSPX. Buffers allocated
+ * using vsp1_isp_alloc_buffer() shall be released with a call to
+ * vsp1_isp_free_buffer(). This function is used by the ISP driver to allocate
+ * memory for the ConfigDMA parameters buffer.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_alloc_buffer(struct device *dev, size_t size,
+ struct vsp1_isp_buffer_desc *buffer_desc)
+{
+ struct device *bus_master = vsp1_isp_get_bus_master(dev);
+
+ if (IS_ERR_OR_NULL(bus_master))
+ return -ENODEV;
+
+ buffer_desc->cpu_addr = dma_alloc_coherent(bus_master, size,
+ &buffer_desc->dma_addr,
+ GFP_KERNEL);
+ if (!buffer_desc->cpu_addr)
+ return -ENOMEM;
+
+ buffer_desc->size = size;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_alloc_buffer);
+
+/**
+ * vsp1_isp_free_buffer - Release a buffer allocated by vsp1_isp_alloc_buffer()
+ * @dev: The VSP1 struct device
+ * @buffer_desc: The descriptor of the buffer to release as returned by
+ * vsp1_isp_alloc_buffer()
+ *
+ * Release memory in the VSPX address space allocated by
+ * vsp1_isp_alloc_buffer().
+ */
+void vsp1_isp_free_buffer(struct device *dev,
+ struct vsp1_isp_buffer_desc *buffer_desc)
+{
+ struct device *bus_master = vsp1_isp_get_bus_master(dev);
+
+ if (IS_ERR_OR_NULL(bus_master))
+ return;
+
+ dma_free_coherent(bus_master, buffer_desc->size, buffer_desc->cpu_addr,
+ buffer_desc->dma_addr);
+}
+
+/**
+ * vsp1_isp_start_streaming - Start processing VSPX jobs
+ * @dev: The VSP1 struct device
+ * @frame_end: The frame end callback description
+ *
+ * Start the VSPX and prepare for accepting buffer transfer job requests.
+ * The caller is responsible for tracking the started state of the VSPX.
+ * Attempting to start an already started VSPX instance is an error.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_start_streaming(struct device *dev,
+ struct vsp1_vspx_frame_end *frame_end)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ u32 value;
+ int ret;
+
+ if (!frame_end)
+ return -EINVAL;
+
+ guard(mutex)(&vspx_pipe->mutex);
+
+ scoped_guard(spinlock_irq, &vspx_pipe->lock) {
+ if (vspx_pipe->enabled)
+ return -EBUSY;
+ }
+
+ vspx_pipe->vspx_frame_end = frame_end->vspx_frame_end;
+ vspx_pipe->frame_end_data = frame_end->frame_end_data;
+
+ /* Enable the VSP1 and prepare for streaming. */
+ vsp1_pipeline_dump(pipe, "VSPX job");
+
+ ret = vsp1_device_get(vsp1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Make sure VSPX is not active. This should never happen in normal
+ * usage
+ */
+ value = vsp1_read(vsp1, VI6_CMD(0));
+ if (value & VI6_CMD_STRCMD) {
+ dev_err(vsp1->dev,
+ "%s: Starting of WPF0 already reserved\n", __func__);
+ ret = -EBUSY;
+ goto error_put;
+ }
+
+ value = vsp1_read(vsp1, VI6_STATUS);
+ if (value & VI6_STATUS_SYS_ACT(0)) {
+ dev_err(vsp1->dev,
+ "%s: WPF0 has not entered idle state\n", __func__);
+ ret = -EBUSY;
+ goto error_put;
+ }
+
+ scoped_guard(spinlock_irq, &vspx_pipe->lock) {
+ vspx_pipe->enabled = true;
+ }
+
+ return 0;
+
+error_put:
+ vsp1_device_put(vsp1);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_start_streaming);
+
+/**
+ * vsp1_isp_stop_streaming - Stop the VSPX
+ * @dev: The VSP1 struct device
+ *
+ * Stop the VSPX operation by stopping the vsp1 pipeline and waiting for the
+ * last frame in transfer, if any, to complete.
+ *
+ * The caller is responsible for tracking the stopped state of the VSPX.
+ * Attempting to stop an already stopped VSPX instance is a nop.
+ */
+void vsp1_isp_stop_streaming(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+
+ guard(mutex)(&vspx_pipe->mutex);
+
+ scoped_guard(spinlock_irq, &vspx_pipe->lock) {
+ if (!vspx_pipe->enabled)
+ return;
+
+ vspx_pipe->enabled = false;
+ }
+
+ WARN_ON_ONCE(vsp1_pipeline_stop(pipe));
+
+ vspx_pipe->vspx_frame_end = NULL;
+ vsp1_dlm_reset(pipe->output->dlm);
+ vsp1_device_put(vsp1);
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_stop_streaming);
+
+/**
+ * vsp1_isp_job_prepare - Prepare a new buffer transfer job
+ * @dev: The VSP1 struct device
+ * @job: The job description
+ *
+ * Prepare a new buffer transfer job by populating a display list that will be
+ * later executed by a call to vsp1_isp_job_run(). All pending jobs must be
+ * released after stopping the streaming operations with a call to
+ * vsp1_isp_job_release().
+ *
+ * In order for the VSPX to accept new jobs to prepare the VSPX must have been
+ * started.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_job_prepare(struct device *dev, struct vsp1_isp_job_desc *job)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ const struct v4l2_pix_format_mplane *pix_mp;
+ struct vsp1_dl_list *second_dl = NULL;
+ struct vsp1_dl_body *dlb;
+ struct vsp1_dl_list *dl;
+ int ret;
+
+ /*
+ * Transfer the buffers described in the job: an optional ConfigDMA
+ * parameters buffer and a RAW image.
+ */
+
+ job->dl = vsp1_dl_list_get(pipe->output->dlm);
+ if (!job->dl)
+ return -ENOMEM;
+
+ dl = job->dl;
+ dlb = vsp1_dl_list_get_body0(dl);
+
+ /* Configure IIF routing and enable IIF function. */
+ vsp1_entity_route_setup(pipe->iif, pipe, dlb);
+ vsp1_entity_configure_stream(pipe->iif, pipe->iif->state, pipe,
+ dl, dlb);
+
+ /* Configure WPF0 to enable RPF0 as source. */
+ vsp1_entity_route_setup(&pipe->output->entity, pipe, dlb);
+ vsp1_entity_configure_stream(&pipe->output->entity,
+ pipe->output->entity.state, pipe,
+ dl, dlb);
+
+ if (job->config.pairs) {
+ /*
+ * Writing less than 17 pairs corrupts the output images ( < 16
+ * pairs) or freezes the VSPX operations (= 16 pairs). Only
+ * allow more than 16 pairs to be written.
+ */
+ if (job->config.pairs <= 16) {
+ ret = -EINVAL;
+ goto error_put_dl;
+ }
+
+ /*
+ * Configure RPF0 for ConfigDMA data. Transfer the number of
+ * configuration pairs plus 2 words for the header.
+ */
+ ret = vsp1_vspx_pipeline_configure(vsp1, job->config.mem,
+ V4L2_META_FMT_GENERIC_8,
+ job->config.pairs * 2 + 2, 1,
+ job->config.pairs * 2 + 2,
+ VSPX_IIF_SINK_PAD_CONFIG,
+ dl, dlb);
+ if (ret)
+ goto error_put_dl;
+
+ second_dl = vsp1_dl_list_get(pipe->output->dlm);
+ if (!second_dl) {
+ ret = -ENOMEM;
+ goto error_put_dl;
+ }
+
+ dl = second_dl;
+ dlb = vsp1_dl_list_get_body0(dl);
+ }
+
+ /* Configure RPF0 for RAW image transfer. */
+ pix_mp = &job->img.fmt;
+ ret = vsp1_vspx_pipeline_configure(vsp1, job->img.mem,
+ pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height,
+ pix_mp->plane_fmt[0].bytesperline,
+ VSPX_IIF_SINK_PAD_IMG, dl, dlb);
+ if (ret)
+ goto error_put_dl;
+
+ if (second_dl)
+ vsp1_dl_list_add_chain(job->dl, second_dl);
+
+ return 0;
+
+error_put_dl:
+ if (second_dl)
+ vsp1_dl_list_put(second_dl);
+ vsp1_dl_list_put(job->dl);
+ job->dl = NULL;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_job_prepare);
+
+/**
+ * vsp1_isp_job_run - Run a buffer transfer job
+ * @dev: The VSP1 struct device
+ * @job: The job to be run
+ *
+ * Run the display list contained in the job description provided by the caller.
+ * The job must have been prepared with a call to vsp1_isp_job_prepare() and
+ * the job's display list shall be valid.
+ *
+ * Jobs can be run only on VSPX instances which have been started. Requests
+ * to run a job after the VSPX has been stopped return -EINVAL and the job
+ * resources shall be released by the caller with vsp1_isp_job_release().
+ * When a job is run successfully all the resources acquired by
+ * vsp1_isp_job_prepare() are released by this function and no further action
+ * is required to the caller.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_job_run(struct device *dev, struct vsp1_isp_job_desc *job)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ u32 value;
+
+ /* Make sure VSPX is not busy processing a frame. */
+ value = vsp1_read(vsp1, VI6_CMD(0));
+ if (value) {
+ dev_err(vsp1->dev,
+ "%s: Starting of WPF0 already reserved\n", __func__);
+ return -EBUSY;
+ }
+
+ scoped_guard(spinlock_irqsave, &vspx_pipe->lock) {
+ /*
+ * If a new job is scheduled when the VSPX is stopped, do not
+ * run it.
+ */
+ if (!vspx_pipe->enabled)
+ return -EINVAL;
+
+ vsp1_dl_list_commit(job->dl, 0);
+
+ /*
+ * The display list is now under control of the display list
+ * manager and will be released automatically when the job
+ * completes.
+ */
+ job->dl = NULL;
+ }
+
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ vsp1_pipeline_run(pipe);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_job_run);
+
+/**
+ * vsp1_isp_job_release - Release a non processed transfer job
+ * @dev: The VSP1 struct device
+ * @job: The job to release
+ *
+ * Release a job prepared by a call to vsp1_isp_job_prepare() and not yet
+ * run. All pending jobs shall be released after streaming has been stopped.
+ */
+void vsp1_isp_job_release(struct device *dev,
+ struct vsp1_isp_job_desc *job)
+{
+ vsp1_dl_list_put(job->dl);
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_job_release);
+
+/* -----------------------------------------------------------------------------
+ * Initialization and cleanup
+ */
+
+int vsp1_vspx_init(struct vsp1_device *vsp1)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe;
+ struct vsp1_pipeline *pipe;
+
+ vsp1->vspx = devm_kzalloc(vsp1->dev, sizeof(*vsp1->vspx), GFP_KERNEL);
+ if (!vsp1->vspx)
+ return -ENOMEM;
+
+ vsp1->vspx->vsp1 = vsp1;
+
+ vspx_pipe = &vsp1->vspx->pipe;
+ vspx_pipe->enabled = false;
+
+ pipe = &vspx_pipe->pipe;
+
+ vsp1_pipeline_init(pipe);
+
+ pipe->partitions = 1;
+ pipe->part_table = &vspx_pipe->partition;
+ pipe->interlaced = false;
+ pipe->frame_end = vsp1_vspx_pipeline_frame_end;
+
+ mutex_init(&vspx_pipe->mutex);
+ spin_lock_init(&vspx_pipe->lock);
+
+ /*
+ * Initialize RPF0 as input for VSPX and use it unconditionally for
+ * now.
+ */
+ pipe->inputs[0] = vsp1->rpf[0];
+ pipe->inputs[0]->entity.pipe = pipe;
+ pipe->inputs[0]->entity.sink = &vsp1->iif->entity;
+ list_add_tail(&pipe->inputs[0]->entity.list_pipe, &pipe->entities);
+
+ pipe->iif = &vsp1->iif->entity;
+ pipe->iif->pipe = pipe;
+ pipe->iif->sink = &vsp1->wpf[0]->entity;
+ pipe->iif->sink_pad = RWPF_PAD_SINK;
+ list_add_tail(&pipe->iif->list_pipe, &pipe->entities);
+
+ pipe->output = vsp1->wpf[0];
+ pipe->output->entity.pipe = pipe;
+ list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);
+
+ return 0;
+}
+
+void vsp1_vspx_cleanup(struct vsp1_device *vsp1)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+
+ mutex_destroy(&vspx_pipe->mutex);
+}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.h b/drivers/media/platform/renesas/vsp1/vsp1_vspx.h
new file mode 100644
index 000000000000..f871bf9e7dec
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_vspx.h -- R-Car Gen 4 VSPX
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+#ifndef __VSP1_VSPX_H__
+#define __VSP1_VSPX_H__
+
+#include "vsp1.h"
+
+int vsp1_vspx_init(struct vsp1_device *vsp1);
+void vsp1_vspx_cleanup(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_VSPX_H__ */
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
index b41d3960c1b4..9bbeec4996aa 100644
--- a/drivers/media/platform/rockchip/Kconfig
+++ b/drivers/media/platform/rockchip/Kconfig
@@ -4,3 +4,4 @@ comment "Rockchip media platform drivers"
source "drivers/media/platform/rockchip/rga/Kconfig"
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
+source "drivers/media/platform/rockchip/rkvdec/Kconfig"
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
index 4f782b876ac9..286dc5c53f7e 100644
--- a/drivers/media/platform/rockchip/Makefile
+++ b/drivers/media/platform/rockchip/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += rga/
obj-y += rkisp1/
+obj-y += rkvdec/
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
index ca952fd0829b..5f187f9efc7b 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
@@ -415,6 +415,8 @@ struct rkisp1_params {
spinlock_t config_lock; /* locks the buffers list 'params' */
struct list_head params;
+ struct v4l2_ctrl_handler ctrls;
+
const struct v4l2_meta_format *metafmt;
enum v4l2_quantization quantization;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index b28f4140c8a3..f1585f8fa0f4 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -5,6 +5,7 @@
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
+#include <linux/bitfield.h>
#include <linux/math.h>
#include <linux/string.h>
@@ -60,6 +61,7 @@ union rkisp1_ext_params_config {
struct rkisp1_ext_params_afc_config afc;
struct rkisp1_ext_params_compand_bls_config compand_bls;
struct rkisp1_ext_params_compand_curve_config compand_curve;
+ struct rkisp1_ext_params_wdr_config wdr;
};
enum rkisp1_params_formats {
@@ -1348,6 +1350,73 @@ rkisp1_compand_compress_config(struct rkisp1_params *params,
arg->x);
}
+static void rkisp1_wdr_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_wdr_config *arg)
+{
+ unsigned int i;
+ u32 value;
+
+ value = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_WDR_CTRL)
+ & ~(RKISP1_CIF_ISP_WDR_USE_IREF |
+ RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT |
+ RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE |
+ RKISP1_CIF_ISP_WDR_USE_Y9_8 |
+ RKISP1_CIF_ISP_WDR_USE_RGB7_8 |
+ RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT |
+ RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK);
+
+ /* Colorspace and chrominance mapping */
+ if (arg->use_rgb_colorspace)
+ value |= RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT;
+
+ if (!arg->use_rgb_colorspace && arg->bypass_chroma_mapping)
+ value |= RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE;
+
+ /* Illumination reference */
+ if (arg->use_iref) {
+ value |= RKISP1_CIF_ISP_WDR_USE_IREF;
+
+ if (arg->iref_config.use_y9_8)
+ value |= RKISP1_CIF_ISP_WDR_USE_Y9_8;
+
+ if (arg->iref_config.use_rgb7_8)
+ value |= RKISP1_CIF_ISP_WDR_USE_RGB7_8;
+
+ if (arg->iref_config.disable_transient)
+ value |= RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT;
+
+ value |= FIELD_PREP(RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK,
+ min(arg->iref_config.rgb_factor,
+ RKISP1_CIF_ISP_WDR_RGB_FACTOR_MAX));
+ }
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_CTRL, value);
+
+ /* RGB and Luminance offsets */
+ value = FIELD_PREP(RKISP1_CIF_ISP_WDR_RGB_OFFSET_MASK,
+ arg->rgb_offset)
+ | FIELD_PREP(RKISP1_CIF_ISP_WDR_LUM_OFFSET_MASK,
+ arg->luma_offset);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_OFFSET, value);
+
+ /* DeltaMin */
+ value = FIELD_PREP(RKISP1_CIF_ISP_WDR_DMIN_THRESH_MASK,
+ arg->dmin_thresh)
+ | FIELD_PREP(RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK,
+ min(arg->dmin_strength,
+ RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX));
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_DELTAMIN, value);
+
+ /* Tone curve */
+ for (i = 0; i < RKISP1_CIF_ISP_WDR_CURVE_NUM_DY_REGS; i++)
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_TONECURVE(i),
+ arg->tone_curve.dY[i]);
+ for (i = 0; i < RKISP1_CIF_ISP_WDR_CURVE_NUM_COEFF; i++)
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_TONECURVE_YM(i),
+ arg->tone_curve.ym[i] &
+ RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK);
+}
+
static void
rkisp1_isp_isr_other_config(struct rkisp1_params *params,
const struct rkisp1_params_cfg *new_params)
@@ -2005,6 +2074,25 @@ static void rkisp1_ext_params_compand_compress(struct rkisp1_params *params,
RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE);
}
+static void rkisp1_ext_params_wdr(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_wdr_config *wdr = &block->wdr;
+
+ if (wdr->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_WDR_CTRL,
+ RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
+ return;
+ }
+
+ rkisp1_wdr_config(params, &wdr->config);
+
+ if ((wdr->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(wdr->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_WDR_CTRL,
+ RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
+}
+
typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
const union rkisp1_ext_params_config *config);
@@ -2118,6 +2206,11 @@ static const struct rkisp1_ext_params_handler {
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
.features = RKISP1_FEATURE_COMPAND,
},
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR] = {
+ .size = sizeof(struct rkisp1_ext_params_wdr_config),
+ .handler = rkisp1_ext_params_wdr,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
};
static void rkisp1_ext_params_config(struct rkisp1_params *params,
@@ -2736,6 +2829,44 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q,
return vb2_queue_init(q);
}
+static int rkisp1_params_ctrl_init(struct rkisp1_params *params)
+{
+ struct v4l2_ctrl_config ctrl_config = {
+ .id = RKISP1_CID_SUPPORTED_PARAMS_BLOCKS,
+ .name = "Supported Params Blocks",
+ .type = V4L2_CTRL_TYPE_BITMASK,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ };
+ int ret;
+
+ v4l2_ctrl_handler_init(&params->ctrls, 1);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(rkisp1_ext_params_handlers); i++) {
+ const struct rkisp1_ext_params_handler *block_handler;
+
+ block_handler = &rkisp1_ext_params_handlers[i];
+ ctrl_config.max |= BIT(i);
+
+ if ((params->rkisp1->info->features & block_handler->features) !=
+ block_handler->features)
+ continue;
+
+ ctrl_config.def |= BIT(i);
+ }
+
+ v4l2_ctrl_new_custom(&params->ctrls, &ctrl_config, NULL);
+
+ params->vnode.vdev.ctrl_handler = &params->ctrls;
+
+ if (params->ctrls.error) {
+ ret = params->ctrls.error;
+ v4l2_ctrl_handler_free(&params->ctrls);
+ return ret;
+ }
+
+ return 0;
+}
+
int rkisp1_params_register(struct rkisp1_device *rkisp1)
{
struct rkisp1_params *params = &rkisp1->params;
@@ -2763,7 +2894,9 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1)
vdev->queue = &node->buf_queue;
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
vdev->vfl_dir = VFL_DIR_TX;
- rkisp1_params_init_vb2_queue(vdev->queue, params);
+ ret = rkisp1_params_init_vb2_queue(vdev->queue, params);
+ if (ret)
+ goto err_media;
params->metafmt = &rkisp1_params_formats[RKISP1_PARAMS_FIXED];
@@ -2777,18 +2910,26 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1)
node->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret)
- goto error;
+ goto err_media;
+
+ ret = rkisp1_params_ctrl_init(params);
+ if (ret) {
+ dev_err(rkisp1->dev, "Control initialization error %d\n", ret);
+ goto err_media;
+ }
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(rkisp1->dev,
"failed to register %s, ret=%d\n", vdev->name, ret);
- goto error;
+ goto err_ctrl;
}
return 0;
-error:
+err_ctrl:
+ v4l2_ctrl_handler_free(&params->ctrls);
+err_media:
media_entity_cleanup(&vdev->entity);
mutex_destroy(&node->vlock);
return ret;
@@ -2804,6 +2945,7 @@ void rkisp1_params_unregister(struct rkisp1_device *rkisp1)
return;
vb2_video_unregister_device(vdev);
+ v4l2_ctrl_handler_free(&params->ctrls);
media_entity_cleanup(&vdev->entity);
mutex_destroy(&node->vlock);
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index 139177db9c6d..fbeb186cde0d 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -703,6 +703,27 @@
#define RKISP1_CIF_ISP_COMPAND_CTRL_SOFT_RESET_FLAG BIT(2)
#define RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE BIT(3)
+/* WDR */
+/* ISP_WDR_CTRL */
+#define RKISP1_CIF_ISP_WDR_CTRL_ENABLE BIT(0)
+#define RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT BIT(1)
+#define RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE BIT(2)
+#define RKISP1_CIF_ISP_WDR_USE_IREF BIT(3)
+#define RKISP1_CIF_ISP_WDR_USE_Y9_8 BIT(4)
+#define RKISP1_CIF_ISP_WDR_USE_RGB7_8 BIT(5)
+#define RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT BIT(6)
+#define RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK GENMASK(11, 8)
+#define RKISP1_CIF_ISP_WDR_RGB_FACTOR_MAX 8U
+/* ISP_WDR_TONE_CURVE_YM */
+#define RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK GENMASK(12, 0)
+/* ISP_WDR_OFFSET */
+#define RKISP1_CIF_ISP_WDR_RGB_OFFSET_MASK GENMASK(11, 0)
+#define RKISP1_CIF_ISP_WDR_LUM_OFFSET_MASK GENMASK(27, 16)
+/* ISP_WDR_DELTAMIN */
+#define RKISP1_CIF_ISP_WDR_DMIN_THRESH_MASK GENMASK(11, 0)
+#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16)
+#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U
+
/* =================================================================== */
/* CIF Registers */
/* =================================================================== */
@@ -1295,82 +1316,12 @@
#define RKISP1_CIF_ISP_WDR_BASE 0x00002a00
#define RKISP1_CIF_ISP_WDR_CTRL (RKISP1_CIF_ISP_WDR_BASE + 0x00000000)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000004)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_2 (RKISP1_CIF_ISP_WDR_BASE + 0x00000008)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_3 (RKISP1_CIF_ISP_WDR_BASE + 0x0000000c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000010)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0 (RKISP1_CIF_ISP_WDR_BASE + 0x00000014)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000018)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2 (RKISP1_CIF_ISP_WDR_BASE + 0x0000001c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3 (RKISP1_CIF_ISP_WDR_BASE + 0x00000020)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000024)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5 (RKISP1_CIF_ISP_WDR_BASE + 0x00000028)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6 (RKISP1_CIF_ISP_WDR_BASE + 0x0000002c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7 (RKISP1_CIF_ISP_WDR_BASE + 0x00000030)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8 (RKISP1_CIF_ISP_WDR_BASE + 0x00000034)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9 (RKISP1_CIF_ISP_WDR_BASE + 0x00000038)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10 (RKISP1_CIF_ISP_WDR_BASE + 0x0000003c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11 (RKISP1_CIF_ISP_WDR_BASE + 0x00000040)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12 (RKISP1_CIF_ISP_WDR_BASE + 0x00000044)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13 (RKISP1_CIF_ISP_WDR_BASE + 0x00000048)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14 (RKISP1_CIF_ISP_WDR_BASE + 0x0000004c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15 (RKISP1_CIF_ISP_WDR_BASE + 0x00000050)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16 (RKISP1_CIF_ISP_WDR_BASE + 0x00000054)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17 (RKISP1_CIF_ISP_WDR_BASE + 0x00000058)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18 (RKISP1_CIF_ISP_WDR_BASE + 0x0000005c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19 (RKISP1_CIF_ISP_WDR_BASE + 0x00000060)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20 (RKISP1_CIF_ISP_WDR_BASE + 0x00000064)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21 (RKISP1_CIF_ISP_WDR_BASE + 0x00000068)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22 (RKISP1_CIF_ISP_WDR_BASE + 0x0000006c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23 (RKISP1_CIF_ISP_WDR_BASE + 0x00000070)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24 (RKISP1_CIF_ISP_WDR_BASE + 0x00000074)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25 (RKISP1_CIF_ISP_WDR_BASE + 0x00000078)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26 (RKISP1_CIF_ISP_WDR_BASE + 0x0000007c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27 (RKISP1_CIF_ISP_WDR_BASE + 0x00000080)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28 (RKISP1_CIF_ISP_WDR_BASE + 0x00000084)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29 (RKISP1_CIF_ISP_WDR_BASE + 0x00000088)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30 (RKISP1_CIF_ISP_WDR_BASE + 0x0000008c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31 (RKISP1_CIF_ISP_WDR_BASE + 0x00000090)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32 (RKISP1_CIF_ISP_WDR_BASE + 0x00000094)
+#define RKISP1_CIF_ISP_WDR_TONECURVE(n) (RKISP1_CIF_ISP_WDR_BASE + 0x00000004 + (n) * 4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM(n) (RKISP1_CIF_ISP_WDR_BASE + 0x00000014 + (n) * 4)
#define RKISP1_CIF_ISP_WDR_OFFSET (RKISP1_CIF_ISP_WDR_BASE + 0x00000098)
#define RKISP1_CIF_ISP_WDR_DELTAMIN (RKISP1_CIF_ISP_WDR_BASE + 0x0000009c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000ac)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000bc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000cc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000dc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000ec)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000fc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000100)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000104)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000108)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000010c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000110)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000114)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000118)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000011c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000120)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000124)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000128)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000012c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000130)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_SHD(n) (RKISP1_CIF_ISP_WDR_BASE + 0x000000a0 + (n) * 4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_SHD(n) (RKISP1_CIF_ISP_WDR_BASE + 0x000000b0 + (n) * 4)
#define RKISP1_CIF_ISP_HIST_BASE_V12 0x00002c00
#define RKISP1_CIF_ISP_HIST_CTRL_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000000)
diff --git a/drivers/media/platform/rockchip/rkvdec/Kconfig b/drivers/media/platform/rockchip/rkvdec/Kconfig
new file mode 100644
index 000000000000..5f3bdd848a2c
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+config VIDEO_ROCKCHIP_VDEC
+ tristate "Rockchip Video Decoder driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_MEM2MEM_DEV
+ select V4L2_H264
+ select V4L2_VP9
+ help
+ Support for the Rockchip Video Decoder IP present on Rockchip SoCs,
+ which accelerates video decoding.
+ To compile this driver as a module, choose M here: the module
+ will be called rockchip-vdec.
diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile
new file mode 100644
index 000000000000..cb86b429cfaa
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o
+
+rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-vp9.o
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
new file mode 100644
index 000000000000..d14b4d173448
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
@@ -0,0 +1,1212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Video Decoder H264 backend
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ */
+
+#include <media/v4l2-h264.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-regs.h"
+
+/* Size with u32 units. */
+#define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128)
+#define RKV_RPS_SIZE ((128 + 128) / 4)
+#define RKV_ERROR_INFO_SIZE (256 * 144 * 4)
+
+#define RKVDEC_NUM_REFLIST 3
+
+struct rkvdec_h264_scaling_list {
+ u8 scaling_list_4x4[6][16];
+ u8 scaling_list_8x8[6][64];
+ u8 padding[128];
+};
+
+struct rkvdec_sps_pps_packet {
+ u32 info[8];
+};
+
+struct rkvdec_ps_field {
+ u16 offset;
+ u8 len;
+};
+
+#define PS_FIELD(_offset, _len) \
+ ((struct rkvdec_ps_field){ _offset, _len })
+
+#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4)
+#define PROFILE_IDC PS_FIELD(4, 8)
+#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1)
+#define CHROMA_FORMAT_IDC PS_FIELD(13, 2)
+#define BIT_DEPTH_LUMA PS_FIELD(15, 3)
+#define BIT_DEPTH_CHROMA PS_FIELD(18, 3)
+#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1)
+#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4)
+#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5)
+#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2)
+#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4)
+#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1)
+#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9)
+#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9)
+#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1)
+#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1)
+#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1)
+#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1)
+#define NUM_VIEWS PS_FIELD(60, 2)
+#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10)
+#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1)
+#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10)
+#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1)
+#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10)
+#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8)
+#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5)
+#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1)
+#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1)
+#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5)
+#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1)
+#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2)
+#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7)
+#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6)
+#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5)
+#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1)
+#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1)
+#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1)
+#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1)
+#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5)
+#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1)
+#define SCALING_LIST_ADDRESS PS_FIELD(184, 32)
+#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1)
+
+#define DPB_OFFS(i, j) (288 + ((j) * 32 * 7) + ((i) * 7))
+#define DPB_INFO(i, j) PS_FIELD(DPB_OFFS(i, j), 5)
+#define BOTTOM_FLAG(i, j) PS_FIELD(DPB_OFFS(i, j) + 5, 1)
+#define VIEW_INDEX_OFF(i, j) PS_FIELD(DPB_OFFS(i, j) + 6, 1)
+
+/* Data structure describing auxiliary buffer format. */
+struct rkvdec_h264_priv_tbl {
+ s8 cabac_table[4][464][2];
+ struct rkvdec_h264_scaling_list scaling_list;
+ u32 rps[RKV_RPS_SIZE];
+ struct rkvdec_sps_pps_packet param_set[256];
+ u8 err_info[RKV_ERROR_INFO_SIZE];
+};
+
+struct rkvdec_h264_reflists {
+ struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN];
+ struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN];
+ struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN];
+};
+
+struct rkvdec_h264_run {
+ struct rkvdec_run base;
+ const struct v4l2_ctrl_h264_decode_params *decode_params;
+ const struct v4l2_ctrl_h264_sps *sps;
+ const struct v4l2_ctrl_h264_pps *pps;
+ const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix;
+ struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES];
+};
+
+struct rkvdec_h264_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct rkvdec_h264_reflists reflists;
+};
+
+#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \
+ idc2_m, idc2_n, intra_m, intra_n) \
+ [0][(ctxidx)] = {idc0_m, idc0_n}, \
+ [1][(ctxidx)] = {idc1_m, idc1_n}, \
+ [2][(ctxidx)] = {idc2_m, idc2_n}, \
+ [3][(ctxidx)] = {intra_m, intra_n}
+
+/*
+ * Constant CABAC table.
+ * Built from the tables described in section '9.3.1.1 Initialisation process
+ * for context variables' of the H264 spec.
+ */
+static const s8 rkvdec_h264_cabac_table[4][464][2] = {
+ /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */
+ CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15),
+ CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54),
+ CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74),
+ CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15),
+ CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54),
+ CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74),
+ CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127),
+ CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104),
+ CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53),
+ CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54),
+ CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51),
+
+ /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */
+ CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0),
+ CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0),
+ CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0),
+ CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0),
+ CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0),
+ CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0),
+ CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0),
+ CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0),
+ CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0),
+ CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0),
+ CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0),
+ CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0),
+ CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0),
+
+ /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */
+ CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0),
+ CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0),
+ CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0),
+ CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0),
+ CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0),
+ CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0),
+ CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0),
+ CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0),
+ CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0),
+ CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0),
+ CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0),
+ CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0),
+ CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0),
+ CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0),
+ CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0),
+ CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0),
+
+ /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */
+ CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0),
+ CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0),
+ CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0),
+ CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0),
+ CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0),
+ CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0),
+ CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0),
+ CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0),
+ CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0),
+ CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0),
+ CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0),
+ CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0),
+ CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0),
+ CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0),
+
+ /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */
+ CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0),
+ CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0),
+ CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0),
+ CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0),
+ CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0),
+ CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0),
+
+ /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */
+ CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41),
+ CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63),
+ CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63),
+ CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63),
+ CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83),
+ CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86),
+ CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97),
+ CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72),
+ CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41),
+ CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62),
+
+ /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */
+ CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11),
+ CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55),
+ CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69),
+ CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127),
+ CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102),
+ CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82),
+ CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74),
+ CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107),
+ CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127),
+ CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127),
+ CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127),
+ CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95),
+ CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127),
+ CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114),
+ CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127),
+ CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123),
+ CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115),
+ CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122),
+ CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115),
+ CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63),
+ CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68),
+ CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84),
+ CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104),
+ CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70),
+ CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93),
+ CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90),
+ CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127),
+ CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74),
+ CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97),
+ CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91),
+ CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127),
+ CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56),
+ CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82),
+ CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76),
+ CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125),
+
+ /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */
+ CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93),
+ CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87),
+ CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77),
+ CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71),
+ CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63),
+ CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68),
+ CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84),
+ CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62),
+ CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65),
+ CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61),
+ CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56),
+ CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66),
+ CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64),
+ CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61),
+ CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78),
+ CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50),
+ CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52),
+ CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35),
+ CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44),
+ CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38),
+ CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45),
+ CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46),
+ CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44),
+ CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17),
+ CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51),
+ CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50),
+ CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19),
+ CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33),
+ CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62),
+ CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108),
+ CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100),
+ CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101),
+ CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91),
+ CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94),
+ CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88),
+ CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84),
+ CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86),
+ CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83),
+ CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87),
+ CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94),
+ CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70),
+ CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72),
+ CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74),
+ CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59),
+ CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102),
+ CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100),
+ CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95),
+ CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75),
+ CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72),
+ CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75),
+ CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71),
+ CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46),
+ CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69),
+ CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62),
+ CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65),
+ CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37),
+ CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72),
+ CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57),
+ CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54),
+ CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62),
+ CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72),
+
+ /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */
+ CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0),
+ CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9),
+ CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25),
+ CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18),
+ CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9),
+ CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19),
+ CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37),
+ CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18),
+ CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29),
+ CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33),
+ CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30),
+ CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45),
+ CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58),
+ CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62),
+ CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61),
+ CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38),
+ CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45),
+ CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39),
+ CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42),
+ CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44),
+ CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45),
+ CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41),
+ CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49),
+ CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34),
+ CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42),
+ CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55),
+ CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51),
+ CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46),
+ CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89),
+ CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19),
+ CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17),
+ CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17),
+ CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25),
+ CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20),
+ CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23),
+ CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27),
+ CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23),
+ CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28),
+ CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17),
+ CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11),
+ CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15),
+ CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6),
+ CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1),
+ CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17),
+ CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6),
+ CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3),
+ CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22),
+ CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16),
+ CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4),
+ CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8),
+ CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3),
+ CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3),
+ CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5),
+ CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0),
+ CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16),
+ CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22),
+ CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48),
+ CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37),
+ CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60),
+ CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68),
+ CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97),
+
+ /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */
+ CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71),
+ CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42),
+ CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50),
+ CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54),
+ CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62),
+ CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58),
+ CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63),
+ CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72),
+ CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74),
+ CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91),
+ CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67),
+ CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27),
+ CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39),
+ CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44),
+ CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46),
+ CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64),
+ CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68),
+ CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78),
+ CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77),
+ CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86),
+ CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92),
+ CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55),
+ CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60),
+ CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62),
+ CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65),
+ CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73),
+ CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76),
+ CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80),
+ CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88),
+ CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110),
+ CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97),
+ CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84),
+ CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79),
+ CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73),
+ CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74),
+ CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86),
+ CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96),
+ CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97),
+ CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117),
+ CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78),
+ CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33),
+ CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48),
+ CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53),
+ CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62),
+ CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71),
+ CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79),
+ CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86),
+ CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90),
+ CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97),
+
+ /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */
+ CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93),
+ CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84),
+ CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79),
+ CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66),
+ CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71),
+ CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62),
+ CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60),
+ CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59),
+ CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75),
+ CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62),
+ CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58),
+ CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66),
+ CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79),
+ CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71),
+ CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68),
+ CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44),
+ CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62),
+ CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36),
+ CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40),
+ CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27),
+ CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29),
+ CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44),
+ CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36),
+ CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32),
+ CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42),
+ CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48),
+ CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62),
+ CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46),
+ CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64),
+ CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104),
+ CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97),
+ CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96),
+ CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88),
+ CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85),
+ CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85),
+ CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85),
+ CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88),
+ CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66),
+ CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77),
+ CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76),
+ CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76),
+ CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58),
+ CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76),
+ CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83),
+ CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99),
+ CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95),
+ CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95),
+ CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76),
+ CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74),
+ CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70),
+ CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75),
+ CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68),
+ CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65),
+ CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73),
+ CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62),
+ CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62),
+ CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68),
+ CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75),
+ CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55),
+ CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64),
+ CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70),
+
+ /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */
+ CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6),
+ CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19),
+ CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16),
+ CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14),
+ CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13),
+ CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11),
+ CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15),
+ CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16),
+ CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23),
+ CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23),
+ CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20),
+ CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26),
+ CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44),
+ CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40),
+ CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47),
+ CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17),
+ CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21),
+ CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22),
+ CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27),
+ CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29),
+ CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35),
+ CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50),
+ CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57),
+ CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63),
+ CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77),
+ CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82),
+ CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94),
+ CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69),
+ CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109),
+ CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35),
+ CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34),
+ CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26),
+ CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30),
+ CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32),
+ CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18),
+ CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15),
+ CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15),
+ CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7),
+ CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5),
+ CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0),
+ CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2),
+ CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13),
+ CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35),
+ CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58),
+ CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3),
+ CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0),
+ CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30),
+ CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7),
+ CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15),
+ CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3),
+ CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3),
+ CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1),
+ CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5),
+ CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11),
+ CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5),
+ CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12),
+ CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11),
+ CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29),
+ CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26),
+ CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39),
+ CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66),
+
+ /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */
+ CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21),
+ CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31),
+ CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50),
+ CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120),
+ CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112),
+ CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114),
+ CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85),
+ CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92),
+ CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89),
+ CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71),
+ CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81),
+ CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80),
+ CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68),
+ CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70),
+ CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56),
+ CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68),
+ CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50),
+ CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74),
+ CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13),
+ CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13),
+ CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15),
+ CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14),
+ CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3),
+ CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6),
+ CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34),
+ CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54),
+ CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82),
+ CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75),
+ CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23),
+ CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34),
+ CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43),
+ CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54),
+ CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55),
+ CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61),
+ CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64),
+ CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68),
+ CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92),
+ CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106),
+ CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97),
+ CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90),
+ CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90),
+ CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88),
+ CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73),
+ CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79),
+ CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86),
+ CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73),
+ CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70),
+ CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69),
+ CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66),
+ CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64),
+ CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58),
+ CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59),
+ CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10),
+ CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11),
+ CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8),
+ CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1),
+ CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3),
+ CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9),
+ CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20),
+ CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36),
+ CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67),
+};
+
+static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value)
+{
+ u8 bit = field.offset % 32, word = field.offset / 32;
+ u64 mask = GENMASK_ULL(bit + field.len - 1, bit);
+ u64 val = ((u64)value << bit) & mask;
+
+ buf[word] &= ~mask;
+ buf[word] |= val;
+ if (bit + field.len > 32) {
+ buf[word + 1] &= ~(mask >> 32);
+ buf[word + 1] |= val >> 32;
+ }
+}
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ const struct v4l2_ctrl_h264_sps *sps = run->sps;
+ const struct v4l2_ctrl_h264_pps *pps = run->pps;
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
+ struct rkvdec_sps_pps_packet *hw_ps;
+ dma_addr_t scaling_list_address;
+ u32 scaling_distance;
+ u32 i;
+
+ /*
+ * HW read the SPS/PPS information from PPS packet index by PPS id.
+ * offset from the base can be calculated by PPS_id * 32 (size per PPS
+ * packet unit). so the driver copy SPS/PPS information to the exact PPS
+ * packet unit for HW accessing.
+ */
+ hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value)
+ /* write sps */
+ WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID);
+ WRITE_PPS(sps->profile_idc, PROFILE_IDC);
+ WRITE_PPS(!!(sps->constraint_set_flags & (1 << 3)), CONSTRAINT_SET3_FLAG);
+ WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC);
+ WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA);
+ WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA);
+ WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS),
+ QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG);
+ WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4);
+ WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES);
+ WRITE_PPS(sps->pic_order_cnt_type, PIC_ORDER_CNT_TYPE);
+ WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4,
+ LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4);
+ WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO),
+ DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG);
+
+ /*
+ * Use the SPS values since they are already in macroblocks
+ * dimensions, height can be field height (halved) if
+ * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
+ * decoding smaller images into larger allocation which can be used
+ * to implementing SVC spatial layer support.
+ */
+ WRITE_PPS(sps->pic_width_in_mbs_minus1 + 1, PIC_WIDTH_IN_MBS);
+ WRITE_PPS(sps->pic_height_in_map_units_minus1 + 1, PIC_HEIGHT_IN_MBS);
+
+ WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY),
+ FRAME_MBS_ONLY_FLAG);
+ WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD),
+ MB_ADAPTIVE_FRAME_FIELD_FLAG);
+ WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE),
+ DIRECT_8X8_INFERENCE_FLAG);
+
+ /* write pps */
+ WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID);
+ WRITE_PPS(pps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID);
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE),
+ ENTROPY_CODING_MODE_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT),
+ BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG);
+ WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1,
+ NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0));
+ WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1,
+ NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1));
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED),
+ WEIGHTED_PRED_FLAG);
+ WRITE_PPS(pps->weighted_bipred_idc, WEIGHTED_BIPRED_IDC);
+ WRITE_PPS(pps->pic_init_qp_minus26, PIC_INIT_QP_MINUS26);
+ WRITE_PPS(pps->pic_init_qs_minus26, PIC_INIT_QS_MINUS26);
+ WRITE_PPS(pps->chroma_qp_index_offset, CHROMA_QP_INDEX_OFFSET);
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT),
+ DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED),
+ CONSTRAINED_INTRA_PRED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT),
+ REDUNDANT_PIC_CNT_PRESENT);
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE),
+ TRANSFORM_8X8_MODE_FLAG);
+ WRITE_PPS(pps->second_chroma_qp_index_offset,
+ SECOND_CHROMA_QP_INDEX_OFFSET);
+
+ WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT),
+ SCALING_LIST_ENABLE_FLAG);
+ /* To be on the safe side, program the scaling matrix address */
+ scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
+ scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance;
+ WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS);
+
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ u32 is_longterm = 0;
+
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
+ is_longterm = 1;
+
+ WRITE_PPS(is_longterm, IS_LONG_TERM(i));
+ }
+}
+
+static void lookup_ref_buf_idx(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb;
+ struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
+ struct vb2_buffer *buf = NULL;
+
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) {
+ buf = vb2_find_buffer(cap_q, dpb[i].reference_ts);
+ if (!buf)
+ pr_debug("No buffer for reference_ts %llu",
+ dpb[i].reference_ts);
+ }
+
+ run->ref_buf[i] = buf;
+ }
+}
+
+static void assemble_hw_rps(struct rkvdec_ctx *ctx,
+ struct v4l2_h264_reflist_builder *builder,
+ struct rkvdec_h264_run *run)
+{
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
+
+ u32 *hw_rps = priv_tbl->rps;
+ u32 i, j;
+ u16 *p = (u16 *)hw_rps;
+
+ memset(hw_rps, 0, sizeof(priv_tbl->rps));
+
+ /*
+ * Assign an invalid pic_num if DPB entry at that position is inactive.
+ * If we assign 0 in that position hardware will treat that as a real
+ * reference picture with pic_num 0, triggering output picture
+ * corruption.
+ */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
+ continue;
+
+ p[i] = builder->refs[i].frame_num;
+ }
+
+ for (j = 0; j < RKVDEC_NUM_REFLIST; j++) {
+ for (i = 0; i < builder->num_valid; i++) {
+ struct v4l2_h264_reference *ref;
+ bool dpb_valid;
+ bool bottom;
+
+ switch (j) {
+ case 0:
+ ref = &h264_ctx->reflists.p[i];
+ break;
+ case 1:
+ ref = &h264_ctx->reflists.b0[i];
+ break;
+ case 2:
+ ref = &h264_ctx->reflists.b1[i];
+ break;
+ }
+
+ if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb)))
+ continue;
+
+ dpb_valid = run->ref_buf[ref->index] != NULL;
+ bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF;
+
+ set_ps_field(hw_rps, DPB_INFO(i, j),
+ ref->index | dpb_valid << 4);
+ set_ps_field(hw_rps, BOTTOM_FLAG(i, j), bottom);
+ }
+ }
+}
+
+static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix;
+ const struct v4l2_ctrl_h264_pps *pps = run->pps;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
+
+ if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT))
+ return;
+
+ BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_4x4) !=
+ sizeof(scaling->scaling_list_4x4));
+ BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_8x8) !=
+ sizeof(scaling->scaling_list_8x8));
+
+ memcpy(tbl->scaling_list.scaling_list_4x4,
+ scaling->scaling_list_4x4,
+ sizeof(scaling->scaling_list_4x4));
+
+ memcpy(tbl->scaling_list.scaling_list_8x8,
+ scaling->scaling_list_8x8,
+ sizeof(scaling->scaling_list_8x8));
+}
+
+/*
+ * dpb poc related registers table
+ */
+static const u32 poc_reg_tbl_top_field[16] = {
+ RKVDEC_REG_H264_POC_REFER0(0),
+ RKVDEC_REG_H264_POC_REFER0(2),
+ RKVDEC_REG_H264_POC_REFER0(4),
+ RKVDEC_REG_H264_POC_REFER0(6),
+ RKVDEC_REG_H264_POC_REFER0(8),
+ RKVDEC_REG_H264_POC_REFER0(10),
+ RKVDEC_REG_H264_POC_REFER0(12),
+ RKVDEC_REG_H264_POC_REFER0(14),
+ RKVDEC_REG_H264_POC_REFER1(1),
+ RKVDEC_REG_H264_POC_REFER1(3),
+ RKVDEC_REG_H264_POC_REFER1(5),
+ RKVDEC_REG_H264_POC_REFER1(7),
+ RKVDEC_REG_H264_POC_REFER1(9),
+ RKVDEC_REG_H264_POC_REFER1(11),
+ RKVDEC_REG_H264_POC_REFER1(13),
+ RKVDEC_REG_H264_POC_REFER2(0)
+};
+
+static const u32 poc_reg_tbl_bottom_field[16] = {
+ RKVDEC_REG_H264_POC_REFER0(1),
+ RKVDEC_REG_H264_POC_REFER0(3),
+ RKVDEC_REG_H264_POC_REFER0(5),
+ RKVDEC_REG_H264_POC_REFER0(7),
+ RKVDEC_REG_H264_POC_REFER0(9),
+ RKVDEC_REG_H264_POC_REFER0(11),
+ RKVDEC_REG_H264_POC_REFER0(13),
+ RKVDEC_REG_H264_POC_REFER1(0),
+ RKVDEC_REG_H264_POC_REFER1(2),
+ RKVDEC_REG_H264_POC_REFER1(4),
+ RKVDEC_REG_H264_POC_REFER1(6),
+ RKVDEC_REG_H264_POC_REFER1(8),
+ RKVDEC_REG_H264_POC_REFER1(10),
+ RKVDEC_REG_H264_POC_REFER1(12),
+ RKVDEC_REG_H264_POC_REFER1(14),
+ RKVDEC_REG_H264_POC_REFER2(1)
+};
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
+ const struct v4l2_ctrl_h264_sps *sps = run->sps;
+ const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
+ const struct v4l2_pix_format_mplane *dst_fmt;
+ struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+ struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+ const struct v4l2_format *f;
+ dma_addr_t rlc_addr;
+ dma_addr_t refer_addr;
+ u32 rlc_len;
+ u32 hor_virstride;
+ u32 ver_virstride;
+ u32 y_virstride;
+ u32 yuv_virstride = 0;
+ u32 offset;
+ dma_addr_t dst_addr;
+ u32 reg, i;
+
+ reg = RKVDEC_MODE(RKVDEC_MODE_H264);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL);
+
+ f = &ctx->decoded_fmt;
+ dst_fmt = &f->fmt.pix_mp;
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
+ y_virstride = hor_virstride * ver_virstride;
+
+ if (sps->chroma_format_idc == 0)
+ yuv_virstride = y_virstride;
+ else if (sps->chroma_format_idc == 1)
+ yuv_virstride = y_virstride + y_virstride / 2;
+ else if (sps->chroma_format_idc == 2)
+ yuv_virstride = 2 * y_virstride;
+
+ reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) |
+ RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) |
+ RKVDEC_SLICE_NUM_HIGHBIT |
+ RKVDEC_SLICE_NUM_LOWBITS(0x7ff);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR);
+
+ /* config rlc base address */
+ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE);
+ writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_RLCWRITE_BASE);
+
+ rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+ reg = RKVDEC_STRM_LEN(rlc_len);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN);
+
+ /* config cabac table */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE);
+
+ /* config output base address */
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE);
+
+ reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE);
+
+ reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE);
+
+ /* config ref pic address & poc */
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ struct vb2_buffer *vb_buf = run->ref_buf[i];
+
+ /*
+ * If a DPB entry is unused or invalid, address of current destination
+ * buffer is returned.
+ */
+ if (!vb_buf)
+ vb_buf = &dst_buf->vb2_buf;
+ refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)
+ refer_addr |= RKVDEC_COLMV_USED_FLAG_REF;
+ if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)
+ refer_addr |= RKVDEC_FIELD_REF;
+
+ if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF)
+ refer_addr |= RKVDEC_TOPFIELD_USED_REF;
+ if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)
+ refer_addr |= RKVDEC_BOTFIELD_USED_REF;
+
+ writel_relaxed(dpb[i].top_field_order_cnt,
+ rkvdec->regs + poc_reg_tbl_top_field[i]);
+ writel_relaxed(dpb[i].bottom_field_order_cnt,
+ rkvdec->regs + poc_reg_tbl_bottom_field[i]);
+
+ if (i < V4L2_H264_NUM_DPB_ENTRIES - 1)
+ writel_relaxed(refer_addr,
+ rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i));
+ else
+ writel_relaxed(refer_addr,
+ rkvdec->regs + RKVDEC_REG_H264_BASE_REFER15);
+ }
+
+ reg = RKVDEC_CUR_POC(dec_params->top_field_order_cnt);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0);
+
+ reg = RKVDEC_CUR_POC(dec_params->bottom_field_order_cnt);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC1);
+
+ /* config hw pps address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_PPS_BASE);
+
+ /* config hw rps address */
+ offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_RPS_BASE);
+
+ reg = RKVDEC_AXI_DDR_RDATA(0);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA);
+
+ reg = RKVDEC_AXI_DDR_WDATA(0);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA);
+
+ offset = offsetof(struct rkvdec_h264_priv_tbl, err_info);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_H264_ERRINFO_BASE);
+}
+
+#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2
+
+static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+ fmt->num_planes = 1;
+ if (!fmt->plane_fmt[0].sizeimage)
+ fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
+ RKVDEC_H264_MAX_DEPTH_IN_BYTES;
+ return 0;
+}
+
+static enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_ctrl *ctrl)
+{
+ const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
+
+ if (ctrl->id != V4L2_CID_STATELESS_H264_SPS)
+ return RKVDEC_IMG_FMT_ANY;
+
+ if (sps->bit_depth_luma_minus8 == 0) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_8BIT;
+ else
+ return RKVDEC_IMG_FMT_420_8BIT;
+ } else if (sps->bit_depth_luma_minus8 == 2) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_10BIT;
+ else
+ return RKVDEC_IMG_FMT_420_10BIT;
+ }
+
+ return RKVDEC_IMG_FMT_ANY;
+}
+
+static int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx,
+ const struct v4l2_ctrl_h264_sps *sps)
+{
+ unsigned int width, height;
+
+ if (sps->chroma_format_idc > 2)
+ /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
+ /* Only 8-bit and 10-bit is supported */
+ return -EINVAL;
+
+ width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
+ height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
+
+ /*
+ * When frame_mbs_only_flag is not set, this is field height,
+ * which is half the final height (see (7-18) in the
+ * specification)
+ */
+ if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
+ height *= 2;
+
+ if (width > ctx->coded_fmt.fmt.pix_mp.width ||
+ height > ctx->coded_fmt.fmt.pix_mp.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_priv_tbl *priv_tbl;
+ struct rkvdec_h264_ctx *h264_ctx;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SPS);
+ if (!ctrl)
+ return -EINVAL;
+
+ ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+ if (ret)
+ return ret;
+
+ h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL);
+ if (!h264_ctx)
+ return -ENOMEM;
+
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &h264_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ ret = -ENOMEM;
+ goto err_free_ctx;
+ }
+
+ h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ h264_ctx->priv_tbl.cpu = priv_tbl;
+ memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
+ sizeof(rkvdec_h264_cabac_table));
+
+ ctx->priv = h264_ctx;
+ return 0;
+
+err_free_ctx:
+ kfree(h264_ctx);
+ return ret;
+}
+
+static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
+ h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
+ kfree(h264_ctx);
+}
+
+static void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx,
+ struct rkvdec_h264_run *run)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_DECODE_PARAMS);
+ run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SPS);
+ run->sps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_PPS);
+ run->pps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_H264_SCALING_MATRIX);
+ run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
+
+ rkvdec_run_preamble(ctx, &run->base);
+}
+
+static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
+{
+ struct v4l2_h264_reflist_builder reflist_builder;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
+ struct rkvdec_h264_run run;
+
+ rkvdec_h264_run_preamble(ctx, &run);
+
+ /* Build the P/B{0,1} ref lists. */
+ v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
+ run.sps, run.decode_params->dpb);
+ v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
+ v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
+ h264_ctx->reflists.b1);
+
+ assemble_hw_scaling_list(ctx, &run);
+ assemble_hw_pps(ctx, &run);
+ lookup_ref_buf_idx(ctx, &run);
+ assemble_hw_rps(ctx, &reflist_builder, &run);
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
+
+ writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
+ writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E);
+ writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
+ writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
+
+ /* Start decoding! */
+ writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
+ RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E,
+ rkvdec->regs + RKVDEC_REG_INTERRUPT);
+
+ return 0;
+}
+
+static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
+ return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
+
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = {
+ .adjust_fmt = rkvdec_h264_adjust_fmt,
+ .start = rkvdec_h264_start,
+ .stop = rkvdec_h264_stop,
+ .run = rkvdec_h264_run,
+ .try_ctrl = rkvdec_h264_try_ctrl,
+ .get_image_fmt = rkvdec_h264_get_image_fmt,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
new file mode 100644
index 000000000000..15b9bee92016
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef RKVDEC_REGS_H_
+#define RKVDEC_REGS_H_
+
+/* rkvcodec registers */
+#define RKVDEC_REG_INTERRUPT 0x004
+#define RKVDEC_INTERRUPT_DEC_E BIT(0)
+#define RKVDEC_CONFIG_DEC_CLK_GATE_E BIT(1)
+#define RKVDEC_E_STRMD_CLKGATE_DIS BIT(2)
+#define RKVDEC_TIMEOUT_MODE BIT(3)
+#define RKVDEC_IRQ_DIS BIT(4)
+#define RKVDEC_TIMEOUT_E BIT(5)
+#define RKVDEC_BUF_EMPTY_E BIT(6)
+#define RKVDEC_STRM_E_WAITDECFIFO_EMPTY BIT(7)
+#define RKVDEC_IRQ BIT(8)
+#define RKVDEC_IRQ_RAW BIT(9)
+#define RKVDEC_E_REWRITE_VALID BIT(10)
+#define RKVDEC_COMMONIRQ_MODE BIT(11)
+#define RKVDEC_RDY_STA BIT(12)
+#define RKVDEC_BUS_STA BIT(13)
+#define RKVDEC_ERR_STA BIT(14)
+#define RKVDEC_TIMEOUT_STA BIT(15)
+#define RKVDEC_BUF_EMPTY_STA BIT(16)
+#define RKVDEC_COLMV_REF_ERR_STA BIT(17)
+#define RKVDEC_CABU_END_STA BIT(18)
+#define RKVDEC_H264ORVP9_ERR_MODE BIT(19)
+#define RKVDEC_SOFTRST_EN_P BIT(20)
+#define RKVDEC_FORCE_SOFTRESET_VALID BIT(21)
+#define RKVDEC_SOFTRESET_RDY BIT(22)
+
+#define RKVDEC_REG_SYSCTRL 0x008
+#define RKVDEC_IN_ENDIAN BIT(0)
+#define RKVDEC_IN_SWAP32_E BIT(1)
+#define RKVDEC_IN_SWAP64_E BIT(2)
+#define RKVDEC_STR_ENDIAN BIT(3)
+#define RKVDEC_STR_SWAP32_E BIT(4)
+#define RKVDEC_STR_SWAP64_E BIT(5)
+#define RKVDEC_OUT_ENDIAN BIT(6)
+#define RKVDEC_OUT_SWAP32_E BIT(7)
+#define RKVDEC_OUT_CBCR_SWAP BIT(8)
+#define RKVDEC_RLC_MODE_DIRECT_WRITE BIT(10)
+#define RKVDEC_RLC_MODE BIT(11)
+#define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12)
+#define RKVDEC_MODE(x) (((x) & 0x03) << 20)
+#define RKVDEC_MODE_H264 1
+#define RKVDEC_MODE_VP9 2
+#define RKVDEC_RPS_MODE BIT(24)
+#define RKVDEC_STRM_MODE BIT(25)
+#define RKVDEC_H264_STRM_LASTPKT BIT(26)
+#define RKVDEC_H264_FIRSTSLICE_FLAG BIT(27)
+#define RKVDEC_H264_FRAME_ORSLICE BIT(28)
+#define RKVDEC_BUSPR_SLOT_DIS BIT(29)
+
+#define RKVDEC_REG_PICPAR 0x00C
+#define RKVDEC_Y_HOR_VIRSTRIDE(x) ((x) & 0x1ff)
+#define RKVDEC_SLICE_NUM_HIGHBIT BIT(11)
+#define RKVDEC_UV_HOR_VIRSTRIDE(x) (((x) & 0x1ff) << 12)
+#define RKVDEC_SLICE_NUM_LOWBITS(x) (((x) & 0x7ff) << 21)
+
+#define RKVDEC_REG_STRM_RLC_BASE 0x010
+
+#define RKVDEC_REG_STRM_LEN 0x014
+#define RKVDEC_STRM_LEN(x) ((x) & 0x7ffffff)
+
+#define RKVDEC_REG_CABACTBL_PROB_BASE 0x018
+#define RKVDEC_REG_DECOUT_BASE 0x01C
+
+#define RKVDEC_REG_Y_VIRSTRIDE 0x020
+#define RKVDEC_Y_VIRSTRIDE(x) ((x) & 0xfffff)
+
+#define RKVDEC_REG_YUV_VIRSTRIDE 0x024
+#define RKVDEC_YUV_VIRSTRIDE(x) ((x) & 0x1fffff)
+#define RKVDEC_REG_H264_BASE_REFER(i) (((i) * 0x04) + 0x028)
+
+#define RKVDEC_REG_H264_BASE_REFER15 0x0C0
+#define RKVDEC_FIELD_REF BIT(0)
+#define RKVDEC_TOPFIELD_USED_REF BIT(1)
+#define RKVDEC_BOTFIELD_USED_REF BIT(2)
+#define RKVDEC_COLMV_USED_FLAG_REF BIT(3)
+
+#define RKVDEC_REG_VP9_LAST_FRAME_BASE 0x02c
+#define RKVDEC_REG_VP9_GOLDEN_FRAME_BASE 0x030
+#define RKVDEC_REG_VP9_ALTREF_FRAME_BASE 0x034
+
+#define RKVDEC_REG_VP9_CPRHEADER_OFFSET 0x028
+#define RKVDEC_VP9_CPRHEADER_OFFSET(x) ((x) & 0xffff)
+
+#define RKVDEC_REG_VP9_REFERLAST_BASE 0x02C
+#define RKVDEC_REG_VP9_REFERGOLDEN_BASE 0x030
+#define RKVDEC_REG_VP9_REFERALFTER_BASE 0x034
+
+#define RKVDEC_REG_VP9COUNT_BASE 0x038
+#define RKVDEC_VP9COUNT_UPDATE_EN BIT(0)
+
+#define RKVDEC_REG_VP9_SEGIDLAST_BASE 0x03C
+#define RKVDEC_REG_VP9_SEGIDCUR_BASE 0x040
+#define RKVDEC_REG_VP9_FRAME_SIZE(i) ((i) * 0x04 + 0x044)
+#define RKVDEC_VP9_FRAMEWIDTH(x) (((x) & 0xffff) << 0)
+#define RKVDEC_VP9_FRAMEHEIGHT(x) (((x) & 0xffff) << 16)
+
+#define RKVDEC_VP9_SEGID_GRP(i) ((i) * 0x04 + 0x050)
+#define RKVDEC_SEGID_ABS_DELTA(x) ((x) & 0x1)
+#define RKVDEC_SEGID_FRAME_QP_DELTA_EN(x) (((x) & 0x1) << 1)
+#define RKVDEC_SEGID_FRAME_QP_DELTA(x) (((x) & 0x1ff) << 2)
+#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(x) (((x) & 0x1) << 11)
+#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(x) (((x) & 0x7f) << 12)
+#define RKVDEC_SEGID_REFERINFO_EN(x) (((x) & 0x1) << 19)
+#define RKVDEC_SEGID_REFERINFO(x) (((x) & 0x03) << 20)
+#define RKVDEC_SEGID_FRAME_SKIP_EN(x) (((x) & 0x1) << 22)
+
+#define RKVDEC_VP9_CPRHEADER_CONFIG 0x070
+#define RKVDEC_VP9_TX_MODE(x) ((x) & 0x07)
+#define RKVDEC_VP9_FRAME_REF_MODE(x) (((x) & 0x03) << 3)
+
+#define RKVDEC_VP9_REF_SCALE(i) ((i) * 0x04 + 0x074)
+#define RKVDEC_VP9_REF_HOR_SCALE(x) ((x) & 0xffff)
+#define RKVDEC_VP9_REF_VER_SCALE(x) (((x) & 0xffff) << 16)
+
+#define RKVDEC_VP9_REF_DELTAS_LASTFRAME 0x080
+#define RKVDEC_REF_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7))
+
+#define RKVDEC_VP9_INFO_LASTFRAME 0x084
+#define RKVDEC_MODE_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7))
+#define RKVDEC_SEG_EN_LASTFRAME BIT(16)
+#define RKVDEC_LAST_SHOW_FRAME BIT(17)
+#define RKVDEC_LAST_INTRA_ONLY BIT(18)
+#define RKVDEC_LAST_WIDHHEIGHT_EQCUR BIT(19)
+#define RKVDEC_COLOR_SPACE_LASTKEYFRAME(x) (((x) & 0x07) << 20)
+
+#define RKVDEC_VP9_INTERCMD_BASE 0x088
+
+#define RKVDEC_VP9_INTERCMD_NUM 0x08C
+#define RKVDEC_INTERCMD_NUM(x) ((x) & 0xffffff)
+
+#define RKVDEC_VP9_LASTTILE_SIZE 0x090
+#define RKVDEC_LASTTILE_SIZE(x) ((x) & 0xffffff)
+
+#define RKVDEC_VP9_HOR_VIRSTRIDE(i) ((i) * 0x04 + 0x094)
+#define RKVDEC_HOR_Y_VIRSTRIDE(x) ((x) & 0x1ff)
+#define RKVDEC_HOR_UV_VIRSTRIDE(x) (((x) & 0x1ff) << 16)
+
+#define RKVDEC_REG_H264_POC_REFER0(i) (((i) * 0x04) + 0x064)
+#define RKVDEC_REG_H264_POC_REFER1(i) (((i) * 0x04) + 0x0C4)
+#define RKVDEC_REG_H264_POC_REFER2(i) (((i) * 0x04) + 0x120)
+#define RKVDEC_POC_REFER(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_CUR_POC0 0x0A0
+#define RKVDEC_REG_CUR_POC1 0x128
+#define RKVDEC_CUR_POC(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_RLCWRITE_BASE 0x0A4
+#define RKVDEC_REG_PPS_BASE 0x0A8
+#define RKVDEC_REG_RPS_BASE 0x0AC
+
+#define RKVDEC_REG_STRMD_ERR_EN 0x0B0
+#define RKVDEC_STRMD_ERR_EN(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_STRMD_ERR_STA 0x0B4
+#define RKVDEC_STRMD_ERR_STA(x) ((x) & 0xfffffff)
+#define RKVDEC_COLMV_ERR_REF_PICIDX(x) (((x) & 0x0f) << 28)
+
+#define RKVDEC_REG_STRMD_ERR_CTU 0x0B8
+#define RKVDEC_STRMD_ERR_CTU(x) ((x) & 0xff)
+#define RKVDEC_STRMD_ERR_CTU_YOFFSET(x) (((x) & 0xff) << 8)
+#define RKVDEC_STRMFIFO_SPACE2FULL(x) (((x) & 0x7f) << 16)
+#define RKVDEC_VP9_ERR_EN_CTU0 BIT(24)
+
+#define RKVDEC_REG_SAO_CTU_POS 0x0BC
+#define RKVDEC_SAOWR_XOFFSET(x) ((x) & 0x1ff)
+#define RKVDEC_SAOWR_YOFFSET(x) (((x) & 0x3ff) << 16)
+
+#define RKVDEC_VP9_LAST_FRAME_YSTRIDE 0x0C0
+#define RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE 0x0C4
+#define RKVDEC_VP9_ALTREF_FRAME_YSTRIDE 0x0C8
+#define RKVDEC_VP9_REF_YSTRIDE(x) (((x) & 0xfffff) << 0)
+
+#define RKVDEC_VP9_LAST_FRAME_YUVSTRIDE 0x0CC
+#define RKVDEC_VP9_REF_YUVSTRIDE(x) (((x) & 0x1fffff) << 0)
+
+#define RKVDEC_VP9_REF_COLMV_BASE 0x0D0
+
+#define RKVDEC_REG_PERFORMANCE_CYCLE 0x100
+#define RKVDEC_PERFORMANCE_CYCLE(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_AXI_DDR_RDATA 0x104
+#define RKVDEC_AXI_DDR_RDATA(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_AXI_DDR_WDATA 0x108
+#define RKVDEC_AXI_DDR_WDATA(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_FPGADEBUG_RESET 0x10C
+#define RKVDEC_BUSIFD_RESETN BIT(0)
+#define RKVDEC_CABAC_RESETN BIT(1)
+#define RKVDEC_DEC_CTRL_RESETN BIT(2)
+#define RKVDEC_TRANSD_RESETN BIT(3)
+#define RKVDEC_INTRA_RESETN BIT(4)
+#define RKVDEC_INTER_RESETN BIT(5)
+#define RKVDEC_RECON_RESETN BIT(6)
+#define RKVDEC_FILER_RESETN BIT(7)
+
+#define RKVDEC_REG_PERFORMANCE_SEL 0x110
+#define RKVDEC_PERF_SEL_CNT0(x) ((x) & 0x3f)
+#define RKVDEC_PERF_SEL_CNT1(x) (((x) & 0x3f) << 8)
+#define RKVDEC_PERF_SEL_CNT2(x) (((x) & 0x3f) << 16)
+
+#define RKVDEC_REG_PERFORMANCE_CNT(i) ((i) * 0x04 + 0x114)
+#define RKVDEC_PERF_CNT(x) ((x) & 0xffffffff)
+
+#define RKVDEC_REG_H264_ERRINFO_BASE 0x12C
+
+#define RKVDEC_REG_H264_ERRINFO_NUM 0x130
+#define RKVDEC_SLICEDEC_NUM(x) ((x) & 0x3fff)
+#define RKVDEC_STRMD_DECT_ERR_FLAG BIT(15)
+#define RKVDEC_ERR_PKT_NUM(x) (((x) & 0x3fff) << 16)
+
+#define RKVDEC_REG_H264_ERR_E 0x134
+#define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff)
+
+#define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410
+#define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450
+
+#endif /* RKVDEC_REGS_H_ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
new file mode 100644
index 000000000000..0e7e16f20eeb
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Video Decoder VP9 backend
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ * Copyright (C) 2021 Collabora, Ltd.
+ * Andrzej Pietrasiewicz <andrzej.p@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Alpha Lin <Alpha.Lin@rock-chips.com>
+ */
+
+/*
+ * For following the vp9 spec please start reading this driver
+ * code from rkvdec_vp9_run() followed by rkvdec_vp9_done().
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-vp9.h>
+
+#include "rkvdec.h"
+#include "rkvdec-regs.h"
+
+#define RKVDEC_VP9_PROBE_SIZE 4864
+#define RKVDEC_VP9_COUNT_SIZE 13232
+#define RKVDEC_VP9_MAX_SEGMAP_SIZE 73728
+
+struct rkvdec_vp9_intra_mode_probs {
+ u8 y_mode[105];
+ u8 uv_mode[23];
+};
+
+struct rkvdec_vp9_intra_only_frame_probs {
+ u8 coef_intra[4][2][128];
+ struct rkvdec_vp9_intra_mode_probs intra_mode[10];
+};
+
+struct rkvdec_vp9_inter_frame_probs {
+ u8 y_mode[4][9];
+ u8 comp_mode[5];
+ u8 comp_ref[5];
+ u8 single_ref[5][2];
+ u8 inter_mode[7][3];
+ u8 interp_filter[4][2];
+ u8 padding0[11];
+ u8 coef[2][4][2][128];
+ u8 uv_mode_0_2[3][9];
+ u8 padding1[5];
+ u8 uv_mode_3_5[3][9];
+ u8 padding2[5];
+ u8 uv_mode_6_8[3][9];
+ u8 padding3[5];
+ u8 uv_mode_9[9];
+ u8 padding4[7];
+ u8 padding5[16];
+ struct {
+ u8 joint[3];
+ u8 sign[2];
+ u8 classes[2][10];
+ u8 class0_bit[2];
+ u8 bits[2][10];
+ u8 class0_fr[2][2][3];
+ u8 fr[2][3];
+ u8 class0_hp[2];
+ u8 hp[2];
+ } mv;
+};
+
+struct rkvdec_vp9_probs {
+ u8 partition[16][3];
+ u8 pred[3];
+ u8 tree[7];
+ u8 skip[3];
+ u8 tx32[2][3];
+ u8 tx16[2][2];
+ u8 tx8[2][1];
+ u8 is_inter[4];
+ /* 128 bit alignment */
+ u8 padding0[3];
+ union {
+ struct rkvdec_vp9_inter_frame_probs inter;
+ struct rkvdec_vp9_intra_only_frame_probs intra_only;
+ };
+ /* 128 bit alignment */
+ u8 padding1[11];
+};
+
+/* Data structure describing auxiliary buffer format. */
+struct rkvdec_vp9_priv_tbl {
+ struct rkvdec_vp9_probs probs;
+ u8 segmap[2][RKVDEC_VP9_MAX_SEGMAP_SIZE];
+};
+
+struct rkvdec_vp9_refs_counts {
+ u32 eob[2];
+ u32 coeff[3];
+};
+
+struct rkvdec_vp9_inter_frame_symbol_counts {
+ u32 partition[16][4];
+ u32 skip[3][2];
+ u32 inter[4][2];
+ u32 tx32p[2][4];
+ u32 tx16p[2][4];
+ u32 tx8p[2][2];
+ u32 y_mode[4][10];
+ u32 uv_mode[10][10];
+ u32 comp[5][2];
+ u32 comp_ref[5][2];
+ u32 single_ref[5][2][2];
+ u32 mv_mode[7][4];
+ u32 filter[4][3];
+ u32 mv_joint[4];
+ u32 sign[2][2];
+ /* add 1 element for align */
+ u32 classes[2][11 + 1];
+ u32 class0[2][2];
+ u32 bits[2][10][2];
+ u32 class0_fp[2][2][4];
+ u32 fp[2][4];
+ u32 class0_hp[2][2];
+ u32 hp[2][2];
+ struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6];
+};
+
+struct rkvdec_vp9_intra_frame_symbol_counts {
+ u32 partition[4][4][4];
+ u32 skip[3][2];
+ u32 intra[4][2];
+ u32 tx32p[2][4];
+ u32 tx16p[2][4];
+ u32 tx8p[2][2];
+ struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6];
+};
+
+struct rkvdec_vp9_run {
+ struct rkvdec_run base;
+ const struct v4l2_ctrl_vp9_frame *decode_params;
+};
+
+struct rkvdec_vp9_frame_info {
+ u32 valid : 1;
+ u32 segmapid : 1;
+ u32 frame_context_idx : 2;
+ u32 reference_mode : 2;
+ u32 tx_mode : 3;
+ u32 interpolation_filter : 3;
+ u32 flags;
+ u64 timestamp;
+ struct v4l2_vp9_segmentation seg;
+ struct v4l2_vp9_loop_filter lf;
+};
+
+struct rkvdec_vp9_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct rkvdec_aux_buf count_tbl;
+ struct v4l2_vp9_frame_symbol_counts inter_cnts;
+ struct v4l2_vp9_frame_symbol_counts intra_cnts;
+ struct v4l2_vp9_frame_context probability_tables;
+ struct v4l2_vp9_frame_context frame_context[4];
+ struct rkvdec_vp9_frame_info cur;
+ struct rkvdec_vp9_frame_info last;
+};
+
+static void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane)
+{
+ unsigned int idx = 0, byte_count = 0;
+ int k, m, n;
+ u8 p;
+
+ for (k = 0; k < 6; k++) {
+ for (m = 0; m < 6; m++) {
+ for (n = 0; n < 3; n++) {
+ p = coef[k][m][n];
+ coeff_plane[idx++] = p;
+ byte_count++;
+ if (byte_count == 27) {
+ idx += 5;
+ byte_count = 0;
+ }
+ }
+ }
+ }
+}
+
+static void init_intra_only_probs(struct rkvdec_ctx *ctx,
+ const struct rkvdec_vp9_run *run)
+{
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu;
+ struct rkvdec_vp9_intra_only_frame_probs *rkprobs;
+ const struct v4l2_vp9_frame_context *probs;
+ unsigned int i, j, k;
+
+ rkprobs = &tbl->probs.intra_only;
+ probs = &vp9_ctx->probability_tables;
+
+ /*
+ * intra only 149 x 128 bits ,aligned to 152 x 128 bits coeff related
+ * prob 64 x 128 bits
+ */
+ for (i = 0; i < ARRAY_SIZE(probs->coef); i++) {
+ for (j = 0; j < ARRAY_SIZE(probs->coef[0]); j++)
+ write_coeff_plane(probs->coef[i][j][0],
+ rkprobs->coef_intra[i][j]);
+ }
+
+ /* intra mode prob 80 x 128 bits */
+ for (i = 0; i < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob); i++) {
+ unsigned int byte_count = 0;
+ int idx = 0;
+
+ /* vp9_kf_y_mode_prob */
+ for (j = 0; j < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob[0]); j++) {
+ for (k = 0; k < ARRAY_SIZE(v4l2_vp9_kf_y_mode_prob[0][0]);
+ k++) {
+ u8 val = v4l2_vp9_kf_y_mode_prob[i][j][k];
+
+ rkprobs->intra_mode[i].y_mode[idx++] = val;
+ byte_count++;
+ if (byte_count == 27) {
+ byte_count = 0;
+ idx += 5;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < sizeof(v4l2_vp9_kf_uv_mode_prob); ++i) {
+ const u8 *ptr = (const u8 *)v4l2_vp9_kf_uv_mode_prob;
+
+ rkprobs->intra_mode[i / 23].uv_mode[i % 23] = ptr[i];
+ }
+}
+
+static void init_inter_probs(struct rkvdec_ctx *ctx,
+ const struct rkvdec_vp9_run *run)
+{
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu;
+ struct rkvdec_vp9_inter_frame_probs *rkprobs;
+ const struct v4l2_vp9_frame_context *probs;
+ unsigned int i, j, k;
+
+ rkprobs = &tbl->probs.inter;
+ probs = &vp9_ctx->probability_tables;
+
+ /*
+ * inter probs
+ * 151 x 128 bits, aligned to 152 x 128 bits
+ * inter only
+ * intra_y_mode & inter_block info 6 x 128 bits
+ */
+
+ memcpy(rkprobs->y_mode, probs->y_mode, sizeof(rkprobs->y_mode));
+ memcpy(rkprobs->comp_mode, probs->comp_mode,
+ sizeof(rkprobs->comp_mode));
+ memcpy(rkprobs->comp_ref, probs->comp_ref,
+ sizeof(rkprobs->comp_ref));
+ memcpy(rkprobs->single_ref, probs->single_ref,
+ sizeof(rkprobs->single_ref));
+ memcpy(rkprobs->inter_mode, probs->inter_mode,
+ sizeof(rkprobs->inter_mode));
+ memcpy(rkprobs->interp_filter, probs->interp_filter,
+ sizeof(rkprobs->interp_filter));
+
+ /* 128 x 128 bits coeff related */
+ for (i = 0; i < ARRAY_SIZE(probs->coef); i++) {
+ for (j = 0; j < ARRAY_SIZE(probs->coef[0]); j++) {
+ for (k = 0; k < ARRAY_SIZE(probs->coef[0][0]); k++)
+ write_coeff_plane(probs->coef[i][j][k],
+ rkprobs->coef[k][i][j]);
+ }
+ }
+
+ /* intra uv mode 6 x 128 */
+ memcpy(rkprobs->uv_mode_0_2, &probs->uv_mode[0],
+ sizeof(rkprobs->uv_mode_0_2));
+ memcpy(rkprobs->uv_mode_3_5, &probs->uv_mode[3],
+ sizeof(rkprobs->uv_mode_3_5));
+ memcpy(rkprobs->uv_mode_6_8, &probs->uv_mode[6],
+ sizeof(rkprobs->uv_mode_6_8));
+ memcpy(rkprobs->uv_mode_9, &probs->uv_mode[9],
+ sizeof(rkprobs->uv_mode_9));
+
+ /* mv related 6 x 128 */
+ memcpy(rkprobs->mv.joint, probs->mv.joint,
+ sizeof(rkprobs->mv.joint));
+ memcpy(rkprobs->mv.sign, probs->mv.sign,
+ sizeof(rkprobs->mv.sign));
+ memcpy(rkprobs->mv.classes, probs->mv.classes,
+ sizeof(rkprobs->mv.classes));
+ memcpy(rkprobs->mv.class0_bit, probs->mv.class0_bit,
+ sizeof(rkprobs->mv.class0_bit));
+ memcpy(rkprobs->mv.bits, probs->mv.bits,
+ sizeof(rkprobs->mv.bits));
+ memcpy(rkprobs->mv.class0_fr, probs->mv.class0_fr,
+ sizeof(rkprobs->mv.class0_fr));
+ memcpy(rkprobs->mv.fr, probs->mv.fr,
+ sizeof(rkprobs->mv.fr));
+ memcpy(rkprobs->mv.class0_hp, probs->mv.class0_hp,
+ sizeof(rkprobs->mv.class0_hp));
+ memcpy(rkprobs->mv.hp, probs->mv.hp,
+ sizeof(rkprobs->mv.hp));
+}
+
+static void init_probs(struct rkvdec_ctx *ctx,
+ const struct rkvdec_vp9_run *run)
+{
+ const struct v4l2_ctrl_vp9_frame *dec_params;
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_vp9_priv_tbl *tbl = vp9_ctx->priv_tbl.cpu;
+ struct rkvdec_vp9_probs *rkprobs = &tbl->probs;
+ const struct v4l2_vp9_segmentation *seg;
+ const struct v4l2_vp9_frame_context *probs;
+ bool intra_only;
+
+ dec_params = run->decode_params;
+ probs = &vp9_ctx->probability_tables;
+ seg = &dec_params->seg;
+
+ memset(rkprobs, 0, sizeof(*rkprobs));
+
+ intra_only = !!(dec_params->flags &
+ (V4L2_VP9_FRAME_FLAG_KEY_FRAME |
+ V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
+
+ /* sb info 5 x 128 bit */
+ memcpy(rkprobs->partition,
+ intra_only ? v4l2_vp9_kf_partition_probs : probs->partition,
+ sizeof(rkprobs->partition));
+
+ memcpy(rkprobs->pred, seg->pred_probs, sizeof(rkprobs->pred));
+ memcpy(rkprobs->tree, seg->tree_probs, sizeof(rkprobs->tree));
+ memcpy(rkprobs->skip, probs->skip, sizeof(rkprobs->skip));
+ memcpy(rkprobs->tx32, probs->tx32, sizeof(rkprobs->tx32));
+ memcpy(rkprobs->tx16, probs->tx16, sizeof(rkprobs->tx16));
+ memcpy(rkprobs->tx8, probs->tx8, sizeof(rkprobs->tx8));
+ memcpy(rkprobs->is_inter, probs->is_inter, sizeof(rkprobs->is_inter));
+
+ if (intra_only)
+ init_intra_only_probs(ctx, run);
+ else
+ init_inter_probs(ctx, run);
+}
+
+struct rkvdec_vp9_ref_reg {
+ u32 reg_frm_size;
+ u32 reg_hor_stride;
+ u32 reg_y_stride;
+ u32 reg_yuv_stride;
+ u32 reg_ref_base;
+};
+
+static struct rkvdec_vp9_ref_reg ref_regs[] = {
+ {
+ .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(0),
+ .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(0),
+ .reg_y_stride = RKVDEC_VP9_LAST_FRAME_YSTRIDE,
+ .reg_yuv_stride = RKVDEC_VP9_LAST_FRAME_YUVSTRIDE,
+ .reg_ref_base = RKVDEC_REG_VP9_LAST_FRAME_BASE,
+ },
+ {
+ .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(1),
+ .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(1),
+ .reg_y_stride = RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE,
+ .reg_yuv_stride = 0,
+ .reg_ref_base = RKVDEC_REG_VP9_GOLDEN_FRAME_BASE,
+ },
+ {
+ .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(2),
+ .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(2),
+ .reg_y_stride = RKVDEC_VP9_ALTREF_FRAME_YSTRIDE,
+ .reg_yuv_stride = 0,
+ .reg_ref_base = RKVDEC_REG_VP9_ALTREF_FRAME_BASE,
+ }
+};
+
+static struct rkvdec_decoded_buffer *
+get_ref_buf(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
+ struct vb2_buffer *buf;
+
+ /*
+ * If a ref is unused or invalid, address of current destination
+ * buffer is returned.
+ */
+ buf = vb2_find_buffer(cap_q, timestamp);
+ if (!buf)
+ buf = &dst->vb2_buf;
+
+ return vb2_to_rkvdec_decoded_buf(buf);
+}
+
+static dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf)
+{
+ unsigned int aligned_pitch, aligned_height, yuv_len;
+
+ aligned_height = round_up(buf->vp9.height, 64);
+ aligned_pitch = round_up(buf->vp9.width * buf->vp9.bit_depth, 512) / 8;
+ yuv_len = (aligned_height * aligned_pitch * 3) / 2;
+
+ return vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 0) +
+ yuv_len;
+}
+
+static void config_ref_registers(struct rkvdec_ctx *ctx,
+ const struct rkvdec_vp9_run *run,
+ struct rkvdec_decoded_buffer *ref_buf,
+ struct rkvdec_vp9_ref_reg *ref_reg)
+{
+ unsigned int aligned_pitch, aligned_height, y_len, yuv_len;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ aligned_height = round_up(ref_buf->vp9.height, 64);
+ writel_relaxed(RKVDEC_VP9_FRAMEWIDTH(ref_buf->vp9.width) |
+ RKVDEC_VP9_FRAMEHEIGHT(ref_buf->vp9.height),
+ rkvdec->regs + ref_reg->reg_frm_size);
+
+ writel_relaxed(vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0),
+ rkvdec->regs + ref_reg->reg_ref_base);
+
+ if (&ref_buf->base.vb == run->base.bufs.dst)
+ return;
+
+ aligned_pitch = round_up(ref_buf->vp9.width * ref_buf->vp9.bit_depth, 512) / 8;
+ y_len = aligned_height * aligned_pitch;
+ yuv_len = (y_len * 3) / 2;
+
+ writel_relaxed(RKVDEC_HOR_Y_VIRSTRIDE(aligned_pitch / 16) |
+ RKVDEC_HOR_UV_VIRSTRIDE(aligned_pitch / 16),
+ rkvdec->regs + ref_reg->reg_hor_stride);
+ writel_relaxed(RKVDEC_VP9_REF_YSTRIDE(y_len / 16),
+ rkvdec->regs + ref_reg->reg_y_stride);
+
+ if (!ref_reg->reg_yuv_stride)
+ return;
+
+ writel_relaxed(RKVDEC_VP9_REF_YUVSTRIDE(yuv_len / 16),
+ rkvdec->regs + ref_reg->reg_yuv_stride);
+}
+
+static void config_seg_registers(struct rkvdec_ctx *ctx, unsigned int segid)
+{
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ const struct v4l2_vp9_segmentation *seg;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ s16 feature_val;
+ int feature_id;
+ u32 val = 0;
+
+ seg = vp9_ctx->last.valid ? &vp9_ctx->last.seg : &vp9_ctx->cur.seg;
+ feature_id = V4L2_VP9_SEG_LVL_ALT_Q;
+ if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) {
+ feature_val = seg->feature_data[segid][feature_id];
+ val |= RKVDEC_SEGID_FRAME_QP_DELTA_EN(1) |
+ RKVDEC_SEGID_FRAME_QP_DELTA(feature_val);
+ }
+
+ feature_id = V4L2_VP9_SEG_LVL_ALT_L;
+ if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) {
+ feature_val = seg->feature_data[segid][feature_id];
+ val |= RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(1) |
+ RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(feature_val);
+ }
+
+ feature_id = V4L2_VP9_SEG_LVL_REF_FRAME;
+ if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) {
+ feature_val = seg->feature_data[segid][feature_id];
+ val |= RKVDEC_SEGID_REFERINFO_EN(1) |
+ RKVDEC_SEGID_REFERINFO(feature_val);
+ }
+
+ feature_id = V4L2_VP9_SEG_LVL_SKIP;
+ if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid))
+ val |= RKVDEC_SEGID_FRAME_SKIP_EN(1);
+
+ if (!segid &&
+ (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE))
+ val |= RKVDEC_SEGID_ABS_DELTA(1);
+
+ writel_relaxed(val, rkvdec->regs + RKVDEC_VP9_SEGID_GRP(segid));
+}
+
+static void update_dec_buf_info(struct rkvdec_decoded_buffer *buf,
+ const struct v4l2_ctrl_vp9_frame *dec_params)
+{
+ buf->vp9.width = dec_params->frame_width_minus_1 + 1;
+ buf->vp9.height = dec_params->frame_height_minus_1 + 1;
+ buf->vp9.bit_depth = dec_params->bit_depth;
+}
+
+static void update_ctx_cur_info(struct rkvdec_vp9_ctx *vp9_ctx,
+ struct rkvdec_decoded_buffer *buf,
+ const struct v4l2_ctrl_vp9_frame *dec_params)
+{
+ vp9_ctx->cur.valid = true;
+ vp9_ctx->cur.reference_mode = dec_params->reference_mode;
+ vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter;
+ vp9_ctx->cur.flags = dec_params->flags;
+ vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp;
+ vp9_ctx->cur.seg = dec_params->seg;
+ vp9_ctx->cur.lf = dec_params->lf;
+}
+
+static void update_ctx_last_info(struct rkvdec_vp9_ctx *vp9_ctx)
+{
+ vp9_ctx->last = vp9_ctx->cur;
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ const struct rkvdec_vp9_run *run)
+{
+ unsigned int y_len, uv_len, yuv_len, bit_depth, aligned_height, aligned_pitch, stream_len;
+ const struct v4l2_ctrl_vp9_frame *dec_params;
+ struct rkvdec_decoded_buffer *ref_bufs[3];
+ struct rkvdec_decoded_buffer *dst, *last, *mv_ref;
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ u32 val, last_frame_info = 0;
+ const struct v4l2_vp9_segmentation *seg;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ dma_addr_t addr;
+ bool intra_only;
+ unsigned int i;
+
+ dec_params = run->decode_params;
+ dst = vb2_to_rkvdec_decoded_buf(&run->base.bufs.dst->vb2_buf);
+ ref_bufs[0] = get_ref_buf(ctx, &dst->base.vb, dec_params->last_frame_ts);
+ ref_bufs[1] = get_ref_buf(ctx, &dst->base.vb, dec_params->golden_frame_ts);
+ ref_bufs[2] = get_ref_buf(ctx, &dst->base.vb, dec_params->alt_frame_ts);
+
+ if (vp9_ctx->last.valid)
+ last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp);
+ else
+ last = dst;
+
+ update_dec_buf_info(dst, dec_params);
+ update_ctx_cur_info(vp9_ctx, dst, dec_params);
+ seg = &dec_params->seg;
+
+ intra_only = !!(dec_params->flags &
+ (V4L2_VP9_FRAME_FLAG_KEY_FRAME |
+ V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
+
+ writel_relaxed(RKVDEC_MODE(RKVDEC_MODE_VP9),
+ rkvdec->regs + RKVDEC_REG_SYSCTRL);
+
+ bit_depth = dec_params->bit_depth;
+ aligned_height = round_up(ctx->decoded_fmt.fmt.pix_mp.height, 64);
+
+ aligned_pitch = round_up(ctx->decoded_fmt.fmt.pix_mp.width *
+ bit_depth,
+ 512) / 8;
+ y_len = aligned_height * aligned_pitch;
+ uv_len = y_len / 2;
+ yuv_len = y_len + uv_len;
+
+ writel_relaxed(RKVDEC_Y_HOR_VIRSTRIDE(aligned_pitch / 16) |
+ RKVDEC_UV_HOR_VIRSTRIDE(aligned_pitch / 16),
+ rkvdec->regs + RKVDEC_REG_PICPAR);
+ writel_relaxed(RKVDEC_Y_VIRSTRIDE(y_len / 16),
+ rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE);
+ writel_relaxed(RKVDEC_YUV_VIRSTRIDE(yuv_len / 16),
+ rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE);
+
+ stream_len = vb2_get_plane_payload(&run->base.bufs.src->vb2_buf, 0);
+ writel_relaxed(RKVDEC_STRM_LEN(stream_len),
+ rkvdec->regs + RKVDEC_REG_STRM_LEN);
+
+ /*
+ * Reset count buffer, because decoder only output intra related syntax
+ * counts when decoding intra frame, but update entropy need to update
+ * all the probabilities.
+ */
+ if (intra_only)
+ memset(vp9_ctx->count_tbl.cpu, 0, vp9_ctx->count_tbl.size);
+
+ vp9_ctx->cur.segmapid = vp9_ctx->last.segmapid;
+ if (!intra_only &&
+ !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
+ (!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) ||
+ (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP)))
+ vp9_ctx->cur.segmapid++;
+
+ for (i = 0; i < ARRAY_SIZE(ref_bufs); i++)
+ config_ref_registers(ctx, run, ref_bufs[i], &ref_regs[i]);
+
+ for (i = 0; i < 8; i++)
+ config_seg_registers(ctx, i);
+
+ writel_relaxed(RKVDEC_VP9_TX_MODE(vp9_ctx->cur.tx_mode) |
+ RKVDEC_VP9_FRAME_REF_MODE(dec_params->reference_mode),
+ rkvdec->regs + RKVDEC_VP9_CPRHEADER_CONFIG);
+
+ if (!intra_only) {
+ const struct v4l2_vp9_loop_filter *lf;
+ s8 delta;
+
+ if (vp9_ctx->last.valid)
+ lf = &vp9_ctx->last.lf;
+ else
+ lf = &vp9_ctx->cur.lf;
+
+ val = 0;
+ for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) {
+ delta = lf->ref_deltas[i];
+ val |= RKVDEC_REF_DELTAS_LASTFRAME(i, delta);
+ }
+
+ writel_relaxed(val,
+ rkvdec->regs + RKVDEC_VP9_REF_DELTAS_LASTFRAME);
+
+ for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) {
+ delta = lf->mode_deltas[i];
+ last_frame_info |= RKVDEC_MODE_DELTAS_LASTFRAME(i,
+ delta);
+ }
+ }
+
+ if (vp9_ctx->last.valid && !intra_only &&
+ vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED)
+ last_frame_info |= RKVDEC_SEG_EN_LASTFRAME;
+
+ if (vp9_ctx->last.valid &&
+ vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME)
+ last_frame_info |= RKVDEC_LAST_SHOW_FRAME;
+
+ if (vp9_ctx->last.valid &&
+ vp9_ctx->last.flags &
+ (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY))
+ last_frame_info |= RKVDEC_LAST_INTRA_ONLY;
+
+ if (vp9_ctx->last.valid &&
+ last->vp9.width == dst->vp9.width &&
+ last->vp9.height == dst->vp9.height)
+ last_frame_info |= RKVDEC_LAST_WIDHHEIGHT_EQCUR;
+
+ writel_relaxed(last_frame_info,
+ rkvdec->regs + RKVDEC_VP9_INFO_LASTFRAME);
+
+ writel_relaxed(stream_len - dec_params->compressed_header_size -
+ dec_params->uncompressed_header_size,
+ rkvdec->regs + RKVDEC_VP9_LASTTILE_SIZE);
+
+ for (i = 0; !intra_only && i < ARRAY_SIZE(ref_bufs); i++) {
+ unsigned int refw = ref_bufs[i]->vp9.width;
+ unsigned int refh = ref_bufs[i]->vp9.height;
+ u32 hscale, vscale;
+
+ hscale = (refw << 14) / dst->vp9.width;
+ vscale = (refh << 14) / dst->vp9.height;
+ writel_relaxed(RKVDEC_VP9_REF_HOR_SCALE(hscale) |
+ RKVDEC_VP9_REF_VER_SCALE(vscale),
+ rkvdec->regs + RKVDEC_VP9_REF_SCALE(i));
+ }
+
+ addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 0);
+ writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE);
+ addr = vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0);
+ writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE);
+ writel_relaxed(vp9_ctx->priv_tbl.dma +
+ offsetof(struct rkvdec_vp9_priv_tbl, probs),
+ rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE);
+ writel_relaxed(vp9_ctx->count_tbl.dma,
+ rkvdec->regs + RKVDEC_REG_VP9COUNT_BASE);
+
+ writel_relaxed(vp9_ctx->priv_tbl.dma +
+ offsetof(struct rkvdec_vp9_priv_tbl, segmap) +
+ (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid),
+ rkvdec->regs + RKVDEC_REG_VP9_SEGIDCUR_BASE);
+ writel_relaxed(vp9_ctx->priv_tbl.dma +
+ offsetof(struct rkvdec_vp9_priv_tbl, segmap) +
+ (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid)),
+ rkvdec->regs + RKVDEC_REG_VP9_SEGIDLAST_BASE);
+
+ if (!intra_only &&
+ !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
+ vp9_ctx->last.valid)
+ mv_ref = last;
+ else
+ mv_ref = dst;
+
+ writel_relaxed(get_mv_base_addr(mv_ref),
+ rkvdec->regs + RKVDEC_VP9_REF_COLMV_BASE);
+
+ writel_relaxed(ctx->decoded_fmt.fmt.pix_mp.width |
+ (ctx->decoded_fmt.fmt.pix_mp.height << 16),
+ rkvdec->regs + RKVDEC_REG_PERFORMANCE_CYCLE);
+}
+
+static int validate_dec_params(struct rkvdec_ctx *ctx,
+ const struct v4l2_ctrl_vp9_frame *dec_params)
+{
+ unsigned int aligned_width, aligned_height;
+
+ /* We only support profile 0. */
+ if (dec_params->profile != 0) {
+ dev_err(ctx->dev->dev, "unsupported profile %d\n",
+ dec_params->profile);
+ return -EINVAL;
+ }
+
+ aligned_width = round_up(dec_params->frame_width_minus_1 + 1, 64);
+ aligned_height = round_up(dec_params->frame_height_minus_1 + 1, 64);
+
+ /*
+ * Userspace should update the capture/decoded format when the
+ * resolution changes.
+ */
+ if (aligned_width != ctx->decoded_fmt.fmt.pix_mp.width ||
+ aligned_height != ctx->decoded_fmt.fmt.pix_mp.height) {
+ dev_err(ctx->dev->dev,
+ "unexpected bitstream resolution %dx%d\n",
+ dec_params->frame_width_minus_1 + 1,
+ dec_params->frame_height_minus_1 + 1);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rkvdec_vp9_run_preamble(struct rkvdec_ctx *ctx,
+ struct rkvdec_vp9_run *run)
+{
+ const struct v4l2_ctrl_vp9_frame *dec_params;
+ const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates;
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct v4l2_ctrl *ctrl;
+ unsigned int fctx_idx;
+ int ret;
+
+ /* v4l2-specific stuff */
+ rkvdec_run_preamble(ctx, &run->base);
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_VP9_FRAME);
+ if (WARN_ON(!ctrl))
+ return -EINVAL;
+ dec_params = ctrl->p_cur.p;
+
+ ret = validate_dec_params(ctx, dec_params);
+ if (ret)
+ return ret;
+
+ run->decode_params = dec_params;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, V4L2_CID_STATELESS_VP9_COMPRESSED_HDR);
+ if (WARN_ON(!ctrl))
+ return -EINVAL;
+ prob_updates = ctrl->p_cur.p;
+ vp9_ctx->cur.tx_mode = prob_updates->tx_mode;
+
+ /*
+ * vp9 stuff
+ *
+ * by this point the userspace has done all parts of 6.2 uncompressed_header()
+ * except this fragment:
+ * if ( FrameIsIntra || error_resilient_mode ) {
+ * setup_past_independence ( )
+ * if ( frame_type == KEY_FRAME || error_resilient_mode == 1 ||
+ * reset_frame_context == 3 ) {
+ * for ( i = 0; i < 4; i ++ ) {
+ * save_probs( i )
+ * }
+ * } else if ( reset_frame_context == 2 ) {
+ * save_probs( frame_context_idx )
+ * }
+ * frame_context_idx = 0
+ * }
+ */
+ fctx_idx = v4l2_vp9_reset_frame_ctx(dec_params, vp9_ctx->frame_context);
+ vp9_ctx->cur.frame_context_idx = fctx_idx;
+
+ /* 6.1 frame(sz): load_probs() and load_probs2() */
+ vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx];
+
+ /*
+ * The userspace has also performed 6.3 compressed_header(), but handling the
+ * probs in a special way. All probs which need updating, except MV-related,
+ * have been read from the bitstream and translated through inv_map_table[],
+ * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The values passed
+ * by userspace are either translated values (there are no 0 values in
+ * inv_map_table[]), or zero to indicate no update. All MV-related probs which need
+ * updating have been read from the bitstream and (mv_prob << 1) | 1 has been
+ * performed. The values passed by userspace are either new values
+ * to replace old ones (the above mentioned shift and bitwise or never result in
+ * a zero) or zero to indicate no update.
+ * fw_update_probs() performs actual probs updates or leaves probs as-is
+ * for values for which a zero was passed from userspace.
+ */
+ v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, prob_updates, dec_params);
+
+ return 0;
+}
+
+static int rkvdec_vp9_run(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_vp9_run run = { };
+ int ret;
+
+ ret = rkvdec_vp9_run_preamble(ctx, &run);
+ if (ret) {
+ rkvdec_run_postamble(ctx, &run.base);
+ return ret;
+ }
+
+ /* Prepare probs. */
+ init_probs(ctx, &run);
+
+ /* Configure hardware registers. */
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
+
+ writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
+ writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
+
+ writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
+ /* Start decoding! */
+ writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
+ RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E,
+ rkvdec->regs + RKVDEC_REG_INTERRUPT);
+
+ return 0;
+}
+
+#define copy_tx_and_skip(p1, p2) \
+do { \
+ memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \
+ memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \
+ memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \
+ memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \
+} while (0)
+
+static void rkvdec_vp9_done(struct rkvdec_ctx *ctx,
+ struct vb2_v4l2_buffer *src_buf,
+ struct vb2_v4l2_buffer *dst_buf,
+ enum vb2_buffer_state result)
+{
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ unsigned int fctx_idx;
+
+ /* v4l2-specific stuff */
+ if (result == VB2_BUF_STATE_ERROR)
+ goto out_update_last;
+
+ /*
+ * vp9 stuff
+ *
+ * 6.1.2 refresh_probs()
+ *
+ * In the spec a complementary condition goes last in 6.1.2 refresh_probs(),
+ * but it makes no sense to perform all the activities from the first "if"
+ * there if we actually are not refreshing the frame context. On top of that,
+ * because of 6.2 uncompressed_header() whenever error_resilient_mode == 1,
+ * refresh_frame_context == 0. Consequently, if we don't jump to out_update_last
+ * it means error_resilient_mode must be 0.
+ */
+ if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX))
+ goto out_update_last;
+
+ fctx_idx = vp9_ctx->cur.frame_context_idx;
+
+ if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) {
+ /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */
+ struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables;
+ bool frame_is_intra = vp9_ctx->cur.flags &
+ (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY);
+ struct tx_and_skip {
+ u8 tx8[2][1];
+ u8 tx16[2][2];
+ u8 tx32[2][3];
+ u8 skip[3];
+ } _tx_skip, *tx_skip = &_tx_skip;
+ struct v4l2_vp9_frame_symbol_counts *counts;
+
+ /* buffer the forward-updated TX and skip probs */
+ if (frame_is_intra)
+ copy_tx_and_skip(tx_skip, probs);
+
+ /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */
+ *probs = vp9_ctx->frame_context[fctx_idx];
+
+ /* if FrameIsIntra then undo the effect of load_probs2() */
+ if (frame_is_intra)
+ copy_tx_and_skip(probs, tx_skip);
+
+ counts = frame_is_intra ? &vp9_ctx->intra_cnts : &vp9_ctx->inter_cnts;
+ v4l2_vp9_adapt_coef_probs(probs, counts,
+ !vp9_ctx->last.valid ||
+ vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME,
+ frame_is_intra);
+ if (!frame_is_intra) {
+ const struct rkvdec_vp9_inter_frame_symbol_counts *inter_cnts;
+ u32 classes[2][11];
+ int i;
+
+ inter_cnts = vp9_ctx->count_tbl.cpu;
+ for (i = 0; i < ARRAY_SIZE(classes); ++i)
+ memcpy(classes[i], inter_cnts->classes[i], sizeof(classes[0]));
+ counts->classes = &classes;
+
+ /* load_probs2() already done */
+ v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts,
+ vp9_ctx->cur.reference_mode,
+ vp9_ctx->cur.interpolation_filter,
+ vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags);
+ }
+ }
+
+ /* 6.1.2 refresh_probs(): save_probs(fctx_idx) */
+ vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables;
+
+out_update_last:
+ update_ctx_last_info(vp9_ctx);
+}
+
+static void rkvdec_init_v4l2_vp9_count_tbl(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_vp9_intra_frame_symbol_counts *intra_cnts = vp9_ctx->count_tbl.cpu;
+ struct rkvdec_vp9_inter_frame_symbol_counts *inter_cnts = vp9_ctx->count_tbl.cpu;
+ int i, j, k, l, m;
+
+ vp9_ctx->inter_cnts.partition = &inter_cnts->partition;
+ vp9_ctx->inter_cnts.skip = &inter_cnts->skip;
+ vp9_ctx->inter_cnts.intra_inter = &inter_cnts->inter;
+ vp9_ctx->inter_cnts.tx32p = &inter_cnts->tx32p;
+ vp9_ctx->inter_cnts.tx16p = &inter_cnts->tx16p;
+ vp9_ctx->inter_cnts.tx8p = &inter_cnts->tx8p;
+
+ vp9_ctx->intra_cnts.partition = (u32 (*)[16][4])(&intra_cnts->partition);
+ vp9_ctx->intra_cnts.skip = &intra_cnts->skip;
+ vp9_ctx->intra_cnts.intra_inter = &intra_cnts->intra;
+ vp9_ctx->intra_cnts.tx32p = &intra_cnts->tx32p;
+ vp9_ctx->intra_cnts.tx16p = &intra_cnts->tx16p;
+ vp9_ctx->intra_cnts.tx8p = &intra_cnts->tx8p;
+
+ vp9_ctx->inter_cnts.y_mode = &inter_cnts->y_mode;
+ vp9_ctx->inter_cnts.uv_mode = &inter_cnts->uv_mode;
+ vp9_ctx->inter_cnts.comp = &inter_cnts->comp;
+ vp9_ctx->inter_cnts.comp_ref = &inter_cnts->comp_ref;
+ vp9_ctx->inter_cnts.single_ref = &inter_cnts->single_ref;
+ vp9_ctx->inter_cnts.mv_mode = &inter_cnts->mv_mode;
+ vp9_ctx->inter_cnts.filter = &inter_cnts->filter;
+ vp9_ctx->inter_cnts.mv_joint = &inter_cnts->mv_joint;
+ vp9_ctx->inter_cnts.sign = &inter_cnts->sign;
+ /*
+ * rk hardware actually uses "u32 classes[2][11 + 1];"
+ * instead of "u32 classes[2][11];", so this must be explicitly
+ * copied into vp9_ctx->classes when passing the data to the
+ * vp9 library function
+ */
+ vp9_ctx->inter_cnts.class0 = &inter_cnts->class0;
+ vp9_ctx->inter_cnts.bits = &inter_cnts->bits;
+ vp9_ctx->inter_cnts.class0_fp = &inter_cnts->class0_fp;
+ vp9_ctx->inter_cnts.fp = &inter_cnts->fp;
+ vp9_ctx->inter_cnts.class0_hp = &inter_cnts->class0_hp;
+ vp9_ctx->inter_cnts.hp = &inter_cnts->hp;
+
+#define INNERMOST_LOOP \
+ do { \
+ for (m = 0; m < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0][0][0]); ++m) {\
+ vp9_ctx->inter_cnts.coeff[i][j][k][l][m] = \
+ &inter_cnts->ref_cnt[k][i][j][l][m].coeff; \
+ vp9_ctx->inter_cnts.eob[i][j][k][l][m][0] = \
+ &inter_cnts->ref_cnt[k][i][j][l][m].eob[0]; \
+ vp9_ctx->inter_cnts.eob[i][j][k][l][m][1] = \
+ &inter_cnts->ref_cnt[k][i][j][l][m].eob[1]; \
+ \
+ vp9_ctx->intra_cnts.coeff[i][j][k][l][m] = \
+ &intra_cnts->ref_cnt[k][i][j][l][m].coeff; \
+ vp9_ctx->intra_cnts.eob[i][j][k][l][m][0] = \
+ &intra_cnts->ref_cnt[k][i][j][l][m].eob[0]; \
+ vp9_ctx->intra_cnts.eob[i][j][k][l][m][1] = \
+ &intra_cnts->ref_cnt[k][i][j][l][m].eob[1]; \
+ } \
+ } while (0)
+
+ for (i = 0; i < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff); ++i)
+ for (j = 0; j < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0]); ++j)
+ for (k = 0; k < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0]); ++k)
+ for (l = 0; l < ARRAY_SIZE(vp9_ctx->inter_cnts.coeff[0][0][0]); ++l)
+ INNERMOST_LOOP;
+#undef INNERMOST_LOOP
+}
+
+static int rkvdec_vp9_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_vp9_priv_tbl *priv_tbl;
+ struct rkvdec_vp9_ctx *vp9_ctx;
+ unsigned char *count_tbl;
+ int ret;
+
+ vp9_ctx = kzalloc(sizeof(*vp9_ctx), GFP_KERNEL);
+ if (!vp9_ctx)
+ return -ENOMEM;
+
+ ctx->priv = vp9_ctx;
+
+ BUILD_BUG_ON(sizeof(priv_tbl->probs) % 16); /* ensure probs size is 128-bit aligned */
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &vp9_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ ret = -ENOMEM;
+ goto err_free_ctx;
+ }
+
+ vp9_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ vp9_ctx->priv_tbl.cpu = priv_tbl;
+
+ count_tbl = dma_alloc_coherent(rkvdec->dev, RKVDEC_VP9_COUNT_SIZE,
+ &vp9_ctx->count_tbl.dma, GFP_KERNEL);
+ if (!count_tbl) {
+ ret = -ENOMEM;
+ goto err_free_priv_tbl;
+ }
+
+ vp9_ctx->count_tbl.size = RKVDEC_VP9_COUNT_SIZE;
+ vp9_ctx->count_tbl.cpu = count_tbl;
+ rkvdec_init_v4l2_vp9_count_tbl(ctx);
+
+ return 0;
+
+err_free_priv_tbl:
+ dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size,
+ vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma);
+
+err_free_ctx:
+ kfree(vp9_ctx);
+ return ret;
+}
+
+static void rkvdec_vp9_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, vp9_ctx->count_tbl.size,
+ vp9_ctx->count_tbl.cpu, vp9_ctx->count_tbl.dma);
+ dma_free_coherent(rkvdec->dev, vp9_ctx->priv_tbl.size,
+ vp9_ctx->priv_tbl.cpu, vp9_ctx->priv_tbl.dma);
+ kfree(vp9_ctx);
+}
+
+static int rkvdec_vp9_adjust_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+ fmt->num_planes = 1;
+ if (!fmt->plane_fmt[0].sizeimage)
+ fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * 2;
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops = {
+ .adjust_fmt = rkvdec_vp9_adjust_fmt,
+ .start = rkvdec_vp9_start,
+ .stop = rkvdec_vp9_stop,
+ .run = rkvdec_vp9_run,
+ .done = rkvdec_vp9_done,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
new file mode 100644
index 000000000000..d707088ec0dc
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -0,0 +1,1251 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Video Decoder driver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on rkvdec driver by Google LLC. (Tomasz Figa <tfiga@chromium.org>)
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "rkvdec.h"
+#include "rkvdec-regs.h"
+
+static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1,
+ enum rkvdec_image_fmt fmt2)
+{
+ return fmt1 == fmt2 || fmt2 == RKVDEC_IMG_FMT_ANY ||
+ fmt1 == RKVDEC_IMG_FMT_ANY;
+}
+
+static bool rkvdec_image_fmt_changed(struct rkvdec_ctx *ctx,
+ enum rkvdec_image_fmt image_fmt)
+{
+ if (image_fmt == RKVDEC_IMG_FMT_ANY)
+ return false;
+
+ return ctx->image_fmt != image_fmt;
+}
+
+static u32 rkvdec_enum_decoded_fmt(struct rkvdec_ctx *ctx, int index,
+ enum rkvdec_image_fmt image_fmt)
+{
+ const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
+ int fmt_idx = -1;
+ unsigned int i;
+
+ if (WARN_ON(!desc))
+ return 0;
+
+ for (i = 0; i < desc->num_decoded_fmts; i++) {
+ if (!rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt,
+ image_fmt))
+ continue;
+ fmt_idx++;
+ if (index == fmt_idx)
+ return desc->decoded_fmts[i].fourcc;
+ }
+
+ return 0;
+}
+
+static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc,
+ enum rkvdec_image_fmt image_fmt)
+{
+ const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
+ unsigned int i;
+
+ for (i = 0; i < desc->num_decoded_fmts; i++) {
+ if (rkvdec_image_fmt_match(desc->decoded_fmts[i].image_fmt,
+ image_fmt) &&
+ desc->decoded_fmts[i].fourcc == fourcc)
+ return true;
+ }
+
+ return false;
+}
+
+static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx,
+ struct v4l2_pix_format_mplane *pix_mp)
+{
+ v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height);
+ pix_mp->plane_fmt[0].sizeimage += 128 *
+ DIV_ROUND_UP(pix_mp->width, 16) *
+ DIV_ROUND_UP(pix_mp->height, 16);
+}
+
+static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f,
+ u32 fourcc)
+{
+ memset(f, 0, sizeof(*f));
+ f->fmt.pix_mp.pixelformat = fourcc;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static void rkvdec_reset_decoded_fmt(struct rkvdec_ctx *ctx)
+{
+ struct v4l2_format *f = &ctx->decoded_fmt;
+ u32 fourcc;
+
+ fourcc = rkvdec_enum_decoded_fmt(ctx, 0, ctx->image_fmt);
+ rkvdec_reset_fmt(ctx, f, fourcc);
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f->fmt.pix_mp.width = ctx->coded_fmt.fmt.pix_mp.width;
+ f->fmt.pix_mp.height = ctx->coded_fmt.fmt.pix_mp.height;
+ rkvdec_fill_decoded_pixfmt(ctx, &f->fmt.pix_mp);
+}
+
+static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
+ const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
+
+ if (desc->ops->try_ctrl)
+ return desc->ops->try_ctrl(ctx, ctrl);
+
+ return 0;
+}
+
+static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
+ const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
+ enum rkvdec_image_fmt image_fmt;
+ struct vb2_queue *vq;
+
+ /* Check if this change requires a capture format reset */
+ if (!desc->ops->get_image_fmt)
+ return 0;
+
+ image_fmt = desc->ops->get_image_fmt(ctx, ctrl);
+ if (rkvdec_image_fmt_changed(ctx, image_fmt)) {
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ ctx->image_fmt = image_fmt;
+ rkvdec_reset_decoded_fmt(ctx);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = {
+ .try_ctrl = rkvdec_try_ctrl,
+ .s_ctrl = rkvdec_s_ctrl,
+};
+
+static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_SPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_PPS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE,
+ .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_H264_START_CODE,
+ .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE,
+ .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA,
+ .cfg.menu_skip_mask =
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE),
+ .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ },
+};
+
+static const struct rkvdec_ctrls rkvdec_h264_ctrls = {
+ .ctrls = rkvdec_h264_ctrl_descs,
+ .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs),
+};
+
+static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .image_fmt = RKVDEC_IMG_FMT_420_8BIT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV15,
+ .image_fmt = RKVDEC_IMG_FMT_420_10BIT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .image_fmt = RKVDEC_IMG_FMT_422_8BIT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV20,
+ .image_fmt = RKVDEC_IMG_FMT_422_10BIT,
+ },
+};
+
+static const struct rkvdec_ctrl_desc rkvdec_vp9_ctrl_descs[] = {
+ {
+ .cfg.id = V4L2_CID_STATELESS_VP9_FRAME,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .cfg.max = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .cfg.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ },
+};
+
+static const struct rkvdec_ctrls rkvdec_vp9_ctrls = {
+ .ctrls = rkvdec_vp9_ctrl_descs,
+ .num_ctrls = ARRAY_SIZE(rkvdec_vp9_ctrl_descs),
+};
+
+static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .image_fmt = RKVDEC_IMG_FMT_420_8BIT,
+ },
+};
+
+static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 4096,
+ .step_width = 64,
+ .min_height = 48,
+ .max_height = 2560,
+ .step_height = 16,
+ },
+ .ctrls = &rkvdec_h264_ctrls,
+ .ops = &rkvdec_h264_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts),
+ .decoded_fmts = rkvdec_h264_decoded_fmts,
+ .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VP9_FRAME,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 4096,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 2304,
+ .step_height = 64,
+ },
+ .ctrls = &rkvdec_vp9_ctrls,
+ .ops = &rkvdec_vp9_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts),
+ .decoded_fmts = rkvdec_vp9_decoded_fmts,
+ }
+};
+
+static const struct rkvdec_coded_fmt_desc *
+rkvdec_find_coded_fmt_desc(u32 fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
+ if (rkvdec_coded_fmts[i].fourcc == fourcc)
+ return &rkvdec_coded_fmts[i];
+ }
+
+ return NULL;
+}
+
+static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx)
+{
+ struct v4l2_format *f = &ctx->coded_fmt;
+
+ ctx->coded_fmt_desc = &rkvdec_coded_fmts[0];
+ rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc);
+
+ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width;
+ f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height;
+
+ if (ctx->coded_fmt_desc->ops->adjust_fmt)
+ ctx->coded_fmt_desc->ops->adjust_fmt(ctx, f);
+}
+
+static int rkvdec_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ const struct rkvdec_coded_fmt_desc *fmt;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ fmt = rkvdec_find_coded_fmt_desc(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = 1;
+ fsize->stepwise.max_width = fmt->frmsize.max_width;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = 1;
+ fsize->stepwise.max_height = fmt->frmsize.max_height;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int rkvdec_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct rkvdec_dev *rkvdec = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+
+ strscpy(cap->driver, rkvdec->dev->driver->name,
+ sizeof(cap->driver));
+ strscpy(cap->card, vdev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ rkvdec->dev->driver->name);
+ return 0;
+}
+
+static int rkvdec_try_capture_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+ const struct rkvdec_coded_fmt_desc *coded_desc;
+
+ /*
+ * The codec context should point to a coded format desc, if the format
+ * on the coded end has not been set yet, it should point to the
+ * default value.
+ */
+ coded_desc = ctx->coded_fmt_desc;
+ if (WARN_ON(!coded_desc))
+ return -EINVAL;
+
+ if (!rkvdec_is_valid_fmt(ctx, pix_mp->pixelformat, ctx->image_fmt))
+ pix_mp->pixelformat = rkvdec_enum_decoded_fmt(ctx, 0,
+ ctx->image_fmt);
+
+ /* Always apply the frmsize constraint of the coded end. */
+ pix_mp->width = max(pix_mp->width, ctx->coded_fmt.fmt.pix_mp.width);
+ pix_mp->height = max(pix_mp->height, ctx->coded_fmt.fmt.pix_mp.height);
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &coded_desc->frmsize);
+
+ rkvdec_fill_decoded_pixfmt(ctx, pix_mp);
+ pix_mp->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int rkvdec_try_output_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+ const struct rkvdec_coded_fmt_desc *desc;
+
+ desc = rkvdec_find_coded_fmt_desc(pix_mp->pixelformat);
+ if (!desc) {
+ pix_mp->pixelformat = rkvdec_coded_fmts[0].fourcc;
+ desc = &rkvdec_coded_fmts[0];
+ }
+
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &desc->frmsize);
+
+ pix_mp->field = V4L2_FIELD_NONE;
+ /* All coded formats are considered single planar for now. */
+ pix_mp->num_planes = 1;
+
+ if (desc->ops->adjust_fmt) {
+ int ret;
+
+ ret = desc->ops->adjust_fmt(ctx, f);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rkvdec_s_capture_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+ struct vb2_queue *vq;
+ int ret;
+
+ /* Change not allowed if queue is busy */
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ ret = rkvdec_try_capture_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ ctx->decoded_fmt = *f;
+ return 0;
+}
+
+static int rkvdec_s_output_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ const struct rkvdec_coded_fmt_desc *desc;
+ struct v4l2_format *cap_fmt;
+ struct vb2_queue *peer_vq, *vq;
+ int ret;
+
+ /*
+ * In order to support dynamic resolution change, the decoder admits
+ * a resolution change, as long as the pixelformat remains. Can't be
+ * done if streaming.
+ */
+ vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (vb2_is_streaming(vq) ||
+ (vb2_is_busy(vq) &&
+ f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat))
+ return -EBUSY;
+
+ /*
+ * Since format change on the OUTPUT queue will reset the CAPTURE
+ * queue, we can't allow doing so when the CAPTURE queue has buffers
+ * allocated.
+ */
+ peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (vb2_is_busy(peer_vq))
+ return -EBUSY;
+
+ ret = rkvdec_try_output_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ desc = rkvdec_find_coded_fmt_desc(f->fmt.pix_mp.pixelformat);
+ if (!desc)
+ return -EINVAL;
+ ctx->coded_fmt_desc = desc;
+ ctx->coded_fmt = *f;
+
+ /*
+ * Current decoded format might have become invalid with newly
+ * selected codec, so reset it to default just to be safe and
+ * keep internal driver state sane. User is mandated to set
+ * the decoded format again after we return, so we don't need
+ * anything smarter.
+ *
+ * Note that this will propagates any size changes to the decoded format.
+ */
+ ctx->image_fmt = RKVDEC_IMG_FMT_ANY;
+ rkvdec_reset_decoded_fmt(ctx);
+
+ /* Propagate colorspace information to capture. */
+ cap_fmt = &ctx->decoded_fmt;
+ cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+
+ /* Enable format specific queue features */
+ vq->subsystem_flags |= desc->subsystem_flags;
+
+ return 0;
+}
+
+static int rkvdec_g_output_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+
+ *f = ctx->coded_fmt;
+ return 0;
+}
+
+static int rkvdec_g_capture_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+
+ *f = ctx->decoded_fmt;
+ return 0;
+}
+
+static int rkvdec_enum_output_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(rkvdec_coded_fmts))
+ return -EINVAL;
+
+ f->pixelformat = rkvdec_coded_fmts[f->index].fourcc;
+ return 0;
+}
+
+static int rkvdec_enum_capture_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(priv);
+ u32 fourcc;
+
+ fourcc = rkvdec_enum_decoded_fmt(ctx, f->index, ctx->image_fmt);
+ if (!fourcc)
+ return -EINVAL;
+
+ f->pixelformat = fourcc;
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rkvdec_ioctl_ops = {
+ .vidioc_querycap = rkvdec_querycap,
+ .vidioc_enum_framesizes = rkvdec_enum_framesizes,
+
+ .vidioc_try_fmt_vid_cap_mplane = rkvdec_try_capture_fmt,
+ .vidioc_try_fmt_vid_out_mplane = rkvdec_try_output_fmt,
+ .vidioc_s_fmt_vid_out_mplane = rkvdec_s_output_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = rkvdec_s_capture_fmt,
+ .vidioc_g_fmt_vid_out_mplane = rkvdec_g_output_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = rkvdec_g_capture_fmt,
+ .vidioc_enum_fmt_vid_out = rkvdec_enum_output_fmt,
+ .vidioc_enum_fmt_vid_cap = rkvdec_enum_capture_fmt,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_decoder_cmd = v4l2_m2m_ioctl_stateless_decoder_cmd,
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
+};
+
+static int rkvdec_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_format *f;
+ unsigned int i;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ f = &ctx->coded_fmt;
+ else
+ f = &ctx->decoded_fmt;
+
+ if (*num_planes) {
+ if (*num_planes != f->fmt.pix_mp.num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+ if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage)
+ return -EINVAL;
+ }
+ } else {
+ *num_planes = f->fmt.pix_mp.num_planes;
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++)
+ sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static int rkvdec_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_format *f;
+ unsigned int i;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ f = &ctx->coded_fmt;
+ else
+ f = &ctx->decoded_fmt;
+
+ for (i = 0; i < f->fmt.pix_mp.num_planes; ++i) {
+ u32 sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < sizeimage)
+ return -EINVAL;
+ }
+
+ /*
+ * Buffer's bytesused must be written by driver for CAPTURE buffers.
+ * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets
+ * it to buffer length).
+ */
+ if (V4L2_TYPE_IS_CAPTURE(vq->type))
+ vb2_set_plane_payload(vb, 0, f->fmt.pix_mp.plane_fmt[0].sizeimage);
+
+ return 0;
+}
+
+static void rkvdec_buf_queue(struct vb2_buffer *vb)
+{
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int rkvdec_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static void rkvdec_buf_request_complete(struct vb2_buffer *vb)
+{
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl);
+}
+
+static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(q);
+ const struct rkvdec_coded_fmt_desc *desc;
+ int ret;
+
+ if (V4L2_TYPE_IS_CAPTURE(q->type))
+ return 0;
+
+ desc = ctx->coded_fmt_desc;
+ if (WARN_ON(!desc))
+ return -EINVAL;
+
+ if (desc->ops->start) {
+ ret = desc->ops->start(ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state)
+{
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(vq);
+
+ while (true) {
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ if (!vbuf)
+ break;
+
+ v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
+ &ctx->ctrl_hdl);
+ v4l2_m2m_buf_done(vbuf, state);
+ }
+}
+
+static void rkvdec_stop_streaming(struct vb2_queue *q)
+{
+ struct rkvdec_ctx *ctx = vb2_get_drv_priv(q);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
+
+ if (WARN_ON(!desc))
+ return;
+
+ if (desc->ops->stop)
+ desc->ops->stop(ctx);
+ }
+
+ rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR);
+}
+
+static const struct vb2_ops rkvdec_queue_ops = {
+ .queue_setup = rkvdec_queue_setup,
+ .buf_prepare = rkvdec_buf_prepare,
+ .buf_queue = rkvdec_buf_queue,
+ .buf_out_validate = rkvdec_buf_out_validate,
+ .buf_request_complete = rkvdec_buf_request_complete,
+ .start_streaming = rkvdec_start_streaming,
+ .stop_streaming = rkvdec_stop_streaming,
+};
+
+static int rkvdec_request_validate(struct media_request *req)
+{
+ unsigned int count;
+
+ count = vb2_request_buffer_cnt(req);
+ if (!count)
+ return -ENOENT;
+ else if (count > 1)
+ return -EINVAL;
+
+ return vb2_request_validate(req);
+}
+
+static const struct media_device_ops rkvdec_media_ops = {
+ .req_validate = rkvdec_request_validate,
+ .req_queue = v4l2_m2m_request_queue,
+};
+
+static void rkvdec_job_finish_no_pm(struct rkvdec_ctx *ctx,
+ enum vb2_buffer_state result)
+{
+ if (ctx->coded_fmt_desc->ops->done) {
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ ctx->coded_fmt_desc->ops->done(ctx, src_buf, dst_buf, result);
+ }
+
+ v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx,
+ result);
+}
+
+static void rkvdec_job_finish(struct rkvdec_ctx *ctx,
+ enum vb2_buffer_state result)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ pm_runtime_mark_last_busy(rkvdec->dev);
+ pm_runtime_put_autosuspend(rkvdec->dev);
+ rkvdec_job_finish_no_pm(ctx, result);
+}
+
+void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run)
+{
+ struct media_request *src_req;
+
+ memset(run, 0, sizeof(*run));
+
+ run->bufs.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ run->bufs.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ /* Apply request(s) controls if needed. */
+ src_req = run->bufs.src->vb2_buf.req_obj.req;
+ if (src_req)
+ v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl);
+
+ v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true);
+}
+
+void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run)
+{
+ struct media_request *src_req = run->bufs.src->vb2_buf.req_obj.req;
+
+ if (src_req)
+ v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl);
+}
+
+static void rkvdec_device_run(void *priv)
+{
+ struct rkvdec_ctx *ctx = priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc;
+ int ret;
+
+ if (WARN_ON(!desc))
+ return;
+
+ ret = pm_runtime_resume_and_get(rkvdec->dev);
+ if (ret < 0) {
+ rkvdec_job_finish_no_pm(ctx, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ ret = desc->ops->run(ctx);
+ if (ret)
+ rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);
+}
+
+static const struct v4l2_m2m_ops rkvdec_m2m_ops = {
+ .device_run = rkvdec_device_run,
+};
+
+static int rkvdec_queue_init(void *priv,
+ struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct rkvdec_ctx *ctx = priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &rkvdec_queue_ops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ /*
+ * Driver does mostly sequential access, so sacrifice TLB efficiency
+ * for faster allocation. Also, no CPU access on the source queue,
+ * so no kernel mapping needed.
+ */
+ src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
+ DMA_ATTR_NO_KERNEL_MAPPING;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &rkvdec->vdev_lock;
+ src_vq->dev = rkvdec->v4l2_dev.dev;
+ src_vq->supports_requests = true;
+ src_vq->requires_requests = true;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->bidirectional = true;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES |
+ DMA_ATTR_NO_KERNEL_MAPPING;
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &rkvdec_queue_ops;
+ dst_vq->buf_struct_size = sizeof(struct rkvdec_decoded_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &rkvdec->vdev_lock;
+ dst_vq->dev = rkvdec->v4l2_dev.dev;
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int rkvdec_add_ctrls(struct rkvdec_ctx *ctx,
+ const struct rkvdec_ctrls *ctrls)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctrls->num_ctrls; i++) {
+ const struct v4l2_ctrl_config *cfg = &ctrls->ctrls[i].cfg;
+
+ v4l2_ctrl_new_custom(&ctx->ctrl_hdl, cfg, ctx);
+ if (ctx->ctrl_hdl.error)
+ return ctx->ctrl_hdl.error;
+ }
+
+ return 0;
+}
+
+static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx)
+{
+ unsigned int i, nctrls = 0;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++)
+ nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls);
+
+ for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
+ ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls);
+ if (ret)
+ goto err_free_handler;
+ }
+
+ ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
+ if (ret)
+ goto err_free_handler;
+
+ ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
+ return 0;
+
+err_free_handler:
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+ return ret;
+}
+
+static int rkvdec_open(struct file *filp)
+{
+ struct rkvdec_dev *rkvdec = video_drvdata(filp);
+ struct rkvdec_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = rkvdec;
+ rkvdec_reset_coded_fmt(ctx);
+ rkvdec_reset_decoded_fmt(ctx);
+ v4l2_fh_init(&ctx->fh, video_devdata(filp));
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rkvdec->m2m_dev, ctx,
+ rkvdec_queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ goto err_free_ctx;
+ }
+
+ ret = rkvdec_init_ctrls(ctx);
+ if (ret)
+ goto err_cleanup_m2m_ctx;
+
+ filp->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ return 0;
+
+err_cleanup_m2m_ctx:
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+err_free_ctx:
+ kfree(ctx);
+ return ret;
+}
+
+static int rkvdec_release(struct file *filp)
+{
+ struct rkvdec_ctx *ctx = fh_to_rkvdec_ctx(filp->private_data);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations rkvdec_fops = {
+ .owner = THIS_MODULE,
+ .open = rkvdec_open,
+ .release = rkvdec_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static int rkvdec_v4l2_init(struct rkvdec_dev *rkvdec)
+{
+ int ret;
+
+ ret = v4l2_device_register(rkvdec->dev, &rkvdec->v4l2_dev);
+ if (ret) {
+ dev_err(rkvdec->dev, "Failed to register V4L2 device\n");
+ return ret;
+ }
+
+ rkvdec->m2m_dev = v4l2_m2m_init(&rkvdec_m2m_ops);
+ if (IS_ERR(rkvdec->m2m_dev)) {
+ v4l2_err(&rkvdec->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(rkvdec->m2m_dev);
+ goto err_unregister_v4l2;
+ }
+
+ rkvdec->mdev.dev = rkvdec->dev;
+ strscpy(rkvdec->mdev.model, "rkvdec", sizeof(rkvdec->mdev.model));
+ strscpy(rkvdec->mdev.bus_info, "platform:rkvdec",
+ sizeof(rkvdec->mdev.bus_info));
+ media_device_init(&rkvdec->mdev);
+ rkvdec->mdev.ops = &rkvdec_media_ops;
+ rkvdec->v4l2_dev.mdev = &rkvdec->mdev;
+
+ rkvdec->vdev.lock = &rkvdec->vdev_lock;
+ rkvdec->vdev.v4l2_dev = &rkvdec->v4l2_dev;
+ rkvdec->vdev.fops = &rkvdec_fops;
+ rkvdec->vdev.release = video_device_release_empty;
+ rkvdec->vdev.vfl_dir = VFL_DIR_M2M;
+ rkvdec->vdev.device_caps = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_M2M_MPLANE;
+ rkvdec->vdev.ioctl_ops = &rkvdec_ioctl_ops;
+ video_set_drvdata(&rkvdec->vdev, rkvdec);
+ strscpy(rkvdec->vdev.name, "rkvdec", sizeof(rkvdec->vdev.name));
+
+ ret = video_register_device(&rkvdec->vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ v4l2_err(&rkvdec->v4l2_dev, "Failed to register video device\n");
+ goto err_cleanup_mc;
+ }
+
+ ret = v4l2_m2m_register_media_controller(rkvdec->m2m_dev, &rkvdec->vdev,
+ MEDIA_ENT_F_PROC_VIDEO_DECODER);
+ if (ret) {
+ v4l2_err(&rkvdec->v4l2_dev,
+ "Failed to initialize V4L2 M2M media controller\n");
+ goto err_unregister_vdev;
+ }
+
+ ret = media_device_register(&rkvdec->mdev);
+ if (ret) {
+ v4l2_err(&rkvdec->v4l2_dev, "Failed to register media device\n");
+ goto err_unregister_mc;
+ }
+
+ return 0;
+
+err_unregister_mc:
+ v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev);
+
+err_unregister_vdev:
+ video_unregister_device(&rkvdec->vdev);
+
+err_cleanup_mc:
+ media_device_cleanup(&rkvdec->mdev);
+ v4l2_m2m_release(rkvdec->m2m_dev);
+
+err_unregister_v4l2:
+ v4l2_device_unregister(&rkvdec->v4l2_dev);
+ return ret;
+}
+
+static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec)
+{
+ media_device_unregister(&rkvdec->mdev);
+ v4l2_m2m_unregister_media_controller(rkvdec->m2m_dev);
+ video_unregister_device(&rkvdec->vdev);
+ media_device_cleanup(&rkvdec->mdev);
+ v4l2_m2m_release(rkvdec->m2m_dev);
+ v4l2_device_unregister(&rkvdec->v4l2_dev);
+}
+
+static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec)
+{
+ if (rkvdec->empty_domain) {
+ /*
+ * To rewrite mapping into the attached IOMMU core, attach a new empty domain that
+ * will program an empty table, then detach it to restore the default domain and
+ * all cached mappings.
+ * This is safely done in this interrupt handler to make sure no memory get mapped
+ * through the IOMMU while the empty domain is attached.
+ */
+ iommu_attach_device(rkvdec->empty_domain, rkvdec->dev);
+ iommu_detach_device(rkvdec->empty_domain, rkvdec->dev);
+ }
+}
+
+static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
+{
+ struct rkvdec_dev *rkvdec = priv;
+ struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
+ enum vb2_buffer_state state;
+ u32 status;
+
+ status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT);
+ writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT);
+
+ if (status & RKVDEC_RDY_STA) {
+ state = VB2_BUF_STATE_DONE;
+ } else {
+ state = VB2_BUF_STATE_ERROR;
+ if (status & RKVDEC_SOFTRESET_RDY)
+ rkvdec_iommu_restore(rkvdec);
+ }
+
+ if (cancel_delayed_work(&rkvdec->watchdog_work))
+ rkvdec_job_finish(ctx, state);
+
+ return IRQ_HANDLED;
+}
+
+static void rkvdec_watchdog_func(struct work_struct *work)
+{
+ struct rkvdec_dev *rkvdec;
+ struct rkvdec_ctx *ctx;
+
+ rkvdec = container_of(to_delayed_work(work), struct rkvdec_dev,
+ watchdog_work);
+ ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
+ if (ctx) {
+ dev_err(rkvdec->dev, "Frame processing timed out!\n");
+ writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT);
+ writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL);
+ rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct of_device_id of_rkvdec_match[] = {
+ { .compatible = "rockchip,rk3399-vdec" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_rkvdec_match);
+
+static const char * const rkvdec_clk_names[] = {
+ "axi", "ahb", "cabac", "core"
+};
+
+static int rkvdec_probe(struct platform_device *pdev)
+{
+ struct rkvdec_dev *rkvdec;
+ unsigned int i;
+ int ret, irq;
+
+ rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL);
+ if (!rkvdec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rkvdec);
+ rkvdec->dev = &pdev->dev;
+ mutex_init(&rkvdec->vdev_lock);
+ INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func);
+
+ rkvdec->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names),
+ sizeof(*rkvdec->clocks), GFP_KERNEL);
+ if (!rkvdec->clocks)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(rkvdec_clk_names); i++)
+ rkvdec->clocks[i].id = rkvdec_clk_names[i];
+
+ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names),
+ rkvdec->clocks);
+ if (ret)
+ return ret;
+
+ rkvdec->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rkvdec->regs))
+ return PTR_ERR(rkvdec->regs);
+
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "Could not set DMA coherent mask.\n");
+ return ret;
+ }
+
+ if (iommu_get_domain_for_dev(&pdev->dev)) {
+ rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
+
+ if (!rkvdec->empty_domain)
+ dev_warn(rkvdec->dev, "cannot alloc new empty domain\n");
+ }
+
+ vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -ENXIO;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ rkvdec_irq_handler, IRQF_ONESHOT,
+ dev_name(&pdev->dev), rkvdec);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request vdec IRQ\n");
+ return ret;
+ }
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = rkvdec_v4l2_init(rkvdec);
+ if (ret)
+ goto err_disable_runtime_pm;
+
+ return 0;
+
+err_disable_runtime_pm:
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void rkvdec_remove(struct platform_device *pdev)
+{
+ struct rkvdec_dev *rkvdec = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&rkvdec->watchdog_work);
+
+ rkvdec_v4l2_cleanup(rkvdec);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+
+ if (rkvdec->empty_domain)
+ iommu_domain_free(rkvdec->empty_domain);
+}
+
+#ifdef CONFIG_PM
+static int rkvdec_runtime_resume(struct device *dev)
+{
+ struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(ARRAY_SIZE(rkvdec_clk_names),
+ rkvdec->clocks);
+}
+
+static int rkvdec_runtime_suspend(struct device *dev)
+{
+ struct rkvdec_dev *rkvdec = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(ARRAY_SIZE(rkvdec_clk_names),
+ rkvdec->clocks);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rkvdec_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(rkvdec_runtime_suspend, rkvdec_runtime_resume, NULL)
+};
+
+static struct platform_driver rkvdec_driver = {
+ .probe = rkvdec_probe,
+ .remove = rkvdec_remove,
+ .driver = {
+ .name = "rkvdec",
+ .of_match_table = of_rkvdec_match,
+ .pm = &rkvdec_pm_ops,
+ },
+};
+module_platform_driver(rkvdec_driver);
+
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@collabora.com>");
+MODULE_DESCRIPTION("Rockchip Video Decoder driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
new file mode 100644
index 000000000000..f6e8bf38add3
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Video Decoder driver
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ *
+ * Based on rkvdec driver by Google LLC. (Tomasz Figa <tfiga@chromium.org>)
+ * Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ */
+#ifndef RKVDEC_H_
+#define RKVDEC_H_
+
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+struct rkvdec_ctx;
+
+struct rkvdec_ctrl_desc {
+ struct v4l2_ctrl_config cfg;
+};
+
+struct rkvdec_ctrls {
+ const struct rkvdec_ctrl_desc *ctrls;
+ unsigned int num_ctrls;
+};
+
+struct rkvdec_run {
+ struct {
+ struct vb2_v4l2_buffer *src;
+ struct vb2_v4l2_buffer *dst;
+ } bufs;
+};
+
+struct rkvdec_vp9_decoded_buffer_info {
+ /* Info needed when the decoded frame serves as a reference frame. */
+ unsigned short width;
+ unsigned short height;
+ unsigned int bit_depth : 4;
+};
+
+struct rkvdec_decoded_buffer {
+ /* Must be the first field in this struct. */
+ struct v4l2_m2m_buffer base;
+
+ union {
+ struct rkvdec_vp9_decoded_buffer_info vp9;
+ };
+};
+
+static inline struct rkvdec_decoded_buffer *
+vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf)
+{
+ return container_of(buf, struct rkvdec_decoded_buffer,
+ base.vb.vb2_buf);
+}
+
+struct rkvdec_coded_fmt_ops {
+ int (*adjust_fmt)(struct rkvdec_ctx *ctx,
+ struct v4l2_format *f);
+ int (*start)(struct rkvdec_ctx *ctx);
+ void (*stop)(struct rkvdec_ctx *ctx);
+ int (*run)(struct rkvdec_ctx *ctx);
+ void (*done)(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *src_buf,
+ struct vb2_v4l2_buffer *dst_buf,
+ enum vb2_buffer_state result);
+ int (*try_ctrl)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
+ enum rkvdec_image_fmt (*get_image_fmt)(struct rkvdec_ctx *ctx,
+ struct v4l2_ctrl *ctrl);
+};
+
+enum rkvdec_image_fmt {
+ RKVDEC_IMG_FMT_ANY = 0,
+ RKVDEC_IMG_FMT_420_8BIT,
+ RKVDEC_IMG_FMT_420_10BIT,
+ RKVDEC_IMG_FMT_422_8BIT,
+ RKVDEC_IMG_FMT_422_10BIT,
+};
+
+struct rkvdec_decoded_fmt_desc {
+ u32 fourcc;
+ enum rkvdec_image_fmt image_fmt;
+};
+
+struct rkvdec_coded_fmt_desc {
+ u32 fourcc;
+ struct v4l2_frmsize_stepwise frmsize;
+ const struct rkvdec_ctrls *ctrls;
+ const struct rkvdec_coded_fmt_ops *ops;
+ unsigned int num_decoded_fmts;
+ const struct rkvdec_decoded_fmt_desc *decoded_fmts;
+ u32 subsystem_flags;
+};
+
+struct rkvdec_dev {
+ struct v4l2_device v4l2_dev;
+ struct media_device mdev;
+ struct video_device vdev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct device *dev;
+ struct clk_bulk_data *clocks;
+ void __iomem *regs;
+ struct mutex vdev_lock; /* serializes ioctls */
+ struct delayed_work watchdog_work;
+ struct iommu_domain *empty_domain;
+};
+
+struct rkvdec_ctx {
+ struct v4l2_fh fh;
+ struct v4l2_format coded_fmt;
+ struct v4l2_format decoded_fmt;
+ const struct rkvdec_coded_fmt_desc *coded_fmt_desc;
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct rkvdec_dev *dev;
+ enum rkvdec_image_fmt image_fmt;
+ void *priv;
+};
+
+static inline struct rkvdec_ctx *fh_to_rkvdec_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct rkvdec_ctx, fh);
+}
+
+struct rkvdec_aux_buf {
+ void *cpu;
+ dma_addr_t dma;
+ size_t size;
+};
+
+void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
+void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
+
+extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops;
+extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops;
+
+#endif /* RKVDEC_H_ */
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
index b243cbb1d010..b5b37b6f8fa8 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
@@ -131,7 +131,7 @@ static const struct dev_pm_ops fimc_is_i2c_pm_ops = {
};
static const struct of_device_id fimc_is_i2c_of_match[] = {
- { .compatible = FIMC_IS_I2C_COMPATIBLE },
+ { .compatible = "samsung,exynos4212-i2c-isp" },
{ },
};
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
index a23bd20be6c8..69d597e5c297 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
@@ -6,7 +6,5 @@
* Sylwester Nawrocki <s.nawrocki@samsung.com>
*/
-#define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp"
-
int fimc_is_register_i2c_driver(void);
void fimc_is_unregister_i2c_driver(void);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
index 2e8fe9e49735..ed7b7ca16f71 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
@@ -207,7 +207,7 @@ static int fimc_is_register_subdevs(struct fimc_is *is)
if (ret < 0)
return ret;
- for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_compatible_node(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") {
for_each_available_child_of_node(i2c_bus, child) {
ret = fimc_is_parse_sensor_config(is, index, child);
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c
index b5ee3c547789..c781853586fd 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.c
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c
@@ -482,15 +482,12 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *port)
{
- struct device_node *ep;
int ret;
- for_each_child_of_node(port, ep) {
+ for_each_child_of_node_scoped(port, ep) {
ret = fimc_md_parse_one_endpoint(fmd, ep);
- if (ret < 0) {
- of_node_put(ep);
+ if (ret < 0)
return ret;
- }
}
return 0;
@@ -501,7 +498,6 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
{
struct device_node *parent = fmd->pdev->dev.of_node;
struct device_node *ports = NULL;
- struct device_node *node;
int ret;
/*
@@ -518,7 +514,7 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
fmd->num_sensors = 0;
/* Attach sensors linked to MIPI CSI-2 receivers */
- for_each_available_child_of_node(parent, node) {
+ for_each_available_child_of_node_scoped(parent, node) {
struct device_node *port;
if (!of_node_name_eq(node, "csis"))
@@ -530,10 +526,8 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
ret = fimc_md_parse_port_node(fmd, port);
of_node_put(port);
- if (ret < 0) {
- of_node_put(node);
+ if (ret < 0)
goto cleanup;
- }
}
/* Attach sensors listed in the parallel-ports node */
@@ -541,12 +535,10 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
if (!ports)
goto rpm_put;
- for_each_child_of_node(ports, node) {
+ for_each_child_of_node_scoped(ports, node) {
ret = fimc_md_parse_port_node(fmd, node);
- if (ret < 0) {
- of_node_put(node);
+ if (ret < 0)
goto cleanup;
- }
}
of_node_put(ports);
@@ -736,10 +728,9 @@ dev_unlock:
static int fimc_md_register_platform_entities(struct fimc_md *fmd,
struct device_node *parent)
{
- struct device_node *node;
int ret = 0;
- for_each_available_child_of_node(parent, node) {
+ for_each_available_child_of_node_scoped(parent, node) {
struct platform_device *pdev;
int plat_entity = -1;
@@ -762,10 +753,8 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd,
ret = fimc_md_register_platform_entity(fmd, pdev,
plat_entity);
put_device(&pdev->dev);
- if (ret < 0) {
- of_node_put(node);
+ if (ret < 0)
break;
- }
}
return ret;
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
index 602c37cbe177..89bd15a4d26a 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
@@ -658,7 +658,7 @@ static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
static int c8sectpfe_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *child, *np = dev->of_node;
+ struct device_node *np = dev->of_node;
struct c8sectpfei *fei;
struct resource *res;
int ret, index = 0;
@@ -742,17 +742,15 @@ static int c8sectpfe_probe(struct platform_device *pdev)
return PTR_ERR(fei->pinctrl);
}
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
struct device_node *i2c_bus;
fei->channel_data[index] = devm_kzalloc(dev,
sizeof(struct channel_info),
GFP_KERNEL);
- if (!fei->channel_data[index]) {
- ret = -ENOMEM;
- goto err_node_put;
- }
+ if (!fei->channel_data[index])
+ return -ENOMEM;
tsin = fei->channel_data[index];
@@ -761,7 +759,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
if (ret) {
dev_err(&pdev->dev, "No tsin_num found\n");
- goto err_node_put;
+ return ret;
}
/* sanity check value */
@@ -769,8 +767,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)",
tsin->tsin_id, fei->hw_stats.num_ib);
- ret = -EINVAL;
- goto err_node_put;
+ return -EINVAL;
}
tsin->invert_ts_clk = of_property_read_bool(child,
@@ -786,22 +783,20 @@ static int c8sectpfe_probe(struct platform_device *pdev)
&tsin->dvb_card);
if (ret) {
dev_err(&pdev->dev, "No dvb-card found\n");
- goto err_node_put;
+ return ret;
}
i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
if (!i2c_bus) {
dev_err(&pdev->dev, "No i2c-bus found\n");
- ret = -ENODEV;
- goto err_node_put;
+ return -ENODEV;
}
tsin->i2c_adapter =
of_find_i2c_adapter_by_node(i2c_bus);
of_node_put(i2c_bus);
if (!tsin->i2c_adapter) {
dev_err(&pdev->dev, "No i2c adapter found\n");
- ret = -ENODEV;
- goto err_node_put;
+ return -ENODEV;
}
/* Acquire reset GPIO and activate it */
@@ -813,7 +808,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
if (ret && ret != -EBUSY) {
dev_err(dev, "Can't request tsin%d reset gpio\n",
fei->channel_data[index]->tsin_id);
- goto err_node_put;
+ return ret;
}
if (!ret) {
@@ -855,10 +850,6 @@ static int c8sectpfe_probe(struct platform_device *pdev)
c8sectpfe_debugfs_init(fei);
return 0;
-
-err_node_put:
- of_node_put(child);
- return ret;
}
static void c8sectpfe_remove(struct platform_device *pdev)
@@ -897,16 +888,15 @@ static void c8sectpfe_remove(struct platform_device *pdev)
static int configure_channels(struct c8sectpfei *fei)
{
int index = 0, ret;
- struct device_node *child, *np = fei->dev->of_node;
+ struct device_node *np = fei->dev->of_node;
/* iterate round each tsin and configure memdma descriptor and IB hw */
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
ret = configure_memdma_and_inputblock(fei,
fei->channel_data[index]);
if (ret) {
dev_err(fei->dev,
"configure_memdma_and_inputblock failed\n");
- of_node_put(child);
goto err_unmap;
}
index++;
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 6412a00be8ea..b628d6e081db 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -619,6 +619,7 @@ static void ti_csi2rx_dma_callback(void *param)
if (ti_csi2rx_start_dma(csi, buf)) {
dev_err(csi->dev, "Failed to queue the next buffer for DMA\n");
+ list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
} else {
list_move_tail(&buf->list, &dma->submitted);
@@ -895,6 +896,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
q->dev = dmaengine_get_dma_device(csi->dma.chan);
q->lock = &csi->mutex;
q->min_queued_buffers = 1;
+ q->allow_cache_hints = 1;
ret = vb2_queue_init(q);
if (ret)
diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c
index da90d7f03f82..bb8a8bd7980c 100644
--- a/drivers/media/platform/ti/vpe/vpdma.c
+++ b/drivers/media/platform/ti/vpe/vpdma.c
@@ -552,38 +552,6 @@ EXPORT_SYMBOL(vpdma_submit_descs);
static void dump_dtd(struct vpdma_dtd *dtd);
-void vpdma_update_dma_addr(struct vpdma_data *vpdma,
- struct vpdma_desc_list *list, dma_addr_t dma_addr,
- void *write_dtd, int drop, int idx)
-{
- struct vpdma_dtd *dtd = list->buf.addr;
- dma_addr_t write_desc_addr;
- int offset;
-
- dtd += idx;
- vpdma_unmap_desc_buf(vpdma, &list->buf);
-
- dtd->start_addr = dma_addr;
-
- /* Calculate write address from the offset of write_dtd from start
- * of the list->buf
- */
- offset = (void *)write_dtd - list->buf.addr;
- write_desc_addr = list->buf.dma_addr + offset;
-
- if (drop)
- dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
- 1, 1, 0);
- else
- dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
- 1, 0, 0);
-
- vpdma_map_desc_buf(vpdma, &list->buf);
-
- dump_dtd(dtd);
-}
-EXPORT_SYMBOL(vpdma_update_dma_addr);
-
void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr,
u32 width, u32 height)
{
diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h
index 393fcbb3cb40..e4d7941c6207 100644
--- a/drivers/media/platform/ti/vpe/vpdma.h
+++ b/drivers/media/platform/ti/vpe/vpdma.h
@@ -222,9 +222,6 @@ void vpdma_free_desc_list(struct vpdma_desc_list *list);
int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list,
int list_num);
bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num);
-void vpdma_update_dma_addr(struct vpdma_data *vpdma,
- struct vpdma_desc_list *list, dma_addr_t dma_addr,
- void *write_dtd, int drop, int idx);
/* VPDMA hardware list funcs */
int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv);
diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h
index edc217eed293..81328c63b796 100644
--- a/drivers/media/platform/verisilicon/hantro.h
+++ b/drivers/media/platform/verisilicon/hantro.h
@@ -323,6 +323,8 @@ struct hantro_postproc_regs {
struct hantro_reg output_fmt;
struct hantro_reg orig_width;
struct hantro_reg display_width;
+ struct hantro_reg input_width_ext;
+ struct hantro_reg input_height_ext;
};
struct hantro_vp9_decoded_buffer_info {
diff --git a/drivers/media/platform/verisilicon/hantro_g1_regs.h b/drivers/media/platform/verisilicon/hantro_g1_regs.h
index c623b3b0be18..7874d76c6898 100644
--- a/drivers/media/platform/verisilicon/hantro_g1_regs.h
+++ b/drivers/media/platform/verisilicon/hantro_g1_regs.h
@@ -350,7 +350,7 @@
#define G1_REG_PP_CONTROL_OUT_WIDTH(v) (((v) << 4) & GENMASK(14, 4))
#define G1_REG_PP_MASK1_ORIG_WIDTH G1_SWREG(88)
#define G1_REG_PP_ORIG_WIDTH(v) (((v) << 23) & GENMASK(31, 23))
-#define G1_REG_PP_DISPLAY_WIDTH G1_SWREG(92)
+#define G1_REG_PP_DISPLAY_WIDTH_IN_EXT G1_SWREG(92)
#define G1_REG_PP_FUSE G1_SWREG(99)
#endif /* HANTRO_G1_REGS_H_ */
diff --git a/drivers/media/platform/verisilicon/hantro_h264.c b/drivers/media/platform/verisilicon/hantro_h264.c
index 4e9a0ecf5c13..2414782f1eb6 100644
--- a/drivers/media/platform/verisilicon/hantro_h264.c
+++ b/drivers/media/platform/verisilicon/hantro_h264.c
@@ -325,12 +325,12 @@ static void update_dpb(struct hantro_ctx *ctx)
continue;
*cdpb = *ndpb;
- set_bit(j, used);
+ __set_bit(j, used);
break;
}
if (j == ARRAY_SIZE(ctx->h264_dec.dpb))
- set_bit(i, new);
+ __set_bit(i, new);
}
/* For entries that could not be matched, use remaining free slots. */
@@ -349,7 +349,7 @@ static void update_dpb(struct hantro_ctx *ctx)
cdpb = &ctx->h264_dec.dpb[j];
*cdpb = *ndpb;
- set_bit(j, used);
+ __set_bit(j, used);
}
}
diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c
index 9f559a13d409..e94d1ba5ef10 100644
--- a/drivers/media/platform/verisilicon/hantro_postproc.c
+++ b/drivers/media/platform/verisilicon/hantro_postproc.c
@@ -49,7 +49,9 @@ static const struct hantro_postproc_regs hantro_g1_postproc_regs = {
.input_fmt = {G1_REG_PP_CONTROL, 29, 0x7},
.output_fmt = {G1_REG_PP_CONTROL, 26, 0x7},
.orig_width = {G1_REG_PP_MASK1_ORIG_WIDTH, 23, 0x1ff},
- .display_width = {G1_REG_PP_DISPLAY_WIDTH, 0, 0xfff},
+ .display_width = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 0, 0xfff},
+ .input_width_ext = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 26, 0x7},
+ .input_height_ext = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 29, 0x7},
};
bool hantro_needs_postproc(const struct hantro_ctx *ctx,
@@ -103,6 +105,8 @@ static void hantro_postproc_g1_enable(struct hantro_ctx *ctx)
HANTRO_PP_REG_WRITE(vpu, output_height, ctx->dst_fmt.height);
HANTRO_PP_REG_WRITE(vpu, orig_width, MB_WIDTH(ctx->dst_fmt.width));
HANTRO_PP_REG_WRITE(vpu, display_width, ctx->dst_fmt.width);
+ HANTRO_PP_REG_WRITE(vpu, input_width_ext, MB_WIDTH(ctx->dst_fmt.width) >> 9);
+ HANTRO_PP_REG_WRITE(vpu, input_height_ext, MB_HEIGHT(ctx->dst_fmt.height >> 8));
}
static int down_scale_factor(struct hantro_ctx *ctx)
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index acd29fa41d2d..02673be9878e 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -17,7 +17,6 @@
#define RK3066_ACLK_MAX_FREQ (300 * 1000 * 1000)
#define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000)
-#define RK3588_ACLK_MAX_FREQ (300 * 1000 * 1000)
#define ROCKCHIP_VPU981_MIN_SIZE 64
@@ -454,13 +453,6 @@ static int rk3066_vpu_hw_init(struct hantro_dev *vpu)
return 0;
}
-static int rk3588_vpu981_hw_init(struct hantro_dev *vpu)
-{
- /* Bump ACLKs to max. possible freq. to improve performance. */
- clk_set_rate(vpu->clocks[0].clk, RK3588_ACLK_MAX_FREQ);
- return 0;
-}
-
static int rockchip_vpu_hw_init(struct hantro_dev *vpu)
{
/* Bump ACLK to max. possible freq. to improve performance. */
@@ -821,7 +813,6 @@ const struct hantro_variant rk3588_vpu981_variant = {
.codec_ops = rk3588_vpu981_codec_ops,
.irqs = rk3588_vpu981_irqs,
.num_irqs = ARRAY_SIZE(rk3588_vpu981_irqs),
- .init = rk3588_vpu981_hw_init,
.clk_names = rk3588_vpu981_vpu_clk_names,
.num_clocks = ARRAY_SIZE(rk3588_vpu981_vpu_clk_names)
};
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index 024b439feec9..30675f681410 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -450,7 +450,6 @@ static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev,
static int xvip_graph_dma_init(struct xvip_composite_device *xdev)
{
struct device_node *ports;
- struct device_node *port;
int ret = 0;
ports = of_get_child_by_name(xdev->dev->of_node, "ports");
@@ -459,12 +458,10 @@ static int xvip_graph_dma_init(struct xvip_composite_device *xdev)
return -EINVAL;
}
- for_each_child_of_node(ports, port) {
+ for_each_child_of_node_scoped(ports, port) {
ret = xvip_graph_dma_init_one(xdev, port);
- if (ret) {
- of_node_put(port);
+ if (ret)
break;
- }
}
of_node_put(ports);
diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c
index 8fc8e496e6aa..392441e0c116 100644
--- a/drivers/media/rc/ir-spi.c
+++ b/drivers/media/rc/ir-spi.c
@@ -21,13 +21,12 @@
#define IR_SPI_DRIVER_NAME "ir-spi"
#define IR_SPI_DEFAULT_FREQUENCY 38000
-#define IR_SPI_MAX_BUFSIZE 4096
+#define IR_SPI_BITS_PER_PULSE 16
struct ir_spi_data {
u32 freq;
bool negated;
- u16 tx_buf[IR_SPI_MAX_BUFSIZE];
u16 pulse;
u16 space;
@@ -43,17 +42,23 @@ static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int coun
unsigned int len = 0;
struct ir_spi_data *idata = dev->priv;
struct spi_transfer xfer;
+ u16 *tx_buf;
/* convert the pulse/space signal to raw binary signal */
for (i = 0; i < count; i++) {
- unsigned int periods;
- int j;
- u16 val;
+ buffer[i] = DIV_ROUND_CLOSEST_ULL((u64)buffer[i] * idata->freq,
+ 1000000);
+ len += buffer[i];
+ }
- periods = DIV_ROUND_CLOSEST(buffer[i] * idata->freq, 1000000);
+ tx_buf = kmalloc_array(len, sizeof(*tx_buf), GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
- if (len + periods >= IR_SPI_MAX_BUFSIZE)
- return -EINVAL;
+ len = 0;
+ for (i = 0; i < count; i++) {
+ int j;
+ u16 val;
/*
* The first value in buffer is a pulse, so that 0, 2, 4, ...
@@ -61,19 +66,19 @@ static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int coun
* contain a space duration.
*/
val = (i % 2) ? idata->space : idata->pulse;
- for (j = 0; j < periods; j++)
- idata->tx_buf[len++] = val;
+ for (j = 0; j < buffer[i]; j++)
+ tx_buf[len++] = val;
}
memset(&xfer, 0, sizeof(xfer));
- xfer.speed_hz = idata->freq * 16;
- xfer.len = len * sizeof(*idata->tx_buf);
- xfer.tx_buf = idata->tx_buf;
+ xfer.speed_hz = idata->freq * IR_SPI_BITS_PER_PULSE;
+ xfer.len = len * sizeof(*tx_buf);
+ xfer.tx_buf = tx_buf;
ret = regulator_enable(idata->regulator);
if (ret)
- return ret;
+ goto err_free_tx_buf;
ret = spi_sync_transfer(idata->spi, &xfer, 1);
if (ret)
@@ -81,6 +86,10 @@ static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int coun
regulator_disable(idata->regulator);
+err_free_tx_buf:
+
+ kfree(tx_buf);
+
return ret ? ret : count;
}
@@ -91,6 +100,9 @@ static int ir_spi_set_tx_carrier(struct rc_dev *dev, u32 carrier)
if (!carrier)
return -EINVAL;
+ if (carrier > idata->spi->max_speed_hz / IR_SPI_BITS_PER_PULSE)
+ return -EINVAL;
+
idata->freq = carrier;
return 0;
diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index e340df0b6261..f94c15ff84f7 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -244,7 +244,8 @@ static const struct v4l2_ctrl_config vivid_ctrl_u8_pixel_array = {
.min = 0x00,
.max = 0xff,
.step = 1,
- .dims = { 640 / PIXEL_ARRAY_DIV, 360 / PIXEL_ARRAY_DIV },
+ .dims = { DIV_ROUND_UP(360, PIXEL_ARRAY_DIV),
+ DIV_ROUND_UP(640, PIXEL_ARRAY_DIV) },
};
static const struct v4l2_ctrl_config vivid_ctrl_s32_array = {
diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c
index 70a4024d461e..e0f4151bda18 100644
--- a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c
+++ b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c
@@ -5,6 +5,7 @@
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*/
+#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
@@ -165,12 +166,7 @@ static const u8 vivid_cc_sequence2[30] = {
static u8 calc_parity(u8 val)
{
- unsigned i;
- unsigned tot = 0;
-
- for (i = 0; i < 7; i++)
- tot += (val & (1 << i)) ? 1 : 0;
- return val | ((tot & 1) ? 0 : 0x80);
+ return val | (parity8(val) ? 0 : 0x80);
}
static void vivid_vbi_gen_set_time_of_day(u8 *packet)
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index 84e9155b5815..2e4c1ed37cd2 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -454,8 +454,8 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
if (keep_controls)
return;
- dims[0] = roundup(dev->src_rect.width, PIXEL_ARRAY_DIV);
- dims[1] = roundup(dev->src_rect.height, PIXEL_ARRAY_DIV);
+ dims[0] = DIV_ROUND_UP(dev->src_rect.height, PIXEL_ARRAY_DIV);
+ dims[1] = DIV_ROUND_UP(dev->src_rect.width, PIXEL_ARRAY_DIV);
v4l2_ctrl_modify_dimensions(dev->pixel_array, dims);
}
diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c
index d98343fd33fe..91e177aa8136 100644
--- a/drivers/media/usb/gspca/vicam.c
+++ b/drivers/media/usb/gspca/vicam.c
@@ -227,6 +227,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
const struct ihex_binrec *rec;
const struct firmware *fw;
u8 *firmware_buf;
+ int len;
ret = request_ihex_firmware(&fw, VICAM_FIRMWARE,
&gspca_dev->dev->dev);
@@ -241,9 +242,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
goto exit;
}
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
- memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len));
+ len = be16_to_cpu(rec->len);
+ if (len > PAGE_SIZE) {
+ ret = -EINVAL;
+ break;
+ }
+ memcpy(firmware_buf, rec->data, len);
ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf,
- be16_to_cpu(rec->len));
+ len);
if (ret < 0)
break;
}
diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
index 070559b01b01..9eacc85e3f11 100644
--- a/drivers/media/usb/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
@@ -124,32 +124,12 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
else
retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf,
msgs[0].len);
- } else if (num == 2) {
- if (msgs[0].addr != msgs[1].addr) {
- v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer with conflicting target addresses\n");
- retval = -EINVAL;
- goto out;
- }
-
- if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) {
- v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with r0=%d, r1=%d\n",
- msgs[0].flags & I2C_M_RD,
- msgs[1].flags & I2C_M_RD);
- retval = -EINVAL;
- goto out;
- }
-
- /*
- * Write followed by atomic read is the only complex xfer that
- * we actually support here.
- */
+ } else {
+ /* do write-then-read */
retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len,
msgs[1].buf, msgs[1].len);
- } else {
- v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num);
}
-out:
mutex_unlock(&dev->i2c_mutex);
return retval ? retval : num;
@@ -165,10 +145,16 @@ static const struct i2c_algorithm hdpvr_algo = {
.functionality = hdpvr_functionality,
};
+/* prevent invalid 0-length usb_control_msg and support only write-then-read */
+static const struct i2c_adapter_quirks hdpvr_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_COMB_WRITE_THEN_READ,
+};
+
static const struct i2c_adapter hdpvr_i2c_adapter_template = {
.name = "Hauppauge HD PVR I2C",
.owner = THIS_MODULE,
.algo = &hdpvr_algo,
+ .quirks = &hdpvr_quirks,
};
static int hdpvr_activate_ir(struct hdpvr_device *dev)
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 5ba3d9c4b3fb..715ce1dcb304 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -232,10 +232,6 @@ static int stk1160_start_streaming(struct stk1160 *dev)
/* submit urbs and enables IRQ */
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- struct stk1160_urb *stk_urb = &dev->isoc_ctl.urb_ctl[i];
-
- dma_sync_sgtable_for_device(stk1160_get_dmadev(dev), stk_urb->sgt,
- DMA_FROM_DEVICE);
rc = usb_submit_urb(dev->isoc_ctl.urb_ctl[i].urb, GFP_KERNEL);
if (rc) {
stk1160_err("cannot submit urb[%d] (%d)\n", i, rc);
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 9cbd957ecc90..416cb74377eb 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -298,9 +298,7 @@ static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb)
static void stk1160_isoc_irq(struct urb *urb)
{
int i, rc;
- struct stk1160_urb *stk_urb = urb->context;
- struct stk1160 *dev = stk_urb->dev;
- struct device *dma_dev = stk1160_get_dmadev(dev);
+ struct stk1160 *dev = urb->context;
switch (urb->status) {
case 0:
@@ -315,10 +313,6 @@ static void stk1160_isoc_irq(struct urb *urb)
return;
}
- invalidate_kernel_vmap_range(stk_urb->transfer_buffer,
- urb->transfer_buffer_length);
- dma_sync_sgtable_for_cpu(dma_dev, stk_urb->sgt, DMA_FROM_DEVICE);
-
stk1160_process_isoc(dev, urb);
/* Reset urb buffers */
@@ -327,7 +321,6 @@ static void stk1160_isoc_irq(struct urb *urb)
urb->iso_frame_desc[i].actual_length = 0;
}
- dma_sync_sgtable_for_device(dma_dev, stk_urb->sgt, DMA_FROM_DEVICE);
rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc)
stk1160_err("urb re-submit failed (%d)\n", rc);
@@ -365,11 +358,9 @@ void stk1160_cancel_isoc(struct stk1160 *dev)
static void stk_free_urb(struct stk1160 *dev, struct stk1160_urb *stk_urb)
{
- struct device *dma_dev = stk1160_get_dmadev(dev);
-
- dma_vunmap_noncontiguous(dma_dev, stk_urb->transfer_buffer);
- dma_free_noncontiguous(dma_dev, stk_urb->urb->transfer_buffer_length,
- stk_urb->sgt, DMA_FROM_DEVICE);
+ usb_free_noncoherent(dev->udev, stk_urb->urb->transfer_buffer_length,
+ stk_urb->transfer_buffer, DMA_FROM_DEVICE,
+ stk_urb->sgt);
usb_free_urb(stk_urb->urb);
stk_urb->transfer_buffer = NULL;
@@ -410,32 +401,19 @@ void stk1160_uninit_isoc(struct stk1160 *dev)
static int stk1160_fill_urb(struct stk1160 *dev, struct stk1160_urb *stk_urb,
int sb_size, int max_packets)
{
- struct device *dma_dev = stk1160_get_dmadev(dev);
-
stk_urb->urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!stk_urb->urb)
return -ENOMEM;
- stk_urb->sgt = dma_alloc_noncontiguous(dma_dev, sb_size,
- DMA_FROM_DEVICE, GFP_KERNEL, 0);
-
- /*
- * If the buffer allocation failed, we exit but return 0 since
- * we allow the driver working with less buffers
- */
- if (!stk_urb->sgt)
- goto free_urb;
- stk_urb->transfer_buffer = dma_vmap_noncontiguous(dma_dev, sb_size,
- stk_urb->sgt);
+ stk_urb->transfer_buffer = usb_alloc_noncoherent(dev->udev, sb_size,
+ GFP_KERNEL, &stk_urb->dma,
+ DMA_FROM_DEVICE, &stk_urb->sgt);
if (!stk_urb->transfer_buffer)
- goto free_sgt;
+ goto free_urb;
- stk_urb->dma = stk_urb->sgt->sgl->dma_address;
stk_urb->dev = dev;
return 0;
-free_sgt:
- dma_free_noncontiguous(dma_dev, sb_size, stk_urb->sgt, DMA_FROM_DEVICE);
- stk_urb->sgt = NULL;
+
free_urb:
usb_free_urb(stk_urb->urb);
stk_urb->urb = NULL;
@@ -494,12 +472,13 @@ int stk1160_alloc_isoc(struct stk1160 *dev)
urb->transfer_buffer = dev->isoc_ctl.urb_ctl[i].transfer_buffer;
urb->transfer_buffer_length = sb_size;
urb->complete = stk1160_isoc_irq;
- urb->context = &dev->isoc_ctl.urb_ctl[i];
+ urb->context = dev;
urb->interval = 1;
urb->start_frame = 0;
urb->number_of_packets = max_packets;
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = dev->isoc_ctl.urb_ctl[i].dma;
+ urb->sgt = dev->isoc_ctl.urb_ctl[i].sgt;
k = 0;
for (j = 0; j < max_packets; j++) {
diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h
index 7b498d14ed7a..4cbcb0a03bab 100644
--- a/drivers/media/usb/stk1160/stk1160.h
+++ b/drivers/media/usb/stk1160/stk1160.h
@@ -16,8 +16,6 @@
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <linux/usb.h>
-#include <linux/usb/hcd.h>
#define STK1160_VERSION "0.9.5"
#define STK1160_VERSION_NUM 0x000905
@@ -195,8 +193,3 @@ void stk1160_select_input(struct stk1160 *dev);
/* Provided by stk1160-ac97.c */
void stk1160_ac97_setup(struct stk1160 *dev);
-
-static inline struct device *stk1160_get_dmadev(struct stk1160 *dev)
-{
- return bus_to_hcd(dev->udev->bus)->self.sysdev;
-}
diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c
index be22a9697197..de0328100a60 100644
--- a/drivers/media/usb/usbtv/usbtv-video.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -73,6 +73,10 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
}
if (params) {
+ if (vb2_is_busy(&usbtv->vb2q) &&
+ (usbtv->width != params->cap_width ||
+ usbtv->height != params->cap_height))
+ return -EBUSY;
usbtv->width = params->cap_width;
usbtv->height = params->cap_height;
usbtv->n_chunks = usbtv->width * usbtv->height
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 44b6513c5264..efe609d70877 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1483,14 +1483,28 @@ static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
return ~0;
}
+/*
+ * Maximum retry count to avoid spurious errors with controls. Increasing this
+ * value does no seem to produce better results in the tested hardware.
+ */
+#define MAX_QUERY_RETRIES 2
+
static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
struct v4l2_query_ext_ctrl *v4l2_ctrl)
{
if (!ctrl->cached) {
- int ret = uvc_ctrl_populate_cache(chain, ctrl);
- if (ret < 0)
+ unsigned int retries;
+ int ret;
+
+ for (retries = 0; retries < MAX_QUERY_RETRIES; retries++) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret != -EIO)
+ break;
+ }
+
+ if (ret)
return ret;
}
@@ -1567,6 +1581,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
{
struct uvc_control_mapping *master_map = NULL;
struct uvc_control *master_ctrl = NULL;
+ int ret;
memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
v4l2_ctrl->id = mapping->id;
@@ -1587,18 +1602,31 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
__uvc_find_control(ctrl->entity, mapping->master_id,
&master_map, &master_ctrl, 0, 0);
if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
+ unsigned int retries;
s32 val;
int ret;
if (WARN_ON(uvc_ctrl_mapping_is_compound(master_map)))
return -EIO;
- ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
- if (ret < 0)
- return ret;
+ for (retries = 0; retries < MAX_QUERY_RETRIES; retries++) {
+ ret = __uvc_ctrl_get(chain, master_ctrl, master_map,
+ &val);
+ if (!ret)
+ break;
+ if (ret < 0 && ret != -EIO)
+ return ret;
+ }
- if (val != mapping->master_manual)
- v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ret == -EIO) {
+ dev_warn_ratelimited(&chain->dev->udev->dev,
+ "UVC non compliance: Error %d querying master control %x (%s)\n",
+ ret, master_map->id,
+ uvc_map_get_name(master_map));
+ } else {
+ if (val != mapping->master_manual)
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ }
}
v4l2_ctrl->elem_size = uvc_mapping_v4l2_size(mapping);
@@ -1613,7 +1641,18 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
return 0;
}
- return __uvc_queryctrl_boundaries(chain, ctrl, mapping, v4l2_ctrl);
+ ret = __uvc_queryctrl_boundaries(chain, ctrl, mapping, v4l2_ctrl);
+ if (ret && !mapping->disabled) {
+ dev_warn(&chain->dev->udev->dev,
+ "UVC non compliance: permanently disabling control %x (%s), due to error %d\n",
+ mapping->id, uvc_map_get_name(mapping), ret);
+ mapping->disabled = true;
+ }
+
+ if (mapping->disabled)
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+ return 0;
}
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
@@ -1812,48 +1851,48 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
}
-static int uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
- struct uvc_fh *new_handle)
+static int uvc_ctrl_set_handle(struct uvc_control *ctrl, struct uvc_fh *handle)
{
- lockdep_assert_held(&handle->chain->ctrl_mutex);
-
- if (new_handle) {
- int ret;
+ int ret;
- if (ctrl->handle)
- dev_warn_ratelimited(&handle->stream->dev->udev->dev,
- "UVC non compliance: Setting an async control with a pending operation.");
+ lockdep_assert_held(&handle->chain->ctrl_mutex);
- if (new_handle == ctrl->handle)
- return 0;
+ if (ctrl->handle) {
+ dev_warn_ratelimited(&handle->stream->dev->udev->dev,
+ "UVC non compliance: Setting an async control with a pending operation.");
- if (ctrl->handle) {
- WARN_ON(!ctrl->handle->pending_async_ctrls);
- if (ctrl->handle->pending_async_ctrls)
- ctrl->handle->pending_async_ctrls--;
- ctrl->handle = new_handle;
- handle->pending_async_ctrls++;
+ if (ctrl->handle == handle)
return 0;
- }
-
- ret = uvc_pm_get(handle->chain->dev);
- if (ret)
- return ret;
- ctrl->handle = new_handle;
- handle->pending_async_ctrls++;
+ WARN_ON(!ctrl->handle->pending_async_ctrls);
+ if (ctrl->handle->pending_async_ctrls)
+ ctrl->handle->pending_async_ctrls--;
+ ctrl->handle = handle;
+ ctrl->handle->pending_async_ctrls++;
return 0;
}
- /* Cannot clear the handle for a control not owned by us.*/
- if (WARN_ON(ctrl->handle != handle))
+ ret = uvc_pm_get(handle->chain->dev);
+ if (ret)
+ return ret;
+
+ ctrl->handle = handle;
+ ctrl->handle->pending_async_ctrls++;
+ return 0;
+}
+
+static int uvc_ctrl_clear_handle(struct uvc_control *ctrl)
+{
+ lockdep_assert_held(&ctrl->handle->chain->ctrl_mutex);
+
+ if (WARN_ON(!ctrl->handle->pending_async_ctrls)) {
+ ctrl->handle = NULL;
return -EINVAL;
+ }
+ ctrl->handle->pending_async_ctrls--;
+ uvc_pm_put(ctrl->handle->chain->dev);
ctrl->handle = NULL;
- if (WARN_ON(!handle->pending_async_ctrls))
- return -EINVAL;
- handle->pending_async_ctrls--;
- uvc_pm_put(handle->chain->dev);
return 0;
}
@@ -1871,7 +1910,7 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
handle = ctrl->handle;
if (handle)
- uvc_ctrl_set_handle(handle, ctrl, NULL);
+ uvc_ctrl_clear_handle(ctrl);
list_for_each_entry(mapping, &ctrl->info.mappings, list) {
s32 value;
@@ -2033,18 +2072,24 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
goto done;
}
- list_add_tail(&sev->node, &mapping->ev_subs);
if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) {
struct v4l2_event ev;
u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
s32 val = 0;
+ ret = uvc_pm_get(handle->chain->dev);
+ if (ret)
+ goto done;
+
if (uvc_ctrl_mapping_is_compound(mapping) ||
__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
changes |= V4L2_EVENT_CTRL_CH_VALUE;
uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
changes);
+
+ uvc_pm_put(handle->chain->dev);
+
/*
* Mark the queue as active, allowing this initial event to be
* accepted.
@@ -2053,6 +2098,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
v4l2_event_queue_fh(sev->fh, &ev);
}
+ list_add_tail(&sev->node, &mapping->ev_subs);
+
done:
mutex_unlock(&handle->chain->ctrl_mutex);
return ret;
@@ -2161,7 +2208,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
if (!rollback && handle && !ret &&
ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
- ret = uvc_ctrl_set_handle(handle, ctrl, handle);
+ ret = uvc_ctrl_set_handle(ctrl, handle);
if (ret < 0 && !rollback) {
if (err_ctrl)
@@ -3271,7 +3318,7 @@ void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
for (unsigned int i = 0; i < entity->ncontrols; ++i) {
if (entity->controls[i].handle != handle)
continue;
- uvc_ctrl_set_handle(handle, &entity->controls[i], NULL);
+ uvc_ctrl_clear_handle(&entity->controls[i]);
}
}
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index da24a655ab68..775bede0d93d 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -344,6 +344,9 @@ static int uvc_parse_format(struct uvc_device *dev,
u8 ftype;
int ret;
+ if (buflen < 4)
+ return -EINVAL;
+
format->type = buffer[2];
format->index = buffer[3];
format->frames = frames;
@@ -1866,7 +1869,7 @@ static int uvc_scan_device(struct uvc_device *dev)
if (list_empty(&dev->chains)) {
dev_info(&dev->udev->dev, "No valid video chain found.\n");
- return -1;
+ return -ENODEV;
}
/* Add GPIO entity to the first chain. */
@@ -1958,31 +1961,7 @@ static void uvc_unregister_video(struct uvc_device *dev)
if (!video_is_registered(&stream->vdev))
continue;
- /*
- * For stream->vdev we follow the same logic as:
- * vb2_video_unregister_device().
- */
-
- /* 1. Take a reference to vdev */
- get_device(&stream->vdev.dev);
-
- /* 2. Ensure that no new ioctls can be called. */
- video_unregister_device(&stream->vdev);
-
- /* 3. Wait for old ioctls to finish. */
- mutex_lock(&stream->mutex);
-
- /* 4. Stop streaming. */
- uvc_queue_release(&stream->queue);
-
- mutex_unlock(&stream->mutex);
-
- put_device(&stream->vdev.dev);
-
- /*
- * For stream->meta.vdev we can directly call:
- * vb2_video_unregister_device().
- */
+ vb2_video_unregister_device(&stream->vdev);
vb2_video_unregister_device(&stream->meta.vdev);
/*
@@ -2030,6 +2009,8 @@ int uvc_register_video_device(struct uvc_device *dev,
vdev->ioctl_ops = ioctl_ops;
vdev->release = uvc_release;
vdev->prio = &stream->chain->prio;
+ vdev->queue = &queue->queue;
+ vdev->lock = &queue->mutex;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vdev->vfl_dir = VFL_DIR_TX;
else
@@ -2239,7 +2220,6 @@ static int uvc_probe(struct usb_interface *intf,
/* Parse the Video Class control descriptor. */
ret = uvc_parse_control(dev);
if (ret < 0) {
- ret = -ENODEV;
uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n");
goto error;
}
@@ -2275,22 +2255,19 @@ static int uvc_probe(struct usb_interface *intf,
goto error;
/* Scan the device for video chains. */
- if (uvc_scan_device(dev) < 0) {
- ret = -ENODEV;
+ ret = uvc_scan_device(dev);
+ if (ret < 0)
goto error;
- }
/* Initialize controls. */
- if (uvc_ctrl_init_device(dev) < 0) {
- ret = -ENODEV;
+ ret = uvc_ctrl_init_device(dev);
+ if (ret < 0)
goto error;
- }
/* Register video device nodes. */
- if (uvc_register_chains(dev) < 0) {
- ret = -ENODEV;
+ ret = uvc_register_chains(dev);
+ if (ret < 0)
goto error;
- }
#ifdef CONFIG_MEDIA_CONTROLLER
/* Register the media device node */
@@ -2316,6 +2293,13 @@ static int uvc_probe(struct usb_interface *intf,
goto error;
}
+ ret = uvc_meta_init(dev);
+ if (ret < 0) {
+ dev_err(&dev->udev->dev,
+ "Error initializing the metadata formats (%d)\n", ret);
+ goto error;
+ }
+
if (dev->quirks & UVC_QUIRK_NO_RESET_RESUME)
udev->quirks &= ~USB_QUIRK_RESET_RESUME;
@@ -2398,9 +2382,12 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
list_for_each_entry(stream, &dev->streams, list) {
if (stream->intf == intf) {
ret = uvc_video_resume(stream, reset);
- if (ret < 0)
- uvc_queue_streamoff(&stream->queue,
- stream->queue.queue.type);
+ if (ret < 0) {
+ mutex_lock(&stream->queue.mutex);
+ vb2_streamoff(&stream->queue.queue,
+ stream->queue.queue.type);
+ mutex_unlock(&stream->queue.mutex);
+ }
return ret;
}
}
@@ -2514,6 +2501,15 @@ static const struct uvc_device_info uvc_quirk_force_y8 = {
* Sort these by vendor/product ID.
*/
static const struct usb_device_id uvc_ids[] = {
+ /* HP Webcam HD 2300 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x03f0,
+ .idProduct = 0xe207,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
/* Quanta ACER HD User Facing */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c
index 82de7781f5b6..229e08ff323e 100644
--- a/drivers/media/usb/uvc/uvc_metadata.c
+++ b/drivers/media/usb/uvc/uvc_metadata.c
@@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
+#include <linux/usb/uvc.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
@@ -63,15 +64,20 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *fh,
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_device *dev = stream->dev;
struct v4l2_meta_format *fmt = &format->fmt.meta;
- u32 fmeta = fmt->dataformat;
+ u32 fmeta = V4L2_META_FMT_UVC;
if (format->type != vfh->vdev->queue->type)
return -EINVAL;
+ for (unsigned int i = 0; i < dev->nmeta_formats; i++)
+ if (dev->meta_formats[i] == fmt->dataformat) {
+ fmeta = fmt->dataformat;
+ break;
+ }
+
memset(fmt, 0, sizeof(*fmt));
- fmt->dataformat = fmeta == dev->info->meta_format
- ? fmeta : V4L2_META_FMT_UVC;
+ fmt->dataformat = fmeta;
fmt->buffersize = UVC_METADATA_BUF_SIZE;
return 0;
@@ -96,7 +102,7 @@ static int uvc_meta_v4l2_set_format(struct file *file, void *fh,
*/
mutex_lock(&stream->mutex);
- if (uvc_queue_allocated(&stream->queue))
+ if (vb2_is_busy(&stream->meta.queue.queue))
ret = -EBUSY;
else
stream->meta.format = fmt->dataformat;
@@ -112,17 +118,19 @@ static int uvc_meta_v4l2_enum_formats(struct file *file, void *fh,
struct v4l2_fh *vfh = file->private_data;
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_device *dev = stream->dev;
- u32 index = fdesc->index;
+ u32 i = fdesc->index;
+
+ if (fdesc->type != vfh->vdev->queue->type)
+ return -EINVAL;
- if (fdesc->type != vfh->vdev->queue->type ||
- index > 1U || (index && !dev->info->meta_format))
+ if (i >= dev->nmeta_formats)
return -EINVAL;
memset(fdesc, 0, sizeof(*fdesc));
fdesc->type = vfh->vdev->queue->type;
- fdesc->index = index;
- fdesc->pixelformat = index ? dev->info->meta_format : V4L2_META_FMT_UVC;
+ fdesc->index = i;
+ fdesc->pixelformat = dev->meta_formats[i];
return 0;
}
@@ -156,6 +164,71 @@ static const struct v4l2_file_operations uvc_meta_fops = {
.mmap = vb2_fop_mmap,
};
+static struct uvc_entity *uvc_meta_find_msxu(struct uvc_device *dev)
+{
+ static const u8 uvc_msxu_guid[16] = UVC_GUID_MSXU_1_5;
+ struct uvc_entity *entity;
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (!memcmp(entity->guid, uvc_msxu_guid, sizeof(entity->guid)))
+ return entity;
+ }
+
+ return NULL;
+}
+
+#define MSXU_CONTROL_METADATA 0x9
+static int uvc_meta_detect_msxu(struct uvc_device *dev)
+{
+ u32 *data __free(kfree) = NULL;
+ struct uvc_entity *entity;
+ int ret;
+
+ entity = uvc_meta_find_msxu(dev);
+ if (!entity)
+ return 0;
+
+ /*
+ * USB requires buffers aligned in a special way, simplest way is to
+ * make sure that query_ctrl will work is to kmalloc() them.
+ */
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Check if the metadata is already enabled. */
+ ret = uvc_query_ctrl(dev, UVC_GET_CUR, entity->id, dev->intfnum,
+ MSXU_CONTROL_METADATA, data, sizeof(*data));
+ if (ret)
+ return 0;
+
+ if (*data) {
+ dev->quirks |= UVC_QUIRK_MSXU_META;
+ return 0;
+ }
+
+ /*
+ * We have seen devices that require 1 to enable the metadata, others
+ * requiring a value != 1 and others requiring a value >1. Luckily for
+ * us, the value from GET_MAX seems to work all the time.
+ */
+ ret = uvc_query_ctrl(dev, UVC_GET_MAX, entity->id, dev->intfnum,
+ MSXU_CONTROL_METADATA, data, sizeof(*data));
+ if (ret || !*data)
+ return 0;
+
+ /*
+ * If we can set MSXU_CONTROL_METADATA, the device will report
+ * metadata.
+ */
+ ret = uvc_query_ctrl(dev, UVC_SET_CUR, entity->id, dev->intfnum,
+ MSXU_CONTROL_METADATA, data, sizeof(*data));
+ if (!ret)
+ dev->quirks |= UVC_QUIRK_MSXU_META;
+
+ return 0;
+}
+
int uvc_meta_register(struct uvc_streaming *stream)
{
struct uvc_device *dev = stream->dev;
@@ -164,13 +237,32 @@ int uvc_meta_register(struct uvc_streaming *stream)
stream->meta.format = V4L2_META_FMT_UVC;
- /*
- * The video interface queue uses manual locking and thus does not set
- * the queue pointer. Set it manually here.
- */
- vdev->queue = &queue->queue;
-
return uvc_register_video_device(dev, stream, vdev, queue,
V4L2_BUF_TYPE_META_CAPTURE,
&uvc_meta_fops, &uvc_meta_ioctl_ops);
}
+
+int uvc_meta_init(struct uvc_device *dev)
+{
+ unsigned int i = 0;
+ int ret;
+
+ ret = uvc_meta_detect_msxu(dev);
+ if (ret)
+ return ret;
+
+ dev->meta_formats[i++] = V4L2_META_FMT_UVC;
+
+ if (dev->info->meta_format &&
+ !WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC))
+ dev->meta_formats[i++] = dev->info->meta_format;
+
+ if (dev->quirks & UVC_QUIRK_MSXU_META &&
+ !WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC_MSXU_1_5))
+ dev->meta_formats[i++] = V4L2_META_FMT_UVC_MSXU_1_5;
+
+ /* IMPORTANT: for new meta-formats update UVC_MAX_META_DATA_FORMATS. */
+ dev->nmeta_formats = i;
+
+ return 0;
+}
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 2ee142621042..790184c9843d 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -42,13 +42,15 @@ static inline struct uvc_buffer *uvc_vbuf_to_buffer(struct vb2_v4l2_buffer *buf)
*
* This function must be called with the queue spinlock held.
*/
-static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
- enum uvc_buffer_state state)
+static void __uvc_queue_return_buffers(struct uvc_video_queue *queue,
+ enum uvc_buffer_state state)
{
enum vb2_buffer_state vb2_state = state == UVC_BUF_STATE_ERROR
? VB2_BUF_STATE_ERROR
: VB2_BUF_STATE_QUEUED;
+ lockdep_assert_held(&queue->irqlock);
+
while (!list_empty(&queue->irqqueue)) {
struct uvc_buffer *buf = list_first_entry(&queue->irqqueue,
struct uvc_buffer,
@@ -59,6 +61,14 @@ static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
}
}
+static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
+ enum uvc_buffer_state state)
+{
+ spin_lock_irq(&queue->irqlock);
+ __uvc_queue_return_buffers(queue, state);
+ spin_unlock_irq(&queue->irqlock);
+}
+
/* -----------------------------------------------------------------------------
* videobuf2 queue operations
*/
@@ -157,7 +167,7 @@ static void uvc_buffer_finish(struct vb2_buffer *vb)
uvc_video_clock_update(stream, vbuf, buf);
}
-static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
+static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_streaming *stream = uvc_queue_to_stream(queue);
@@ -165,31 +175,44 @@ static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
lockdep_assert_irqs_enabled();
+ ret = uvc_pm_get(stream->dev);
+ if (ret)
+ return ret;
+
queue->buf_used = 0;
ret = uvc_video_start_streaming(stream);
if (ret == 0)
return 0;
- spin_lock_irq(&queue->irqlock);
+ uvc_pm_put(stream->dev);
+
uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
- spin_unlock_irq(&queue->irqlock);
return ret;
}
-static void uvc_stop_streaming(struct vb2_queue *vq)
+static void uvc_stop_streaming_video(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_streaming *stream = uvc_queue_to_stream(queue);
lockdep_assert_irqs_enabled();
- if (vq->type != V4L2_BUF_TYPE_META_CAPTURE)
- uvc_video_stop_streaming(uvc_queue_to_stream(queue));
+ uvc_video_stop_streaming(uvc_queue_to_stream(queue));
+
+ uvc_pm_put(stream->dev);
+
+ uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
+}
+
+static void uvc_stop_streaming_meta(struct vb2_queue *vq)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+
+ lockdep_assert_irqs_enabled();
- spin_lock_irq(&queue->irqlock);
uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
- spin_unlock_irq(&queue->irqlock);
}
static const struct vb2_ops uvc_queue_qops = {
@@ -197,15 +220,20 @@ static const struct vb2_ops uvc_queue_qops = {
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
.buf_finish = uvc_buffer_finish,
- .start_streaming = uvc_start_streaming,
- .stop_streaming = uvc_stop_streaming,
+ .start_streaming = uvc_start_streaming_video,
+ .stop_streaming = uvc_stop_streaming_video,
};
static const struct vb2_ops uvc_meta_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
- .stop_streaming = uvc_stop_streaming,
+ /*
+ * .start_streaming is not provided here. Metadata relies on video
+ * streaming being active. If video isn't streaming, then no metadata
+ * will arrive either.
+ */
+ .stop_streaming = uvc_stop_streaming_meta,
};
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
@@ -242,154 +270,11 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
return 0;
}
-void uvc_queue_release(struct uvc_video_queue *queue)
-{
- mutex_lock(&queue->mutex);
- vb2_queue_release(&queue->queue);
- mutex_unlock(&queue->mutex);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 queue operations
- */
-
-int uvc_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_reqbufs(&queue->queue, rb);
- mutex_unlock(&queue->mutex);
-
- return ret ? ret : rb->count;
-}
-
-int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_querybuf(&queue->queue, buf);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *cb)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_create_bufs(&queue->queue, cb);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct media_device *mdev, struct v4l2_buffer *buf)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_qbuf(&queue->queue, mdev, buf);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_expbuf(&queue->queue, exp);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
- int nonblocking)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_streamon(&queue->queue, type);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_streamoff(&queue->queue, type);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
-{
- return vb2_mmap(&queue->queue, vma);
-}
-
-#ifndef CONFIG_MMU
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff)
-{
- return vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
-}
-#endif
-
-__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait)
-{
- __poll_t ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_poll(&queue->queue, file, wait);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
/* -----------------------------------------------------------------------------
*
*/
/*
- * Check if buffers have been allocated.
- */
-int uvc_queue_allocated(struct uvc_video_queue *queue)
-{
- int allocated;
-
- mutex_lock(&queue->mutex);
- allocated = vb2_is_busy(&queue->queue);
- mutex_unlock(&queue->mutex);
-
- return allocated;
-}
-
-/*
* Cancel the video buffers queue.
*
* Cancelling the queue marks all buffers on the irq queue as erroneous,
@@ -406,7 +291,7 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
- uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
+ __uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
/*
* This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_buffer_queue and the disconnection event that
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 668a4e9d772c..160f9cf6e6db 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -47,8 +47,6 @@ void uvc_pm_put(struct uvc_device *dev)
usb_autopm_put_interface(dev->intf);
}
-static int uvc_acquire_privileges(struct uvc_fh *handle);
-
static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain,
struct uvc_control_mapping *map,
const struct uvc_xu_control_mapping *xmap)
@@ -436,10 +434,6 @@ static int uvc_ioctl_s_fmt(struct file *file, void *fh,
const struct uvc_frame *frame;
int ret;
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
if (fmt->type != stream->type)
return -EINVAL;
@@ -448,8 +442,7 @@ static int uvc_ioctl_s_fmt(struct file *file, void *fh,
return ret;
mutex_lock(&stream->mutex);
-
- if (uvc_queue_allocated(&stream->queue)) {
+ if (vb2_is_busy(&stream->queue.queue)) {
ret = -EBUSY;
goto done;
}
@@ -513,10 +506,6 @@ static int uvc_ioctl_s_parm(struct file *file, void *fh,
unsigned int i;
int ret;
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
if (parm->type != stream->type)
return -EINVAL;
@@ -594,63 +583,6 @@ static int uvc_ioctl_s_parm(struct file *file, void *fh,
}
/* ------------------------------------------------------------------------
- * Privilege management
- */
-
-/*
- * Privilege management is the multiple-open implementation basis. The current
- * implementation is completely transparent for the end-user and doesn't
- * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
- * Those ioctls enable finer control on the device (by making possible for a
- * user to request exclusive access to a device), but are not mature yet.
- * Switching to the V4L2 priority mechanism might be considered in the future
- * if this situation changes.
- *
- * Each open instance of a UVC device can either be in a privileged or
- * unprivileged state. Only a single instance can be in a privileged state at
- * a given time. Trying to perform an operation that requires privileges will
- * automatically acquire the required privileges if possible, or return -EBUSY
- * otherwise. Privileges are dismissed when closing the instance or when
- * freeing the video buffers using VIDIOC_REQBUFS.
- *
- * Operations that require privileges are:
- *
- * - VIDIOC_S_INPUT
- * - VIDIOC_S_PARM
- * - VIDIOC_S_FMT
- * - VIDIOC_CREATE_BUFS
- * - VIDIOC_REQBUFS
- */
-static int uvc_acquire_privileges(struct uvc_fh *handle)
-{
- /* Always succeed if the handle is already privileged. */
- if (handle->state == UVC_HANDLE_ACTIVE)
- return 0;
-
- /* Check if the device already has a privileged handle. */
- if (atomic_inc_return(&handle->stream->active) != 1) {
- atomic_dec(&handle->stream->active);
- return -EBUSY;
- }
-
- handle->state = UVC_HANDLE_ACTIVE;
- return 0;
-}
-
-static void uvc_dismiss_privileges(struct uvc_fh *handle)
-{
- if (handle->state == UVC_HANDLE_ACTIVE)
- atomic_dec(&handle->stream->active);
-
- handle->state = UVC_HANDLE_PASSIVE;
-}
-
-static int uvc_has_privileges(struct uvc_fh *handle)
-{
- return handle->state == UVC_HANDLE_ACTIVE;
-}
-
-/* ------------------------------------------------------------------------
* V4L2 file operations
*/
@@ -671,7 +603,6 @@ static int uvc_v4l2_open(struct file *file)
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
handle->stream = stream;
- handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
return 0;
@@ -686,19 +617,8 @@ static int uvc_v4l2_release(struct file *file)
uvc_ctrl_cleanup_fh(handle);
- /* Only free resources if this is a privileged handle. */
- if (uvc_has_privileges(handle))
- uvc_queue_release(&stream->queue);
-
- if (handle->is_streaming)
- uvc_pm_put(stream->dev);
-
/* Release the file handle. */
- uvc_dismiss_privileges(handle);
- v4l2_fh_del(&handle->vfh);
- v4l2_fh_exit(&handle->vfh);
- kfree(handle);
- file->private_data = NULL;
+ vb2_fop_release(file);
return 0;
}
@@ -753,140 +673,6 @@ static int uvc_ioctl_try_fmt(struct file *file, void *fh,
return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
}
-static int uvc_ioctl_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *rb)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- mutex_lock(&stream->mutex);
- ret = uvc_request_buffers(&stream->queue, rb);
- mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
-
- if (ret == 0)
- uvc_dismiss_privileges(handle);
-
- return 0;
-}
-
-static int uvc_ioctl_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_query_buffer(&stream->queue, buf);
-}
-
-static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_queue_buffer(&stream->queue,
- stream->vdev.v4l2_dev->mdev, buf);
-}
-
-static int uvc_ioctl_expbuf(struct file *file, void *fh,
- struct v4l2_exportbuffer *exp)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_export_buffer(&stream->queue, exp);
-}
-
-static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_dequeue_buffer(&stream->queue, buf,
- file->f_flags & O_NONBLOCK);
-}
-
-static int uvc_ioctl_create_bufs(struct file *file, void *fh,
- struct v4l2_create_buffers *cb)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- return uvc_create_buffers(&stream->queue, cb);
-}
-
-static int uvc_ioctl_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- guard(mutex)(&stream->mutex);
-
- if (handle->is_streaming)
- return 0;
-
- ret = uvc_queue_streamon(&stream->queue, type);
- if (ret)
- return ret;
-
- ret = uvc_pm_get(stream->dev);
- if (ret) {
- uvc_queue_streamoff(&stream->queue, type);
- return ret;
- }
- handle->is_streaming = true;
-
- return 0;
-}
-
-static int uvc_ioctl_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- guard(mutex)(&stream->mutex);
-
- uvc_queue_streamoff(&stream->queue, type);
- if (handle->is_streaming) {
- handle->is_streaming = false;
- uvc_pm_put(stream->dev);
- }
-
- return 0;
-}
-
static int uvc_ioctl_enum_input(struct file *file, void *fh,
struct v4l2_input *input)
{
@@ -961,13 +747,13 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
{
struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
struct uvc_video_chain *chain = handle->chain;
u8 *buf;
int ret;
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
+ if (vb2_is_busy(&stream->queue.queue))
+ return -EBUSY;
if (chain->selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
@@ -1381,11 +1167,9 @@ static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
-DEFINE_FREE(uvc_pm_put, struct uvc_device *, if (_T) uvc_pm_put(_T))
static long uvc_v4l2_compat_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct uvc_device *uvc_device __free(uvc_pm_put) = NULL;
struct uvc_fh *handle = file->private_data;
union {
struct uvc_xu_control_mapping xmap;
@@ -1398,38 +1182,38 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
if (ret)
return ret;
- uvc_device = handle->stream->dev;
-
switch (cmd) {
case UVCIOC_CTRL_MAP32:
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
if (ret)
- return ret;
+ break;
ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap);
if (ret)
- return ret;
+ break;
ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
if (ret)
- return ret;
-
+ break;
break;
case UVCIOC_CTRL_QUERY32:
ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
if (ret)
- return ret;
+ break;
ret = uvc_xu_ctrl_query(handle->chain, &karg.xqry);
if (ret)
- return ret;
+ break;
ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
if (ret)
- return ret;
+ break;
break;
default:
- return -ENOIOCTLCMD;
+ ret = -ENOIOCTLCMD;
+ break;
}
+ uvc_pm_put(handle->stream->dev);
+
return ret;
}
#endif
@@ -1438,81 +1222,37 @@ static long uvc_v4l2_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct uvc_fh *handle = file->private_data;
+ unsigned int converted_cmd = v4l2_translate_cmd(cmd);
int ret;
- /* The following IOCTLs do not need to turn on the camera. */
- switch (cmd) {
- case VIDIOC_CREATE_BUFS:
- case VIDIOC_DQBUF:
- case VIDIOC_ENUM_FMT:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- case VIDIOC_ENUM_FRAMESIZES:
- case VIDIOC_ENUMINPUT:
- case VIDIOC_EXPBUF:
- case VIDIOC_G_FMT:
- case VIDIOC_G_PARM:
- case VIDIOC_G_SELECTION:
- case VIDIOC_QBUF:
- case VIDIOC_QUERYCAP:
- case VIDIOC_REQBUFS:
- case VIDIOC_SUBSCRIBE_EVENT:
- case VIDIOC_UNSUBSCRIBE_EVENT:
- return video_ioctl2(file, cmd, arg);
- }
-
- ret = uvc_pm_get(handle->stream->dev);
- if (ret)
+ /* The following IOCTLs need to turn on the camera. */
+ switch (converted_cmd) {
+ case UVCIOC_CTRL_MAP:
+ case UVCIOC_CTRL_QUERY:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_QUERY_EXT_CTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_S_FMT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_PARM:
+ case VIDIOC_TRY_EXT_CTRLS:
+ case VIDIOC_TRY_FMT:
+ ret = uvc_pm_get(handle->stream->dev);
+ if (ret)
+ return ret;
+ ret = video_ioctl2(file, cmd, arg);
+ uvc_pm_put(handle->stream->dev);
return ret;
+ }
- ret = video_ioctl2(file, cmd, arg);
-
- uvc_pm_put(handle->stream->dev);
- return ret;
-}
-
-static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_mmap(&stream->queue, vma);
-}
-
-static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_poll(&stream->queue, file, wait);
-}
-
-#ifndef CONFIG_MMU
-static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
+ /* The other IOCTLs can run with the camera off. */
+ return video_ioctl2(file, cmd, arg);
}
-#endif
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt,
@@ -1526,14 +1266,15 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt,
.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt,
.vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt,
- .vidioc_reqbufs = uvc_ioctl_reqbufs,
- .vidioc_querybuf = uvc_ioctl_querybuf,
- .vidioc_qbuf = uvc_ioctl_qbuf,
- .vidioc_expbuf = uvc_ioctl_expbuf,
- .vidioc_dqbuf = uvc_ioctl_dqbuf,
- .vidioc_create_bufs = uvc_ioctl_create_bufs,
- .vidioc_streamon = uvc_ioctl_streamon,
- .vidioc_streamoff = uvc_ioctl_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_input = uvc_ioctl_enum_input,
.vidioc_g_input = uvc_ioctl_g_input,
.vidioc_s_input = uvc_ioctl_s_input,
@@ -1558,11 +1299,10 @@ const struct v4l2_file_operations uvc_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
- .read = uvc_v4l2_read,
- .mmap = uvc_v4l2_mmap,
- .poll = uvc_v4l2_poll,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
#ifndef CONFIG_MMU
- .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+ .get_unmapped_area = vb2_fop_get_unmapped_area,
#endif
};
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index e3567aeb0007..a5013a7fbca4 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -262,6 +262,15 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
ctrl->dwMaxPayloadTransferSize = bandwidth;
}
+
+ if (stream->intf->num_altsetting > 1 &&
+ ctrl->dwMaxPayloadTransferSize > stream->maxpsize) {
+ dev_warn_ratelimited(&stream->intf->dev,
+ "UVC non compliance: the max payload transmission size (%u) exceeds the size of the ep max packet (%u). Using the max size.\n",
+ ctrl->dwMaxPayloadTransferSize,
+ stream->maxpsize);
+ ctrl->dwMaxPayloadTransferSize = stream->maxpsize;
+ }
}
static size_t uvc_video_ctrl_size(struct uvc_streaming *stream)
@@ -1275,20 +1284,6 @@ static inline enum dma_data_direction uvc_stream_dir(
return DMA_TO_DEVICE;
}
-static inline struct device *uvc_stream_to_dmadev(struct uvc_streaming *stream)
-{
- return bus_to_hcd(stream->dev->udev->bus)->self.sysdev;
-}
-
-static int uvc_submit_urb(struct uvc_urb *uvc_urb, gfp_t mem_flags)
-{
- /* Sync DMA. */
- dma_sync_sgtable_for_device(uvc_stream_to_dmadev(uvc_urb->stream),
- uvc_urb->sgt,
- uvc_stream_dir(uvc_urb->stream));
- return usb_submit_urb(uvc_urb->urb, mem_flags);
-}
-
/*
* uvc_video_decode_data_work: Asynchronous memcpy processing
*
@@ -1310,7 +1305,7 @@ static void uvc_video_copy_data_work(struct work_struct *work)
uvc_queue_buffer_release(op->buf);
}
- ret = uvc_submit_urb(uvc_urb, GFP_KERNEL);
+ ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL);
if (ret < 0)
dev_err(&uvc_urb->stream->intf->dev,
"Failed to resubmit video URB (%d).\n", ret);
@@ -1433,12 +1428,6 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
if (!meta_buf || length == 2)
return;
- if (meta_buf->length - meta_buf->bytesused <
- length + sizeof(meta->ns) + sizeof(meta->sof)) {
- meta_buf->error = 1;
- return;
- }
-
has_pts = mem[1] & UVC_STREAM_PTS;
has_scr = mem[1] & UVC_STREAM_SCR;
@@ -1459,6 +1448,12 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
!memcmp(scr, stream->clock.last_scr, 6)))
return;
+ if (meta_buf->length - meta_buf->bytesused <
+ length + sizeof(meta->ns) + sizeof(meta->sof)) {
+ meta_buf->error = 1;
+ return;
+ }
+
meta = (struct uvc_meta_buf *)((u8 *)meta_buf->mem + meta_buf->bytesused);
local_irq_save(flags);
time = uvc_video_get_time();
@@ -1736,12 +1731,6 @@ static void uvc_video_complete(struct urb *urb)
/* Re-initialise the URB async work. */
uvc_urb->async_operations = 0;
- /* Sync DMA and invalidate vmap range. */
- dma_sync_sgtable_for_cpu(uvc_stream_to_dmadev(uvc_urb->stream),
- uvc_urb->sgt, uvc_stream_dir(stream));
- invalidate_kernel_vmap_range(uvc_urb->buffer,
- uvc_urb->stream->urb_size);
-
/*
* Process the URB headers, and optionally queue expensive memcpy tasks
* to be deferred to a work queue.
@@ -1750,7 +1739,7 @@ static void uvc_video_complete(struct urb *urb)
/* If no async work is needed, resubmit the URB immediately. */
if (!uvc_urb->async_operations) {
- ret = uvc_submit_urb(uvc_urb, GFP_ATOMIC);
+ ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC);
if (ret < 0)
dev_err(&stream->intf->dev,
"Failed to resubmit video URB (%d).\n", ret);
@@ -1765,17 +1754,15 @@ static void uvc_video_complete(struct urb *urb)
*/
static void uvc_free_urb_buffers(struct uvc_streaming *stream)
{
- struct device *dma_dev = uvc_stream_to_dmadev(stream);
+ struct usb_device *udev = stream->dev->udev;
struct uvc_urb *uvc_urb;
for_each_uvc_urb(uvc_urb, stream) {
if (!uvc_urb->buffer)
continue;
- dma_vunmap_noncontiguous(dma_dev, uvc_urb->buffer);
- dma_free_noncontiguous(dma_dev, stream->urb_size, uvc_urb->sgt,
- uvc_stream_dir(stream));
-
+ usb_free_noncoherent(udev, stream->urb_size, uvc_urb->buffer,
+ uvc_stream_dir(stream), uvc_urb->sgt);
uvc_urb->buffer = NULL;
uvc_urb->sgt = NULL;
}
@@ -1786,26 +1773,13 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
static bool uvc_alloc_urb_buffer(struct uvc_streaming *stream,
struct uvc_urb *uvc_urb, gfp_t gfp_flags)
{
- struct device *dma_dev = uvc_stream_to_dmadev(stream);
-
- uvc_urb->sgt = dma_alloc_noncontiguous(dma_dev, stream->urb_size,
- uvc_stream_dir(stream),
- gfp_flags, 0);
- if (!uvc_urb->sgt)
- return false;
- uvc_urb->dma = uvc_urb->sgt->sgl->dma_address;
-
- uvc_urb->buffer = dma_vmap_noncontiguous(dma_dev, stream->urb_size,
- uvc_urb->sgt);
- if (!uvc_urb->buffer) {
- dma_free_noncontiguous(dma_dev, stream->urb_size,
- uvc_urb->sgt,
- uvc_stream_dir(stream));
- uvc_urb->sgt = NULL;
- return false;
- }
+ struct usb_device *udev = stream->dev->udev;
- return true;
+ uvc_urb->buffer = usb_alloc_noncoherent(udev, stream->urb_size,
+ gfp_flags, &uvc_urb->dma,
+ uvc_stream_dir(stream),
+ &uvc_urb->sgt);
+ return !!uvc_urb->buffer;
}
/*
@@ -1953,6 +1927,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
+ urb->sgt = uvc_urb->sgt;
for (i = 0; i < npackets; ++i) {
urb->iso_frame_desc[i].offset = i * psize;
@@ -2009,6 +1984,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
size, uvc_video_complete, uvc_urb);
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = uvc_urb->dma;
+ urb->sgt = uvc_urb->sgt;
uvc_urb->urb = urb;
}
@@ -2120,7 +2096,7 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream,
/* Submit the URBs. */
for_each_uvc_urb(uvc_urb, stream) {
- ret = uvc_submit_urb(uvc_urb, gfp_flags);
+ ret = usb_submit_urb(uvc_urb->urb, gfp_flags);
if (ret < 0) {
dev_err(&stream->intf->dev,
"Failed to submit URB %u (%d).\n",
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index b9f8eb62ba1d..757254fc4fe9 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -77,6 +77,7 @@
#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000
#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000
#define UVC_QUIRK_MJPEG_NO_EOF 0x00020000
+#define UVC_QUIRK_MSXU_META 0x00040000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -134,6 +135,8 @@ struct uvc_control_mapping {
s32 master_manual;
u32 slave_ids[2];
+ bool disabled;
+
const struct uvc_control_mapping *(*filter_mapping)
(struct uvc_video_chain *chain,
struct uvc_control *ctrl);
@@ -326,7 +329,10 @@ struct uvc_buffer {
struct uvc_video_queue {
struct vb2_queue queue;
- struct mutex mutex; /* Protects queue */
+ struct mutex mutex; /*
+ * Serializes vb2_queue and
+ * fops
+ */
unsigned int flags;
unsigned int buf_used;
@@ -570,6 +576,8 @@ struct uvc_status {
};
} __packed;
+#define UVC_MAX_META_DATA_FORMATS 3
+
struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
@@ -580,6 +588,9 @@ struct uvc_device {
const struct uvc_device_info *info;
+ u32 meta_formats[UVC_MAX_META_DATA_FORMATS];
+ unsigned int nmeta_formats;
+
atomic_t nmappings;
/* Video control interface */
@@ -619,18 +630,11 @@ struct uvc_device {
struct uvc_entity *gpio_unit;
};
-enum uvc_handle_state {
- UVC_HANDLE_PASSIVE = 0,
- UVC_HANDLE_ACTIVE = 1,
-};
-
struct uvc_fh {
struct v4l2_fh vfh;
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
- enum uvc_handle_state state;
unsigned int pending_async_ctrls;
- bool is_streaming;
};
/* ------------------------------------------------------------------------
@@ -687,36 +691,11 @@ struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
-void uvc_queue_release(struct uvc_video_queue *queue);
-int uvc_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb);
-int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *v4l2_cb);
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct media_device *mdev,
- struct v4l2_buffer *v4l2_buf);
-int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp);
-int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf, int nonblocking);
-int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type);
-int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type);
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue);
void uvc_queue_buffer_release(struct uvc_buffer *buf);
-int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma);
-__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait);
-#ifndef CONFIG_MMU
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff);
-#endif
-int uvc_queue_allocated(struct uvc_video_queue *queue);
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
return vb2_is_streaming(&queue->queue);
@@ -749,6 +728,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
void uvc_video_clock_update(struct uvc_streaming *stream,
struct vb2_v4l2_buffer *vbuf,
struct uvc_buffer *buf);
+int uvc_meta_init(struct uvc_device *dev);
int uvc_meta_register(struct uvc_streaming *stream);
int uvc_register_video_device(struct uvc_device *dev,
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index bd160a8c9efe..6e585bc76367 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -323,6 +323,12 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_P012M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ /* Tiled YUV formats, non contiguous variant */
+ { .format = V4L2_PIX_FMT_NV12MT, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 64, 32, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
+ { .format = V4L2_PIX_FMT_NV12MT_16X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 16, 8, 0, 0 }},
+
/* Bayer RGB formats */
{ .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
@@ -332,6 +338,10 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
@@ -344,6 +354,28 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ /* Renesas Camera Data Receiver Unit formats, bayer order agnostic */
+ { .format = V4L2_PIX_FMT_RAW_CRU10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 6, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 5, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU20, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 3, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
};
unsigned int i;
@@ -505,10 +537,10 @@ s64 __v4l2_get_link_freq_ctrl(struct v4l2_ctrl_handler *handler,
freq = div_u64(v4l2_ctrl_g_ctrl_int64(ctrl) * mul, div);
- pr_warn("%s: Link frequency estimated using pixel rate: result might be inaccurate\n",
- __func__);
- pr_warn("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n",
- __func__);
+ pr_warn_once("%s: Link frequency estimated using pixel rate: result might be inaccurate\n",
+ __func__);
+ pr_warn_once("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n",
+ __func__);
}
return freq > 0 ? freq : -EINVAL;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 90d25329661e..98b960775e87 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -968,12 +968,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS;
-
- if (p_h264_sps->chroma_format_idc < 3)
- p_h264_sps->flags &=
- ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
}
+ if (p_h264_sps->chroma_format_idc < 3)
+ p_h264_sps->flags &=
+ ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
+
if (p_h264_sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD;
@@ -1631,14 +1631,17 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
EXPORT_SYMBOL(v4l2_ctrl_handler_init_class);
/* Free all controls and control refs */
-void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+int v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
{
struct v4l2_ctrl_ref *ref, *next_ref;
struct v4l2_ctrl *ctrl, *next_ctrl;
struct v4l2_subscribed_event *sev, *next_sev;
- if (hdl == NULL || hdl->buckets == NULL)
- return;
+ if (!hdl)
+ return 0;
+
+ if (!hdl->buckets)
+ return hdl->error;
v4l2_ctrl_handler_free_request(hdl);
@@ -1661,9 +1664,10 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
kvfree(hdl->buckets);
hdl->buckets = NULL;
hdl->cached = NULL;
- hdl->error = 0;
mutex_unlock(hdl->lock);
mutex_destroy(&hdl->_lock);
+
+ return hdl->error;
}
EXPORT_SYMBOL(v4l2_ctrl_handler_free);
diff --git a/drivers/media/v4l2-core/v4l2-i2c.c b/drivers/media/v4l2-core/v4l2-i2c.c
index 586c46544255..ffc64e10fcae 100644
--- a/drivers/media/v4l2-core/v4l2-i2c.c
+++ b/drivers/media/v4l2-core/v4l2-i2c.c
@@ -5,6 +5,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -24,7 +25,7 @@ void v4l2_i2c_subdev_unregister(struct v4l2_subdev *sd)
* registered by us, and would not be
* re-created by just probing the V4L2 driver.
*/
- if (client && !client->dev.of_node && !client->dev.fwnode)
+ if (client && !dev_fwnode(&client->dev))
i2c_unregister_device(client);
}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 650dc1956f73..46da373066f4 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1413,6 +1413,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG10DPCM8: descr = "8-bit Bayer GBGB/RGRG (DPCM)"; break;
case V4L2_PIX_FMT_SGRBG10DPCM8: descr = "8-bit Bayer GRGR/BGBG (DPCM)"; break;
case V4L2_PIX_FMT_SRGGB10DPCM8: descr = "8-bit Bayer RGRG/GBGB (DPCM)"; break;
+ case V4L2_PIX_FMT_RAW_CRU10: descr = "10-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR12: descr = "12-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG12: descr = "12-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG12: descr = "12-bit Bayer GRGR/BGBG"; break;
@@ -1421,6 +1422,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG12P: descr = "12-bit Bayer GBGB/RGRG Packed"; break;
case V4L2_PIX_FMT_SGRBG12P: descr = "12-bit Bayer GRGR/BGBG Packed"; break;
case V4L2_PIX_FMT_SRGGB12P: descr = "12-bit Bayer RGRG/GBGB Packed"; break;
+ case V4L2_PIX_FMT_RAW_CRU12: descr = "12-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR14: descr = "14-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG14: descr = "14-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG14: descr = "14-bit Bayer GRGR/BGBG"; break;
@@ -1429,10 +1431,12 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG14P: descr = "14-bit Bayer GBGB/RGRG Packed"; break;
case V4L2_PIX_FMT_SGRBG14P: descr = "14-bit Bayer GRGR/BGBG Packed"; break;
case V4L2_PIX_FMT_SRGGB14P: descr = "14-bit Bayer RGRG/GBGB Packed"; break;
+ case V4L2_PIX_FMT_RAW_CRU14: descr = "14-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR16: descr = "16-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG16: descr = "16-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG16: descr = "16-bit Bayer GRGR/BGBG"; break;
case V4L2_PIX_FMT_SRGGB16: descr = "16-bit Bayer RGRG/GBGB"; break;
+ case V4L2_PIX_FMT_RAW_CRU20: descr = "14-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SN9C20X_I420: descr = "GSPCA SN9C20X I420"; break;
case V4L2_PIX_FMT_SPCA501: descr = "GSPCA SPCA501"; break;
case V4L2_PIX_FMT_SPCA505: descr = "GSPCA SPCA505"; break;
@@ -1459,6 +1463,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break;
case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break;
case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break;
+ case V4L2_META_FMT_UVC_MSXU_1_5: descr = "UVC MSXU Metadata"; break;
case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break;
case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break;
case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break;
@@ -3245,7 +3250,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
return ret;
}
-static unsigned int video_translate_cmd(unsigned int cmd)
+unsigned int v4l2_translate_cmd(unsigned int cmd)
{
#if !defined(CONFIG_64BIT) && defined(CONFIG_COMPAT_32BIT_TIME)
switch (cmd) {
@@ -3266,6 +3271,7 @@ static unsigned int video_translate_cmd(unsigned int cmd)
return cmd;
}
+EXPORT_SYMBOL_GPL(v4l2_translate_cmd);
static int video_get_user(void __user *arg, void *parg,
unsigned int real_cmd, unsigned int cmd,
@@ -3426,7 +3432,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
size_t array_size = 0;
void __user *user_ptr = NULL;
void **kernel_ptr = NULL;
- unsigned int cmd = video_translate_cmd(orig_cmd);
+ unsigned int cmd = v4l2_translate_cmd(orig_cmd);
const size_t ioc_size = _IOC_SIZE(cmd);
/* Copy arguments into temp kernel buffer */
diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c
index 6e2647323522..36a0f1a1b0d9 100644
--- a/drivers/media/v4l2-core/v4l2-jpeg.c
+++ b/drivers/media/v4l2-core/v4l2-jpeg.c
@@ -711,83 +711,3 @@ int v4l2_jpeg_parse_header(void *buf, size_t len, struct v4l2_jpeg_header *out)
return marker;
}
EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header);
-
-/**
- * v4l2_jpeg_parse_frame_header - parse frame header
- * @buf: address of the frame header, after the SOF0 marker
- * @len: length of the frame header
- * @frame_header: returns the parsed frame header
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_frame_header(void *buf, size_t len,
- struct v4l2_jpeg_frame_header *frame_header)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_frame_header(&stream, SOF0, frame_header);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header);
-
-/**
- * v4l2_jpeg_parse_scan_header - parse scan header
- * @buf: address of the scan header, after the SOS marker
- * @len: length of the scan header
- * @scan_header: returns the parsed scan header
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_scan_header(void *buf, size_t len,
- struct v4l2_jpeg_scan_header *scan_header)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_scan_header(&stream, scan_header);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header);
-
-/**
- * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment
- * @buf: address of the quantization table segment, after the DQT marker
- * @len: length of the quantization table segment
- * @precision: sample precision (P) in bits per component
- * @q_tables: returns four references into the buffer for the
- * four possible quantization table destinations
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision,
- struct v4l2_jpeg_reference *q_tables)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_quantization_tables(&stream, precision, q_tables);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables);
-
-/**
- * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment
- * @buf: address of the Huffman table segment, after the DHT marker
- * @len: length of the Huffman table segment
- * @huffman_tables: returns four references into the buffer for the
- * four possible Huffman table destinations, in
- * the order DC0, DC1, AC0, AC1
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len,
- struct v4l2_jpeg_reference *huffman_tables)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_huffman_tables(&stream, huffman_tables);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables);
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index a3074f469b15..4fd25fea3b58 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -1004,6 +1004,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct v4l2_subdev_route *routes =
(struct v4l2_subdev_route *)(uintptr_t)routing->routes;
struct v4l2_subdev_krouting krouting = {};
+ unsigned int num_active_routes = 0;
unsigned int i;
if (!v4l2_subdev_enable_streams_api)
@@ -1041,9 +1042,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (!(pads[route->source_pad].flags &
MEDIA_PAD_FL_SOURCE))
return -EINVAL;
+
+ if (route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)
+ num_active_routes++;
}
/*
+ * Drivers that implement routing need to report a frame
+ * descriptor accordingly, with up to one entry per route. Until
+ * the frame descriptors entries get allocated dynamically,
+ * limit the number of active routes to
+ * V4L2_FRAME_DESC_ENTRY_MAX.
+ */
+ if (num_active_routes > V4L2_FRAME_DESC_ENTRY_MAX)
+ return -E2BIG;
+
+ /*
* If the driver doesn't support setting routing, just return
* the routing table.
*/
@@ -2219,6 +2233,9 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd,
*found_streams = BIT_ULL(0);
*enabled_streams =
(sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0;
+ dev_dbg(sd->dev,
+ "collect_streams: sub-device \"%s\" does not support streams\n",
+ sd->entity.name);
return;
}
@@ -2236,6 +2253,10 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd,
if (cfg->enabled)
*enabled_streams |= BIT_ULL(cfg->stream);
}
+
+ dev_dbg(sd->dev,
+ "collect_streams: \"%s\":%u: found %#llx enabled %#llx\n",
+ sd->entity.name, pad, *found_streams, *enabled_streams);
}
static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd,
@@ -2271,6 +2292,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
bool use_s_stream;
int ret;
+ dev_dbg(dev, "enable streams \"%s\":%u/%#llx\n", sd->entity.name, pad,
+ streams_mask);
+
/* A few basic sanity checks first. */
if (pad >= sd->entity.num_pads)
return -EINVAL;
@@ -2318,8 +2342,6 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
goto done;
}
- dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask);
-
already_streaming = v4l2_subdev_is_streaming(sd);
if (!use_s_stream) {
@@ -2371,6 +2393,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
bool use_s_stream;
int ret;
+ dev_dbg(dev, "disable streams \"%s\":%u/%#llx\n", sd->entity.name, pad,
+ streams_mask);
+
/* A few basic sanity checks first. */
if (pad >= sd->entity.num_pads)
return -EINVAL;
@@ -2418,8 +2443,6 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
goto done;
}
- dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask);
-
if (!use_s_stream) {
/* Call the .disable_streams() operation. */
ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,