* [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
@ 2024-07-19 12:40 Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver Shreeya Patel
` (5 more replies)
0 siblings, 6 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-19 12:40 UTC (permalink / raw)
To: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Shreeya Patel
This series implements support for the Synopsys DesignWare
HDMI RX Controller, being compliant with standard HDMI 1.4b
and HDMI 2.0.
Features that are currently supported by the HDMI RX driver
have been tested on rock5b board using a HDMI to micro-HDMI cable.
It is recommended to use a good quality cable as there were
multiple issues seen during testing the driver.
Please note the below information :-
* While testing the driver on rock5b we noticed that the binary BL31
from Rockchip contains some unknown code to get the HDMI-RX PHY
access working without any errors.
With TF-A BL31, the HDMI-RX PHY also works fine but there were no
interrupts seen for rk_hdmirx-hdmi leading to some errors when
loading the driver [0]. It doesn't affect the functionality of the
driver though.
* We have tested the working of OBS studio with HDMIRX driver and
there were no issues seen.
* We also tested and verified the support for interlaced video.
[0] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/trusted-firmware-a/-/issues/1
To test the HDMI RX Controller driver, following example commands can be used :-
root@debian-rockchip-rock5b-rk3588:~# v4l2-ctl --verbose -d /dev/video0 \
--set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 \
--stream-skip=3 --stream-count=100 --stream-to=/home/hdmiin4k.raw --stream-poll
root@debian-rockchip-rock5b-rk3588:~# ffmpeg -f rawvideo -vcodec rawvideo \
-s 1920x1080 -r 60 -pix_fmt bgr24 -i /home/hdmiin4k.raw output.mkv
CEC compliance test results :-
* https://gitlab.collabora.com/-/snippets/381
* https://gitlab.collabora.com/-/snippets/380
Following is the v4l2-compliance test result :-
root@debian-rockchip-rock5b-rk3588:~# v4l2-compliance -d /dev/video0
v4l2-compliance 1.27.0-5220, 64 bits, 64-bit time_t
v4l2-compliance SHA: 8387e3673837 2024-07-01 11:09:32
Compliance test for snps_hdmirx device /dev/video0:
Driver Info:
Driver name : snps_hdmirx
Card type : snps_hdmirx
Bus info : platform:fdee0000.hdmi-receiver
Driver version : 6.10.0
Capabilities : 0x84201000
Video Capture Multiplanar
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04201000
Video Capture Multiplanar
Streaming
Extended Pix Format
Required ioctls:
test VIDIOC_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 1 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
test VIDIOC_DV_TIMINGS_CAP: OK
test VIDIOC_G/S_EDID: OK
Control ioctls (Input 0):
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 3 Private Controls: 0
Format ioctls (Input 0):
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls (Input 0):
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls (Input 0):
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test CREATE_BUFS maximum buffers: OK
test VIDIOC_REMOVE_BUFS: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)
Total for snps_hdmirx device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
Changes in v4 :-
- Remove DTS changes included in the device tree patch
- Remove the hdmi rx pin info as it's already present
in the rk3588-base-pinctrl.dtsi
- Create a separate config option for selecting the EDID
and enable it by default
- Improve the comment related to DV timings and move it
to the side of hdmirx_get_detected_timings
- Add 100ms delay before pulling the HPD high
- Do not return the detected timings from VIDIOC_G_DV_TIMINGS
- Drop the bus info from hdmirx_querycap
- If *num_planes != 0 then return 0 in hdmirx_queue_setup
- Set queue->min_queued_buffers to 1
- Drop q->allow_cache_hints = 0; as it's always 0 by default
- Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
- Drop .read = vb2_fop_read as it's not supported by driver
- Remove redundant edid_init_data_600M
- Make HPD low when driver is loaded
- Add support for reading AVI Infoframe
- Remove msg_len checks from hdmirx_cec_transmit
- Add info about the CEC compliance test in the cover letter
- Add arbitration lost status
- Validate the physical address inside the EDID
Changes in v3 :-
- Use v4l2-common helpers in the HDMIRX driver
- Rename cma node and phandle names
- Elaborate the comment to explain 160MiB calculation
- Move &hdmi_receiver_cma to the rock5b dts file
- Add information about interlaced video testing in the
cover-letter
Changes in v2 :-
- Fix checkpatch --strict warnings
- Move the dt-binding include file changes in a separate patch
- Add a description for the hardware in the dt-bindings file
- Rename resets, vo1 grf and HPD properties
- Add a proper description for grf and vo1-grf phandles in the
bindings
- Rename the HDMI RX node name to hdmi-receiver
- Include gpio header file in binding example to fix the
dt_binding_check failure
- Move hdmirx_cma node to the rk3588.dtsi file
- Add an entry to MAINTAINERS file for the HDMIRX driver
Shreeya Patel (4):
MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
dt-bindings: media: Document bindings for HDMI RX Controller
arm64: dts: rockchip: Add device tree support for HDMI RX Controller
media: platform: synopsys: Add support for hdmi input driver
.../bindings/media/snps,dw-hdmi-rx.yaml | 132 +
MAINTAINERS | 8 +
.../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +
.../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 56 +
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/synopsys/Kconfig | 3 +
drivers/media/platform/synopsys/Makefile | 2 +
.../media/platform/synopsys/hdmirx/Kconfig | 27 +
.../media/platform/synopsys/hdmirx/Makefile | 4 +
.../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
.../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
.../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
.../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
14 files changed, 3734 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
create mode 100644 drivers/media/platform/synopsys/Kconfig
create mode 100644 drivers/media/platform/synopsys/Makefile
create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
--
2.39.2
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
@ 2024-07-19 12:40 ` Shreeya Patel
2024-07-19 12:47 ` Christopher Obbard
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
` (4 subsequent siblings)
5 siblings, 1 reply; 40+ messages in thread
From: Shreeya Patel @ 2024-07-19 12:40 UTC (permalink / raw)
To: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Shreeya Patel
Add an entry for Synopsys DesignWare HDMI Receiver Controller
Driver.
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
---
Changes in v4 :-
- No change
Changes in v3 :-
- No change
Changes in v2 :-
- Add a patch for MAINTAINERS file changes
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1c87b471941c..0f0e1d58abff 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22138,6 +22138,14 @@ F: drivers/net/pcs/pcs-xpcs.c
F: drivers/net/pcs/pcs-xpcs.h
F: include/linux/pcs/pcs-xpcs.h
+SYNOPSYS DESIGNWARE HDMI RX CONTROLLER DRIVER
+M: Shreeya Patel <shreeya.patel@collabora.com
+L: linux-media@vger.kernel.org
+L: kernel@collabora.com
+S: Maintained
+F: Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
+F: drivers/media/platform/synopsys/hdmirx/*
+
SYNOPSYS DESIGNWARE I2C DRIVER
M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
--
2.39.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver Shreeya Patel
@ 2024-07-19 12:40 ` Shreeya Patel
2024-07-19 22:10 ` Rob Herring (Arm)
` (3 more replies)
2024-07-19 12:40 ` [PATCH v4 3/4] arm64: dts: rockchip: Add device tree support " Shreeya Patel
` (3 subsequent siblings)
5 siblings, 4 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-19 12:40 UTC (permalink / raw)
To: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Shreeya Patel, Dmitry Osipenko
Document bindings for the Synopsys DesignWare HDMI RX Controller.
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
---
Changes in v4 :-
- No change
Changes in v3 :-
- Rename hdmirx_cma to hdmi_receiver_cma
- Add a Reviewed-by tag
Changes in v2 :-
- Add a description for the hardware
- Rename resets, vo1 grf and HPD properties
- Add a proper description for grf and vo1-grf phandles
- Rename the HDMI Input node name to hdmi-receiver
- Improve the subject line
- Include gpio header file in example to fix dt_binding_check failure
.../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
1 file changed, 132 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
new file mode 100644
index 000000000000..96ae1e2d2816
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
+
+---
+$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synopsys DesignWare HDMI RX Controller
+
+maintainers:
+ - Shreeya Patel <shreeya.patel@collabora.com>
+
+description:
+ Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
+ allowing devices to receive and decode high-resolution video streams
+ from external sources like media players, cameras, laptops, etc.
+
+properties:
+ compatible:
+ items:
+ - const: rockchip,rk3588-hdmirx-ctrler
+ - const: snps,dw-hdmi-rx
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 3
+
+ interrupt-names:
+ items:
+ - const: cec
+ - const: hdmi
+ - const: dma
+
+ clocks:
+ maxItems: 7
+
+ clock-names:
+ items:
+ - const: aclk
+ - const: audio
+ - const: cr_para
+ - const: pclk
+ - const: ref
+ - const: hclk_s_hdmirx
+ - const: hclk_vo1
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 4
+
+ reset-names:
+ items:
+ - const: axi
+ - const: apb
+ - const: ref
+ - const: biu
+
+ memory-region:
+ maxItems: 1
+
+ hpd-gpios:
+ description: GPIO specifier for HPD.
+ maxItems: 1
+
+ rockchip,grf:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ The phandle of the syscon node for the general register file
+ containing HDMIRX PHY status bits.
+
+ rockchip,vo1-grf:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ The phandle of the syscon node for the Video Output GRF register
+ to enable EDID transfer through SDAIN and SCLIN.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - clocks
+ - clock-names
+ - power-domains
+ - resets
+ - pinctrl-0
+ - hpd-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/rockchip,rk3588-cru.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/power/rk3588-power.h>
+ #include <dt-bindings/reset/rockchip,rk3588-cru.h>
+ hdmi_receiver: hdmi-receiver@fdee0000 {
+ compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
+ reg = <0xfdee0000 0x6000>;
+ interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "cec", "hdmi", "dma";
+ clocks = <&cru ACLK_HDMIRX>,
+ <&cru CLK_HDMIRX_AUD>,
+ <&cru CLK_CR_PARA>,
+ <&cru PCLK_HDMIRX>,
+ <&cru CLK_HDMIRX_REF>,
+ <&cru PCLK_S_HDMIRX>,
+ <&cru HCLK_VO1>;
+ clock-names = "aclk",
+ "audio",
+ "cr_para",
+ "pclk",
+ "ref",
+ "hclk_s_hdmirx",
+ "hclk_vo1";
+ power-domains = <&power RK3588_PD_VO1>;
+ resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
+ <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
+ reset-names = "axi", "apb", "ref", "biu";
+ memory-region = <&hdmi_receiver_cma>;
+ pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
+ pinctrl-names = "default";
+ hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
+ };
--
2.39.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH v4 3/4] arm64: dts: rockchip: Add device tree support for HDMI RX Controller
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
@ 2024-07-19 12:40 ` Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
` (2 subsequent siblings)
5 siblings, 0 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-19 12:40 UTC (permalink / raw)
To: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Shreeya Patel, Dmitry Osipenko
Add device tree support for Synopsys DesignWare HDMI RX
Controller.
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
---
Changes in v4 :-
- Remove DTS changes added to this patch
- Remove the HDMI RX pin nodes as it's already present
in the rk3588-base-pinctrl.dtsi
Changes in v3 :-
- Rename cma node and phandle names
- Elaborate the comment to explain 160MiB calculation
- Move &hdmi_receiver_cma to the rock5b dts file
Changes in v2 :-
- Fix some of the checkpatch errors and warnings
- Rename resets, vo1-grf and HPD
- Move hdmirx_cma node to the rk3588.dtsi file
.../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +++++
| 56 +++++++++++++++++++
2 files changed, 70 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi
index 30db12c4fc82..ca006f5a4c90 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base-pinctrl.dtsi
@@ -594,6 +594,20 @@ hdmim0_tx1_hpd: hdmim0-tx1-hpd {
/* hdmim0_tx1_hpd */
<1 RK_PA6 5 &pcfg_pull_none>;
};
+
+ /omit-if-no-ref/
+ hdmim1_rx: hdmim1-rx {
+ rockchip,pins =
+ /* hdmim1_rx_cec */
+ <3 RK_PD1 5 &pcfg_pull_none>,
+ /* hdmim1_rx_scl */
+ <3 RK_PD2 5 &pcfg_pull_none_smt>,
+ /* hdmim1_rx_sda */
+ <3 RK_PD3 5 &pcfg_pull_none_smt>,
+ /* hdmim1_rx_hpdin */
+ <3 RK_PD4 5 &pcfg_pull_none>;
+ };
+
/omit-if-no-ref/
hdmim1_rx_cec: hdmim1-rx-cec {
rockchip,pins =
--git a/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
index 0ce0934ec6b7..96f8ce91eaf4 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-extra.dtsi
@@ -7,6 +7,29 @@
#include "rk3588-extra-pinctrl.dtsi"
/ {
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ /*
+ * The 4k HDMI capture controller works only with 32bit
+ * phys addresses and doesn't support IOMMU. HDMI RX CMA
+ * must be reserved below 4GB.
+ * The size of 160MB was determined as follows:
+ * (3840 * 2160 pixels) * (4 bytes/pixel) * (2 frames/buffer) / 10^6 = 66MB
+ * To ensure sufficient support for practical use-cases,
+ * we doubled the 66MB value.
+ */
+ hdmi_receiver_cma: hdmi-receiver-cma {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x0 0x0 0xffffffff>;
+ size = <0x0 (160 * 0x100000)>; /* 160MiB */
+ no-map;
+ status = "disabled";
+ };
+ };
+
usb_host1_xhci: usb@fc400000 {
compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
reg = <0x0 0xfc400000 0x0 0x400000>;
@@ -135,6 +158,39 @@ i2s10_8ch: i2s@fde00000 {
status = "disabled";
};
+ hdmi_receiver: hdmi_receiver@fdee0000 {
+ compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
+ reg = <0x0 0xfdee0000 0x0 0x6000>;
+ power-domains = <&power RK3588_PD_VO1>;
+ rockchip,grf = <&sys_grf>;
+ rockchip,vo1-grf = <&vo1_grf>;
+ interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "cec", "hdmi", "dma";
+ clocks = <&cru ACLK_HDMIRX>,
+ <&cru CLK_HDMIRX_AUD>,
+ <&cru CLK_CR_PARA>,
+ <&cru PCLK_HDMIRX>,
+ <&cru CLK_HDMIRX_REF>,
+ <&cru PCLK_S_HDMIRX>,
+ <&cru HCLK_VO1>;
+ clock-names = "aclk",
+ "audio",
+ "cr_para",
+ "pclk",
+ "ref",
+ "hclk_s_hdmirx",
+ "hclk_vo1";
+ resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
+ <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
+ reset-names = "axi", "apb", "ref", "biu";
+ memory-region = <&hdmi_receiver_cma>;
+ pinctrl-0 = <&hdmim1_rx>;
+ pinctrl-names = "default";
+ status = "disabled";
+ };
+
pcie3x4: pcie@fe150000 {
compatible = "rockchip,rk3588-pcie", "rockchip,rk3568-pcie";
#address-cells = <3>;
--
2.39.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
` (2 preceding siblings ...)
2024-07-19 12:40 ` [PATCH v4 3/4] arm64: dts: rockchip: Add device tree support " Shreeya Patel
@ 2024-07-19 12:40 ` Shreeya Patel
2024-07-20 11:33 ` Johan Jonker
` (4 more replies)
2024-07-22 19:39 ` hoff.benjamin.k
2024-08-03 23:57 ` [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Tim Surber
5 siblings, 5 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-19 12:40 UTC (permalink / raw)
To: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Shreeya Patel, Dmitry Osipenko
Add initial support for the Synopsys DesignWare HDMI RX
Controller Driver used by Rockchip RK3588. The driver
supports:
- HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
- RGB888, YUV422, YUV444 and YCC420 pixel formats
- CEC
- EDID configuration
The hardware also has Audio and HDCP capabilities, but these are
not yet supported by the driver.
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
---
Changes in v4 :-
- Create a separate config option for selecting the EDID
and enable it by default
- Improve the comment related to DV timings and move it
to the side of hdmirx_get_detected_timings
- Add 100ms delay before pulling the HPD high
- Do not return the detected timings from VIDIOC_G_DV_TIMINGS
- Drop the bus info from hdmirx_querycap
- If *num_planes != 0 then return 0 in hdmirx_queue_setup
- Set queue->min_queued_buffers to 1
- Drop q->allow_cache_hints = 0; as it's always 0 by default
- Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
- Drop .read = vb2_fop_read as it's not supported by driver
- Remove redundant edid_init_data_600M
- Make HPD low when driver is loaded
- Add support for reading AVI Infoframe
- Remove msg_len checks from hdmirx_cec_transmit
- Add info about the CEC compliance test in the cover letter
- Add arbitration lost status
- Validate the physical address inside the EDID
Changes in v3 :-
- Use v4l2-common helper functions
Changes in v2 :-
- Fix checkpatch --strict warnings
- Rename resets, vo1-grf and HPD node names as per the DT changes
drivers/media/platform/Kconfig | 1 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/synopsys/Kconfig | 3 +
drivers/media/platform/synopsys/Makefile | 2 +
.../media/platform/synopsys/hdmirx/Kconfig | 27 +
.../media/platform/synopsys/hdmirx/Makefile | 4 +
.../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
.../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
.../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
.../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
10 files changed, 3524 insertions(+)
create mode 100644 drivers/media/platform/synopsys/Kconfig
create mode 100644 drivers/media/platform/synopsys/Makefile
create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 85d2627776b6..9287faafdce5 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -85,6 +85,7 @@ source "drivers/media/platform/rockchip/Kconfig"
source "drivers/media/platform/samsung/Kconfig"
source "drivers/media/platform/st/Kconfig"
source "drivers/media/platform/sunxi/Kconfig"
+source "drivers/media/platform/synopsys/Kconfig"
source "drivers/media/platform/ti/Kconfig"
source "drivers/media/platform/verisilicon/Kconfig"
source "drivers/media/platform/via/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index ace4e34483dd..6fd7db0541c7 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -28,6 +28,7 @@ obj-y += rockchip/
obj-y += samsung/
obj-y += st/
obj-y += sunxi/
+obj-y += synopsys/
obj-y += ti/
obj-y += verisilicon/
obj-y += via/
diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
new file mode 100644
index 000000000000..4fd521f78425
--- /dev/null
+++ b/drivers/media/platform/synopsys/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/synopsys/hdmirx/Kconfig"
diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
new file mode 100644
index 000000000000..3b12c574dd67
--- /dev/null
+++ b/drivers/media/platform/synopsys/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += hdmirx/
diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
new file mode 100644
index 000000000000..ab569e59300f
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_SYNOPSYS_HDMIRX
+ tristate "Synopsys DesignWare HDMI Receiver driver"
+ depends on VIDEO_DEV
+ depends on ARCH_ROCKCHIP
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select CEC_CORE
+ select CEC_NOTIFIER
+ select HDMI
+ help
+ Support for Synopsys HDMI HDMI RX Controller.
+ This driver supports HDMI 2.0 version.
+
+ To compile this driver as a module, choose M here. The module
+ will be called synopsys_hdmirx.
+
+config HDMIRX_LOAD_DEFAULT_EDID
+ bool "Load default EDID"
+ depends on VIDEO_SYNOPSYS_HDMIRX
+ default "y"
+ help
+ Preload the default EDID (Extended Display Identification Data).
+ EDID contains information about the capabilities of the display,
+ such as supported resolutions, refresh rates, and audio formats.
diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
new file mode 100644
index 000000000000..2fa2d9e25300
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
+
+obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
new file mode 100644
index 000000000000..1dfecf256393
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -0,0 +1,2763 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Collabora, Ltd.
+ * Author: Shreeya Patel <shreeya.patel@collabora.com>
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/workqueue.h>
+
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-common.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "snps_hdmirx.h"
+#include "snps_hdmirx_cec.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-3)");
+
+#define EDID_NUM_BLOCKS_MAX 2
+#define EDID_BLOCK_SIZE 128
+#define HDMIRX_STORED_BIT_WIDTH 8
+#define IREF_CLK_FREQ_HZ 428571429
+#define MEMORY_ALIGN_ROUND_UP_BYTES 64
+#define HDMIRX_PLANE_Y 0
+#define HDMIRX_PLANE_CBCR 1
+#define RK_IRQ_HDMIRX_HDMI 210
+#define FILTER_FRAME_CNT 6
+#define RK_SIP_FIQ_CTRL 0x82000024
+#define SIP_WDT_CFG 0x82000026
+#define DETECTION_THRESHOLD 7
+
+/* fiq control sub func */
+enum {
+ RK_SIP_FIQ_CTRL_FIQ_EN = 1,
+ RK_SIP_FIQ_CTRL_FIQ_DIS,
+ RK_SIP_FIQ_CTRL_SET_AFF
+};
+
+/* SIP_WDT_CONFIG call types */
+enum {
+ WDT_START = 0,
+ WDT_STOP = 1,
+ WDT_PING = 2,
+};
+
+enum hdmirx_pix_fmt {
+ HDMIRX_RGB888 = 0,
+ HDMIRX_YUV422 = 1,
+ HDMIRX_YUV444 = 2,
+ HDMIRX_YUV420 = 3,
+};
+
+enum ddr_store_fmt {
+ STORE_RGB888 = 0,
+ STORE_RGBA_ARGB,
+ STORE_YUV420_8BIT,
+ STORE_YUV420_10BIT,
+ STORE_YUV422_8BIT,
+ STORE_YUV422_10BIT,
+ STORE_YUV444_8BIT,
+ STORE_YUV420_16BIT = 8,
+ STORE_YUV422_16BIT = 9,
+};
+
+enum hdmirx_reg_attr {
+ HDMIRX_ATTR_RW = 0,
+ HDMIRX_ATTR_RO = 1,
+ HDMIRX_ATTR_WO = 2,
+ HDMIRX_ATTR_RE = 3,
+};
+
+enum {
+ HDMIRX_RST_A,
+ HDMIRX_RST_P,
+ HDMIRX_RST_REF,
+ HDMIRX_RST_BIU,
+ HDMIRX_NUM_RST,
+};
+
+static const char * const pix_fmt_str[] = {
+ "RGB888",
+ "YUV422",
+ "YUV444",
+ "YUV420",
+};
+
+struct hdmirx_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ u32 buff_addr[VIDEO_MAX_PLANES];
+};
+
+struct hdmirx_stream {
+ struct snps_hdmirx_dev *hdmirx_dev;
+ struct video_device vdev;
+ struct vb2_queue buf_queue;
+ struct list_head buf_head;
+ struct hdmirx_buffer *curr_buf;
+ struct hdmirx_buffer *next_buf;
+ struct v4l2_pix_format_mplane pixm;
+ const struct v4l2_format_info *out_finfo;
+ struct mutex vlock; /* to lock resources associated with video buffer and video device */
+ spinlock_t vbq_lock; /* to lock video buffer queue */
+ bool stopping;
+ wait_queue_head_t wq_stopped;
+ u32 frame_idx;
+ u32 line_flag_int_cnt;
+ u32 irq_stat;
+};
+
+struct snps_hdmirx_dev {
+ struct device *dev;
+ struct device *codec_dev;
+ struct hdmirx_stream stream;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *detect_tx_5v_ctrl;
+ struct v4l2_ctrl *rgb_range;
+ struct v4l2_dv_timings timings;
+ struct gpio_desc *detect_5v_gpio;
+ struct work_struct work_wdt_config;
+ struct delayed_work delayed_work_hotplug;
+ struct delayed_work delayed_work_res_change;
+ struct delayed_work delayed_work_heartbeat;
+ struct cec_notifier *cec_notifier;
+ struct hdmirx_cec *cec;
+ struct mutex stream_lock; /* to lock video stream capture */
+ struct mutex work_lock; /* to lock the critical section of hotplug event */
+ struct reset_control_bulk_data resets[HDMIRX_NUM_RST];
+ struct clk_bulk_data *clks;
+ struct regmap *grf;
+ struct regmap *vo1_grf;
+ struct completion cr_write_done;
+ struct completion timer_base_lock;
+ struct completion avi_pkt_rcv;
+ enum hdmirx_pix_fmt pix_fmt;
+ void __iomem *regs;
+ int hdmi_irq;
+ int dma_irq;
+ int det_irq;
+ bool hpd_trigger_level;
+ bool tmds_clk_ratio;
+ bool is_dvi_mode;
+ bool got_timing;
+ u32 num_clks;
+ u32 edid_blocks_written;
+ u32 cur_vic;
+ u32 cur_fmt_fourcc;
+ u32 color_depth;
+ u8 edid[EDID_BLOCK_SIZE * 2];
+ hdmi_codec_plugged_cb plugged_cb;
+ spinlock_t rst_lock; /* to lock register access */
+};
+
+static u8 edid_init_data_340M[] = {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+ 0x49, 0x70, 0x88, 0x35, 0x01, 0x00, 0x00, 0x00,
+ 0x2D, 0x1F, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78,
+ 0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23,
+ 0x09, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x61, 0x40,
+ 0x01, 0x01, 0x81, 0x00, 0x95, 0x00, 0xA9, 0xC0,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
+ 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
+ 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00, 0x1E,
+ 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20,
+ 0x6E, 0x28, 0x55, 0x00, 0x20, 0xC2, 0x31, 0x00,
+ 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x52,
+ 0x4B, 0x2D, 0x55, 0x48, 0x44, 0x0A, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
+ 0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA7,
+
+ 0x02, 0x03, 0x2F, 0xD1, 0x51, 0x07, 0x16, 0x14,
+ 0x05, 0x01, 0x03, 0x12, 0x13, 0x84, 0x22, 0x1F,
+ 0x90, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x23, 0x09,
+ 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x67, 0x03,
+ 0x0C, 0x00, 0x30, 0x00, 0x10, 0x44, 0xE3, 0x05,
+ 0x03, 0x01, 0xE4, 0x0F, 0x00, 0x80, 0x01, 0x02,
+ 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58,
+ 0x2C, 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00,
+ 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
+};
+
+static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94;
+
+static const struct v4l2_dv_timings_cap hdmirx_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(640, 4096, /* min/max width */
+ 480, 2160, /* min/max height */
+ 20000000, 600000000, /* min/max pixelclock */
+ /* standards */
+ V4L2_DV_BT_STD_CEA861,
+ /* capabilities */
+ V4L2_DV_BT_CAP_PROGRESSIVE |
+ V4L2_DV_BT_CAP_INTERLACED)
+};
+
+static void hdmirx_writel(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val)
+{
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
+ writel(val, hdmirx_dev->regs + reg);
+ spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
+}
+
+static u32 hdmirx_readl(struct snps_hdmirx_dev *hdmirx_dev, int reg)
+{
+ unsigned long lock_flags = 0;
+ u32 val;
+
+ spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
+ val = readl(hdmirx_dev->regs + reg);
+ spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
+ return val;
+}
+
+static void hdmirx_reset_dma(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
+ reset_control_reset(hdmirx_dev->resets[0].rstc);
+ spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
+}
+
+static void hdmirx_update_bits(struct snps_hdmirx_dev *hdmirx_dev, int reg,
+ u32 mask, u32 data)
+{
+ unsigned long lock_flags = 0;
+ u32 val;
+
+ spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
+ val = readl(hdmirx_dev->regs + reg) & ~mask;
+ val |= (data & mask);
+ writel(val, hdmirx_dev->regs + reg);
+ spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
+}
+
+static int hdmirx_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ if (fh->vdev->vfl_dir == VFL_DIR_RX)
+ return v4l2_src_change_event_subscribe(fh, sub);
+ break;
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ bool ret;
+ int val, i, cnt;
+
+ cnt = 0;
+ for (i = 0; i < 10; i++) {
+ usleep_range(1000, 1100);
+ val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ if (val > 0)
+ cnt++;
+ if (cnt >= DETECTION_THRESHOLD)
+ break;
+ }
+
+ ret = (cnt >= DETECTION_THRESHOLD) ? true : false;
+ v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u32 mu_status, dma_st10, cmu_st;
+
+ mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
+ dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
+ cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
+
+ if ((mu_status & TMDSVALID_STABLE_ST) &&
+ (dma_st10 & HDMIRX_LOCK) &&
+ (cmu_st & TMDSQPCLK_LOCKED_ST))
+ return false;
+
+ return true;
+}
+
+static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val, color_depth_reg;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
+
+ switch (color_depth_reg) {
+ case 0x4:
+ hdmirx_dev->color_depth = 24;
+ break;
+ case 0x5:
+ hdmirx_dev->color_depth = 30;
+ break;
+ case 0x6:
+ hdmirx_dev->color_depth = 36;
+ break;
+ case 0x7:
+ hdmirx_dev->color_depth = 48;
+ break;
+ default:
+ hdmirx_dev->color_depth = 24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
+ __func__, hdmirx_dev->color_depth, color_depth_reg);
+}
+
+static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ hdmirx_dev->pix_fmt = val & HDMIRX_FORMAT_MASK;
+
+ switch (hdmirx_dev->pix_fmt) {
+ case HDMIRX_RGB888:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ break;
+ case HDMIRX_YUV422:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV16;
+ break;
+ case HDMIRX_YUV444:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV24;
+ break;
+ case HDMIRX_YUV420:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV12;
+ break;
+ default:
+ v4l2_err(v4l2_dev,
+ "%s: err pix_fmt: %d, set RGB888 as default\n",
+ __func__, hdmirx_dev->pix_fmt);
+ hdmirx_dev->pix_fmt = HDMIRX_RGB888;
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s\n", __func__,
+ pix_fmt_str[hdmirx_dev->pix_fmt]);
+}
+
+static void hdmirx_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_bt_timings *bt, bool from_dma)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 hact, vact, htotal, vtotal, fps;
+ u32 hfp, hs, hbp, vfp, vs, vbp;
+ u32 val;
+
+ if (from_dma) {
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS2);
+ hact = (val >> 16) & 0xffff;
+ vact = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS3);
+ htotal = (val >> 16) & 0xffff;
+ vtotal = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS4);
+ hs = (val >> 16) & 0xffff;
+ vs = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS5);
+ hbp = (val >> 16) & 0xffff;
+ vbp = val & 0xffff;
+ hfp = htotal - hact - hs - hbp;
+ vfp = vtotal - vact - vs - vbp;
+ } else {
+ val = hdmirx_readl(hdmirx_dev, VMON_STATUS1);
+ hs = (val >> 16) & 0xffff;
+ hfp = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, VMON_STATUS2);
+ hbp = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, VMON_STATUS3);
+ htotal = (val >> 16) & 0xffff;
+ hact = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, VMON_STATUS4);
+ vs = (val >> 16) & 0xffff;
+ vfp = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, VMON_STATUS5);
+ vbp = val & 0xffff;
+ val = hdmirx_readl(hdmirx_dev, VMON_STATUS6);
+ vtotal = (val >> 16) & 0xffff;
+ vact = val & 0xffff;
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
+ hact *= 2;
+ }
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
+ htotal *= 2;
+ fps = (bt->pixelclock + (htotal * vtotal) / 2) / (htotal * vtotal);
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
+ fps *= 2;
+ bt->width = hact;
+ bt->height = vact;
+ bt->hfrontporch = hfp;
+ bt->hsync = hs;
+ bt->hbackporch = hbp;
+ bt->vfrontporch = vfp;
+ bt->vsync = vs;
+ bt->vbackporch = vbp;
+
+ v4l2_dbg(1, debug, v4l2_dev, "get timings from %s\n", from_dma ? "dma" : "ctrl");
+ v4l2_dbg(1, debug, v4l2_dev, "act:%ux%u, total:%ux%u, fps:%u, pixclk:%llu\n",
+ bt->width, bt->height, htotal, vtotal, fps, bt->pixelclock);
+
+ v4l2_dbg(2, debug, v4l2_dev, "hfp:%u, hs:%u, hbp:%u, vfp:%u, vs:%u, vbp:%u\n",
+ bt->hfrontporch, bt->hsync, bt->hbackporch,
+ bt->vfrontporch, bt->vsync, bt->vbackporch);
+}
+
+static bool hdmirx_check_timing_valid(struct v4l2_bt_timings *bt)
+{
+ if (bt->width < 100 || bt->width > 5000 ||
+ bt->height < 100 || bt->height > 5000)
+ return false;
+
+ if (!bt->hsync || bt->hsync > 200 ||
+ !bt->vsync || bt->vsync > 100)
+ return false;
+
+ if (!bt->hbackporch || bt->hbackporch > 2000 ||
+ !bt->vbackporch || bt->vbackporch > 2000)
+ return false;
+
+ if (!bt->hfrontporch || bt->hfrontporch > 2000 ||
+ !bt->vfrontporch || bt->vfrontporch > 2000)
+ return false;
+
+ return true;
+}
+
+static void hdmirx_get_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ union hdmi_infoframe frame = {};
+ int err, i, b, itr = 0;
+ u8 aviif[3 + 7 * 4];
+ u32 val;
+
+ aviif[itr++] = HDMI_INFOFRAME_TYPE_AVI;
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PH2_1);
+ aviif[itr++] = val & 0xff;
+ aviif[itr++] = (val >> 8) & 0xff;
+
+ for (i = 0; i < 7; i++) {
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB3_0 + 4 * i);
+
+ for (b = 0; b < 4; b++)
+ aviif[itr++] = (val >> (8 * b)) & 0xff;
+ }
+
+ err = hdmi_infoframe_unpack(&frame, aviif, sizeof(aviif));
+ if (err) {
+ v4l2_err(v4l2_dev, "failed to unpack AVI infoframe\n");
+ return;
+ }
+
+ v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, frame.avi.quantization_range);
+}
+
+/*
+ * When querying DV timings during preview, if the DMA's timing is stable,
+ * we retrieve the timings directly from the DMA. However, if the current
+ * resolution is negative, obtaining the timing from CTRL may require a
+ * change in the sync polarity, potentially leading to DMA errors.
+ */
+static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_dv_timings *timings,
+ bool from_dma)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_bt_timings *bt = &timings->bt;
+ u32 field_type, color_depth, deframer_st;
+ u32 val, tmdsqpclk_freq, pix_clk;
+ u64 tmp_data, tmds_clk;
+
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+ timings->type = V4L2_DV_BT_656_1120;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ field_type = (val & HDMIRX_TYPE_MASK) >> 7;
+ hdmirx_get_pix_fmt(hdmirx_dev);
+ bt->interlaced = field_type & BIT(0) ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB7_4);
+ hdmirx_dev->cur_vic = val | VIC_VAL_MASK;
+ hdmirx_get_colordepth(hdmirx_dev);
+ color_depth = hdmirx_dev->color_depth;
+ deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
+ hdmirx_dev->is_dvi_mode = deframer_st & OPMODE_STS_MASK ? false : true;
+ tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
+ tmds_clk = tmdsqpclk_freq * 4 * 1000;
+ tmp_data = tmds_clk * 24;
+ do_div(tmp_data, color_depth);
+ pix_clk = tmp_data;
+ bt->pixelclock = pix_clk;
+
+ hdmirx_get_avi_infoframe(hdmirx_dev);
+
+ hdmirx_get_timings(hdmirx_dev, bt, from_dma);
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height *= 2;
+ bt->il_vsync = bt->vsync + 1;
+ }
+
+ v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu\n", tmds_clk);
+ v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, vic:%d, color:%d, mode:%s\n",
+ bt->interlaced, hdmirx_dev->pix_fmt,
+ hdmirx_dev->cur_vic, hdmirx_dev->color_depth,
+ hdmirx_dev->is_dvi_mode ? "dvi" : "hdmi");
+ v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
+
+ if (!hdmirx_check_timing_valid(bt))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ return !tx_5v_power_present(hdmirx_dev);
+}
+
+static int hdmirx_query_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ if (port_no_link(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: port has no link\n", __func__);
+ return -ENOLINK;
+ }
+
+ if (signal_not_lock(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: signal is not locked\n", __func__);
+ return -ENOLCK;
+ }
+
+ ret = hdmirx_get_detected_timings(hdmirx_dev, timings, true);
+ if (ret)
+ return ret;
+
+ if (debug)
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
+ "query_dv_timings: ", timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: timings out of range\n", __func__);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: %sable, hpd_trigger_level:%d\n",
+ __func__, en ? "en" : "dis",
+ hdmirx_dev->hpd_trigger_level);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, HPDLOW, en ? 0 : HPDLOW);
+ en = hdmirx_dev->hpd_trigger_level ? en : !en;
+ hdmirx_writel(hdmirx_dev, CORE_CONFIG, en);
+}
+
+static int hdmirx_write_edid(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_edid *edid, bool hpd_up)
+{
+ u32 edid_len = edid->blocks * EDID_BLOCK_SIZE;
+ char data[300];
+ u32 i;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+ if (edid->pad)
+ return -EINVAL;
+
+ if (edid->start_block)
+ return -EINVAL;
+
+ if (edid->blocks > EDID_NUM_BLOCKS_MAX) {
+ edid->blocks = EDID_NUM_BLOCKS_MAX;
+ return -E2BIG;
+ }
+
+ if (!edid->blocks) {
+ hdmirx_dev->edid_blocks_written = 0;
+ return 0;
+ }
+
+ cec_s_phys_addr_from_edid(hdmirx_dev->cec->adap,
+ (const struct edid *)edid->edid);
+
+ memset(&hdmirx_dev->edid, 0, sizeof(hdmirx_dev->edid));
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK |
+ EDID_WRITE_EN_MASK |
+ EDID_SLAVE_ADDR_MASK,
+ EDID_READ_EN(0) |
+ EDID_WRITE_EN(1) |
+ EDID_SLAVE_ADDR(0x50));
+ for (i = 0; i < edid_len; i++)
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG10, edid->edid[i]);
+
+ /* read out for debug */
+ if (debug >= 2) {
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK |
+ EDID_WRITE_EN_MASK,
+ EDID_READ_EN(1) |
+ EDID_WRITE_EN(0));
+ edid_len = edid_len > sizeof(data) ? sizeof(data) : edid_len;
+ memset(data, 0, sizeof(data));
+ for (i = 0; i < edid_len; i++)
+ data[i] = hdmirx_readl(hdmirx_dev, DMA_STATUS14);
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data,
+ edid_len, false);
+ }
+
+ /*
+ * You must set EDID_READ_EN & EDID_WRITE_EN bit to 0,
+ * when the read/write edid operation is completed.Otherwise, it
+ * will affect the reading and writing of other registers
+ */
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK | EDID_WRITE_EN_MASK,
+ EDID_READ_EN(0) | EDID_WRITE_EN(0));
+
+ hdmirx_dev->edid_blocks_written = edid->blocks;
+ memcpy(&hdmirx_dev->edid, edid->edid, edid->blocks * EDID_BLOCK_SIZE);
+ if (hpd_up) {
+ if (tx_5v_power_present(hdmirx_dev)) {
+ /* Add 100ms delay after updating the EDID as per HDMI specs */
+ msleep(100);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Before clearing interrupt, we need to read the interrupt status.
+ */
+static inline void hdmirx_clear_interrupt(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 reg, u32 val)
+{
+ /* (interrupt status register) = (interrupt clear register) - 0x8 */
+ hdmirx_readl(hdmirx_dev, reg - 0x8);
+ hdmirx_writel(hdmirx_dev, reg, val);
+}
+
+static void hdmirx_interrupts_setup(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: %sable\n",
+ __func__, en ? "en" : "dis");
+
+ /* Note: In DVI mode, it needs to be written twice to take effect. */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+
+ if (en) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG,
+ TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG);
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ TMDSVALID_STABLE_CHG, TMDSVALID_STABLE_CHG);
+ hdmirx_update_bits(hdmirx_dev, AVPUNIT_0_INT_MASK_N,
+ CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ,
+ CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ);
+ } else {
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
+ }
+}
+
+static void hdmirx_plugout(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct arm_smccc_res res;
+
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
+ hdmirx_interrupts_setup(hdmirx_dev, false);
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ hdmirx_reset_dma(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE | PHY_RESET |
+ PHY_PDDQ, HDMI_DISABLE);
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
+ cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_heartbeat);
+ flush_work(&hdmirx_dev->work_wdt_config);
+ arm_smccc_smc(SIP_WDT_CFG, WDT_STOP, 0, 0, 0, 0, 0, 0, &res);
+}
+
+static int hdmirx_set_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct arm_smccc_res res;
+ int ret;
+
+ disable_irq(hdmirx_dev->hdmi_irq);
+ disable_irq(hdmirx_dev->dma_irq);
+ arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_DIS,
+ RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
+
+ if (tx_5v_power_present(hdmirx_dev))
+ hdmirx_plugout(hdmirx_dev);
+ ret = hdmirx_write_edid(hdmirx_dev, edid, false);
+ if (ret)
+ return ret;
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+ enable_irq(hdmirx_dev->dma_irq);
+ arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
+ RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(500));
+ return 0;
+}
+
+static int hdmirx_get_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->pad)
+ return -EINVAL;
+
+ if (!edid->start_block && !edid->blocks) {
+ edid->blocks = hdmirx_dev->edid_blocks_written;
+ return 0;
+ }
+
+ if (!hdmirx_dev->edid_blocks_written)
+ return -ENODATA;
+
+ if (edid->start_block >= hdmirx_dev->edid_blocks_written || !edid->blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > hdmirx_dev->edid_blocks_written)
+ edid->blocks = hdmirx_dev->edid_blocks_written - edid->start_block;
+
+ memcpy(edid->edid, &hdmirx_dev->edid, edid->blocks * EDID_BLOCK_SIZE);
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: read EDID:\n", __func__);
+ if (debug > 0)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
+ edid->edid, edid->blocks * EDID_BLOCK_SIZE, false);
+
+ return 0;
+}
+
+static int hdmirx_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_fract fps;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ fps = v4l2_calc_timeperframe(&hdmirx_dev->timings);
+ parm->parm.capture.timeperframe.numerator = fps.numerator;
+ parm->parm.capture.timeperframe.denominator = fps.denominator;
+
+ return 0;
+}
+
+static int hdmirx_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap)
+{
+ *cap = hdmirx_timings_cap;
+ return 0;
+}
+
+static int hdmirx_enum_dv_timings(struct file *file, void *_fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ return v4l2_enum_dv_timings_cap(timings, &hdmirx_timings_cap, NULL, NULL);
+}
+
+static void hdmirx_scdc_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, I2C_SLAVE_CONFIG1,
+ I2C_SDA_OUT_HOLD_VALUE_QST_MASK |
+ I2C_SDA_IN_HOLD_VALUE_QST_MASK,
+ I2C_SDA_OUT_HOLD_VALUE_QST(0x80) |
+ I2C_SDA_IN_HOLD_VALUE_QST(0x15));
+ hdmirx_update_bits(hdmirx_dev, SCDC_REGBANK_CONFIG0,
+ SCDC_SINKVERSION_QST_MASK,
+ SCDC_SINKVERSION_QST(1));
+}
+
+static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
+ u32 bit_mask, u32 expect_val, bool is_grf,
+ u32 ms)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 i, val;
+
+ for (i = 0; i < ms; i++) {
+ if (is_grf)
+ regmap_read(hdmirx_dev->grf, reg, &val);
+ else
+ val = hdmirx_readl(hdmirx_dev, reg);
+
+ if ((val & bit_mask) == expect_val) {
+ v4l2_dbg(2, debug, v4l2_dev,
+ "%s: i:%d, time: %dms\n", __func__, i, ms);
+ break;
+ }
+ usleep_range(1000, 1010);
+ }
+
+ if (i == ms)
+ return -1;
+
+ return 0;
+}
+
+static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 phy_reg, u32 val)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ reinit_completion(&hdmirx_dev->cr_write_done);
+ /* clear irq status */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_WRITE_DONE, PHYCREG_CR_WRITE_DONE);
+ /* write phy reg addr */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg);
+ /* write phy reg val */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG2, val);
+ /* config write enable */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_WRITE_P);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->cr_write_done,
+ msecs_to_jiffies(20))) {
+ dev_err(dev, "%s wait cr write done failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void hdmirx_tmds_clk_ratio_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS1);
+ v4l2_dbg(3, debug, v4l2_dev, "%s: scdc_regbank_st:%#x\n", __func__, val);
+ hdmirx_dev->tmds_clk_ratio = (val & SCDC_TMDSBITCLKRATIO) > 0;
+
+ if (hdmirx_dev->tmds_clk_ratio) {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX greater than 3.4Gbps\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
+ TMDS_CLOCK_RATIO, TMDS_CLOCK_RATIO);
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX less than 3.4Gbps\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
+ TMDS_CLOCK_RATIO, 0);
+ }
+}
+
+static void hdmirx_phy_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, SCDC_INT_MASK_N, SCDCTMDSCCFG_CHG,
+ SCDCTMDSCCFG_CHG);
+ /* cr_para_clk 24M */
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, REFFREQ_SEL_MASK, REFFREQ_SEL(0));
+ /* rx data width 40bit valid */
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, RXDATA_WIDTH, RXDATA_WIDTH);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, PHY_RESET);
+ usleep_range(100, 110);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, 0);
+ usleep_range(100, 110);
+ /* select cr para interface */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3);
+
+ if (wait_reg_bit_status(hdmirx_dev, SYS_GRF_SOC_STATUS1,
+ HDMIRXPHY_SRAM_INIT_DONE,
+ HDMIRXPHY_SRAM_INIT_DONE, true, 10))
+ dev_err(dev, "%s: phy SRAM init failed\n", __func__);
+
+ regmap_write(hdmirx_dev->grf, SYS_GRF_SOC_CON1,
+ (HDMIRXPHY_SRAM_EXT_LD_DONE << 16) |
+ HDMIRXPHY_SRAM_EXT_LD_DONE);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 1);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG,
+ CDR_SETTING_BOUNDARY_3_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG,
+ CDR_SETTING_BOUNDARY_4_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG,
+ CDR_SETTING_BOUNDARY_5_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG,
+ CDR_SETTING_BOUNDARY_6_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG,
+ CDR_SETTING_BOUNDARY_7_DEFAULT);
+
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_PDDQ, 0);
+ if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, PDDQ_ACK, 0, false, 10))
+ dev_err(dev, "%s: wait pddq ack failed\n", __func__);
+
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE, 0);
+ if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, HDMI_DISABLE_ACK, 0,
+ false, 50))
+ dev_err(dev, "%s: wait hdmi disable ack failed\n", __func__);
+
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+}
+
+static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ reinit_completion(&hdmirx_dev->timer_base_lock);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TIMER_BASE_LOCKED_IRQ, TIMER_BASE_LOCKED_IRQ);
+ /* write irefclk freq */
+ hdmirx_writel(hdmirx_dev, GLOBAL_TIMER_REF_BASE, IREF_CLK_FREQ_HZ);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->timer_base_lock,
+ msecs_to_jiffies(20)))
+ dev_err(dev, "%s wait timer base lock failed\n", __func__);
+
+ hdmirx_update_bits(hdmirx_dev, CMU_CONFIG0,
+ TMDSQPCLK_STABLE_FREQ_MARGIN_MASK |
+ AUDCLK_STABLE_FREQ_MARGIN_MASK,
+ TMDSQPCLK_STABLE_FREQ_MARGIN(2) |
+ AUDCLK_STABLE_FREQ_MARGIN(1));
+ hdmirx_update_bits(hdmirx_dev, DESCRAND_EN_CONTROL,
+ SCRAMB_EN_SEL_QST_MASK, SCRAMB_EN_SEL_QST(1));
+ hdmirx_update_bits(hdmirx_dev, CED_CONFIG,
+ CED_VIDDATACHECKEN_QST |
+ CED_DATAISCHECKEN_QST |
+ CED_GBCHECKEN_QST |
+ CED_CTRLCHECKEN_QST |
+ CED_CHLOCKMAXER_QST_MASK,
+ CED_VIDDATACHECKEN_QST |
+ CED_GBCHECKEN_QST |
+ CED_CTRLCHECKEN_QST |
+ CED_CHLOCKMAXER_QST(0x10));
+ hdmirx_update_bits(hdmirx_dev, DEFRAMER_CONFIG0,
+ VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST_MASK,
+ VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
+}
+
+static void hdmirx_set_negative_pol(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ if (en) {
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN);
+ hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
+ VPROC_VSYNC_POL_OVR_VALUE |
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_VALUE |
+ VPROC_HSYNC_POL_OVR_EN,
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_EN);
+ return;
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN, 0);
+
+ hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
+ VPROC_VSYNC_POL_OVR_VALUE |
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_VALUE |
+ VPROC_HSYNC_POL_OVR_EN, 0);
+}
+
+static int hdmirx_try_to_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_dv_timings *timings,
+ int try_cnt)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int i, cnt = 0, fail_cnt = 0, ret = 0;
+ bool from_dma = false;
+
+ hdmirx_set_negative_pol(hdmirx_dev, false);
+ for (i = 0; i < try_cnt; i++) {
+ ret = hdmirx_get_detected_timings(hdmirx_dev, timings, from_dma);
+ if (ret) {
+ cnt = 0;
+ fail_cnt++;
+ if (fail_cnt > 3) {
+ hdmirx_set_negative_pol(hdmirx_dev, true);
+ from_dma = true;
+ }
+ } else {
+ cnt++;
+ }
+ if (cnt >= 5)
+ break;
+
+ usleep_range(10 * 1000, 10 * 1100);
+ }
+
+ if (try_cnt > 8 && cnt < 5)
+ v4l2_dbg(1, debug, v4l2_dev, "%s: res not stable\n", __func__);
+
+ return ret;
+}
+
+static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_dv_timings timings;
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ const struct v4l2_event ev_src_chg = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ if (hdmirx_try_to_get_timings(hdmirx_dev, &timings, 20)) {
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(20));
+ return;
+ }
+
+ hdmirx_dev->got_timing = true;
+ v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
+ v4l2_event_queue(&stream->vdev, &ev_src_chg);
+}
+
+static void hdmirx_set_ddr_store_fmt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ enum ddr_store_fmt store_fmt;
+ u32 dma_cfg1;
+
+ switch (hdmirx_dev->pix_fmt) {
+ case HDMIRX_RGB888:
+ store_fmt = STORE_RGB888;
+ break;
+ case HDMIRX_YUV444:
+ store_fmt = STORE_YUV444_8BIT;
+ break;
+ case HDMIRX_YUV422:
+ store_fmt = STORE_YUV422_8BIT;
+ break;
+ case HDMIRX_YUV420:
+ store_fmt = STORE_YUV420_8BIT;
+ break;
+ default:
+ store_fmt = STORE_RGB888;
+ break;
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
+ DDR_STORE_FORMAT_MASK, DDR_STORE_FORMAT(store_fmt));
+ dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
+ __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
+}
+
+static int hdmirx_wait_lock_and_get_timing(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 mu_status, scdc_status, dma_st10, cmu_st;
+ u32 i;
+
+ for (i = 0; i < 300; i++) {
+ mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
+ scdc_status = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS3);
+ dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
+ cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
+
+ if ((mu_status & TMDSVALID_STABLE_ST) &&
+ (dma_st10 & HDMIRX_LOCK) &&
+ (cmu_st & TMDSQPCLK_LOCKED_ST))
+ break;
+
+ if (!tx_5v_power_present(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: HDMI pull out, return\n", __func__);
+ return -1;
+ }
+
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+ }
+
+ if (i == 300) {
+ v4l2_err(v4l2_dev, "%s: signal not lock, tmds_clk_ratio:%d\n",
+ __func__, hdmirx_dev->tmds_clk_ratio);
+ v4l2_err(v4l2_dev, "%s: mu_st:%#x, scdc_st:%#x, dma_st10:%#x\n",
+ __func__, mu_status, scdc_status, dma_st10);
+ return -1;
+ }
+
+ v4l2_info(v4l2_dev, "%s: signal lock ok, i:%d\n", __func__, i);
+ hdmirx_writel(hdmirx_dev, GLOBAL_SWRESET_REQUEST, DATAPATH_SWRESETREQ);
+
+ reinit_completion(&hdmirx_dev->avi_pkt_rcv);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, PKTDEC_AVIIF_RCV_IRQ);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->avi_pkt_rcv,
+ msecs_to_jiffies(300))) {
+ v4l2_err(v4l2_dev, "%s wait avi_pkt_rcv failed\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, 0);
+ }
+
+ usleep_range(50 * 1000, 50 * 1010);
+ hdmirx_format_change(hdmirx_dev);
+
+ return 0;
+}
+
+static void hdmirx_dma_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_set_ddr_store_fmt(hdmirx_dev);
+
+ /* Note: uv_swap, rb can not swap, doc err*/
+ if (hdmirx_dev->cur_fmt_fourcc != V4L2_PIX_FMT_NV16)
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, RB_SWAP_EN);
+ else
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, 0);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
+ LOCK_FRAME_NUM_MASK,
+ LOCK_FRAME_NUM(2));
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
+ UV_WID_MASK | Y_WID_MASK | ABANDON_EN,
+ UV_WID(1) | Y_WID(2) | ABANDON_EN);
+}
+
+static void hdmirx_submodule_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ /* Note: if not config HDCP2_CONFIG, there will be some errors; */
+ hdmirx_update_bits(hdmirx_dev, HDCP2_CONFIG,
+ HDCP2_SWITCH_OVR_VALUE |
+ HDCP2_SWITCH_OVR_EN,
+ HDCP2_SWITCH_OVR_EN);
+ hdmirx_scdc_init(hdmirx_dev);
+ hdmirx_controller_init(hdmirx_dev);
+}
+
+static int hdmirx_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = 0;
+ strscpy(input->name, "HDMI IN", sizeof(input->name));
+ input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+
+ return 0;
+}
+
+static int hdmirx_get_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int hdmirx_set_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i)
+ return -EINVAL;
+ return 0;
+}
+
+static void hdmirx_set_fmt(struct hdmirx_stream *stream,
+ struct v4l2_pix_format_mplane *pixm, bool try)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_bt_timings *bt = &hdmirx_dev->timings.bt;
+ const struct v4l2_format_info *finfo;
+ unsigned int imagesize = 0;
+ int i;
+
+ memset(&pixm->plane_fmt[0], 0, sizeof(struct v4l2_plane_pix_format));
+ finfo = v4l2_format_info(pixm->pixelformat);
+ if (!finfo) {
+ finfo = v4l2_format_info(V4L2_PIX_FMT_BGR24);
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: set_fmt:%#x not supported, use def_fmt:%x\n",
+ __func__, pixm->pixelformat, finfo->format);
+ }
+
+ if (!bt->width || !bt->height)
+ v4l2_dbg(1, debug, v4l2_dev, "%s: invalid resolution:%#xx%#x\n",
+ __func__, bt->width, bt->height);
+
+ pixm->pixelformat = finfo->format;
+ pixm->width = bt->width;
+ pixm->height = bt->height;
+ pixm->num_planes = finfo->mem_planes;
+ pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
+ pixm->colorspace = V4L2_COLORSPACE_SRGB;
+ pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+
+ if (bt->interlaced == V4L2_DV_INTERLACED)
+ pixm->field = V4L2_FIELD_INTERLACED_TB;
+ else
+ pixm->field = V4L2_FIELD_NONE;
+
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ v4l2_fill_pixfmt_mp(pixm, finfo->format, pixm->width, pixm->height);
+
+ for (i = 0; i < pixm->num_planes; i++) {
+ struct v4l2_plane_pix_format *plane_fmt;
+ int width, height, bpl, size, bpp = 0;
+
+ if (!i) {
+ width = pixm->width;
+ height = pixm->height;
+ } else {
+ width = pixm->width / finfo->hdiv;
+ height = pixm->height / finfo->vdiv;
+ }
+
+ switch (finfo->format) {
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_BGR24:
+ bpp = finfo->bpp[i];
+ break;
+ default:
+ v4l2_dbg(1, debug, v4l2_dev,
+ "fourcc: %#x is not supported\n",
+ finfo->format);
+ break;
+ }
+
+ bpl = ALIGN(width * bpp, MEMORY_ALIGN_ROUND_UP_BYTES);
+ size = bpl * height;
+ imagesize += size;
+
+ if (finfo->mem_planes > i) {
+ /* Set bpl and size for each mplane */
+ plane_fmt = pixm->plane_fmt + i;
+ plane_fmt->bytesperline = bpl;
+ plane_fmt->sizeimage = size;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev,
+ "C-Plane %i size: %d, Total imagesize: %d\n",
+ i, size, imagesize);
+ }
+
+ /* Convert to non-MPLANE format as we want to unify non-MPLANE and MPLANE */
+ if (finfo->mem_planes == 1)
+ pixm->plane_fmt[0].sizeimage = imagesize;
+
+ if (!try) {
+ stream->out_finfo = finfo;
+ stream->pixm = *pixm;
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: req(%d, %d), out(%d, %d), fmt:%#x\n", __func__,
+ pixm->width, pixm->height, stream->pixm.width,
+ stream->pixm.height, finfo->format);
+ }
+}
+
+static int hdmirx_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ if (f->index >= 1)
+ return -EINVAL;
+
+ f->pixelformat = hdmirx_dev->cur_fmt_fourcc;
+
+ return 0;
+}
+
+static int hdmirx_s_fmt_vid_cap_mplane(struct file *file,
+ void *priv, struct v4l2_format *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (vb2_is_busy(&stream->buf_queue)) {
+ v4l2_err(v4l2_dev, "%s: queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ hdmirx_set_fmt(stream, &f->fmt.pix_mp, false);
+
+ return 0;
+}
+
+static int hdmirx_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_pix_format_mplane pixm = {};
+
+ pixm.pixelformat = hdmirx_dev->cur_fmt_fourcc;
+ hdmirx_set_fmt(stream, &pixm, true);
+ f->fmt.pix_mp = pixm;
+
+ return 0;
+}
+
+static int hdmirx_g_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 dma_cfg1;
+
+ *timings = hdmirx_dev->timings;
+ dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
+ __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
+
+ return 0;
+}
+
+static int hdmirx_s_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (!timings)
+ return -EINVAL;
+
+ if (debug)
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
+ "s_dv_timings: ", timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: timings out of range\n", __func__);
+ return -ERANGE;
+ }
+
+ /* Check if the timings are part of the CEA-861 timings. */
+ v4l2_find_dv_timings_cap(timings, &hdmirx_timings_cap, 0, NULL, NULL);
+
+ if (v4l2_match_dv_timings(&hdmirx_dev->timings, timings, 0, false)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: no change\n", __func__);
+ return 0;
+ }
+
+ /*
+ * Changing the timings implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(&stream->buf_queue))
+ return -EBUSY;
+
+ hdmirx_dev->timings = *timings;
+ /* Update the internal format */
+ hdmirx_set_fmt(stream, &stream->pixm, false);
+
+ return 0;
+}
+
+static int hdmirx_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct device *dev = stream->hdmirx_dev->dev;
+
+ strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strscpy(cap->card, dev->driver->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int hdmirx_queue_setup(struct vb2_queue *queue,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_ctxs[])
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ const struct v4l2_format_info *out_finfo;
+ u32 i, height;
+
+ pixm = &stream->pixm;
+ out_finfo = stream->out_finfo;
+
+ if (!num_planes || !out_finfo) {
+ v4l2_err(v4l2_dev, "%s: out_fmt not set\n", __func__);
+ return -EINVAL;
+ }
+
+ if (*num_planes) {
+ if (*num_planes != pixm->num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < *num_planes; i++)
+ if (sizes[i] < pixm->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ return 0;
+ }
+
+ *num_planes = out_finfo->mem_planes;
+ height = pixm->height;
+
+ for (i = 0; i < out_finfo->mem_planes; i++)
+ sizes[i] = pixm->plane_fmt[i].sizeimage;
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: count %d, size %d\n",
+ v4l2_type_names[queue->type], *num_buffers, sizes[0]);
+
+ return 0;
+}
+
+/*
+ * The vb2_buffer are stored in hdmirx_buffer, in order to unify
+ * mplane buffer and none-mplane buffer.
+ */
+static void hdmirx_buf_queue(struct vb2_buffer *vb)
+{
+ const struct v4l2_format_info *out_finfo;
+ struct vb2_v4l2_buffer *vbuf;
+ struct hdmirx_buffer *hdmirx_buf;
+ struct vb2_queue *queue;
+ struct hdmirx_stream *stream;
+ const struct v4l2_pix_format_mplane *pixm;
+ unsigned long lock_flags = 0;
+ int i;
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ hdmirx_buf = container_of(vbuf, struct hdmirx_buffer, vb);
+ queue = vb->vb2_queue;
+ stream = vb2_get_drv_priv(queue);
+ pixm = &stream->pixm;
+ out_finfo = stream->out_finfo;
+
+ memset(hdmirx_buf->buff_addr, 0, sizeof(hdmirx_buf->buff_addr));
+
+ /*
+ * If mplanes > 1, every c-plane has its own m-plane,
+ * otherwise, multiple c-planes are in the same m-plane
+ */
+ for (i = 0; i < out_finfo->mem_planes; i++)
+ hdmirx_buf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ if (out_finfo->mem_planes == 1) {
+ if (out_finfo->comp_planes == 1) {
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_CBCR] =
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_Y];
+ } else {
+ for (i = 0; i < out_finfo->comp_planes - 1; i++)
+ hdmirx_buf->buff_addr[i + 1] =
+ hdmirx_buf->buff_addr[i] +
+ pixm->plane_fmt[i].bytesperline *
+ pixm->height;
+ }
+ }
+
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+ list_add_tail(&hdmirx_buf->queue, &stream->buf_head);
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+}
+
+static void return_all_buffers(struct hdmirx_stream *stream,
+ enum vb2_buffer_state state)
+{
+ struct hdmirx_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stream->vbq_lock, flags);
+ if (stream->curr_buf)
+ list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
+ if (stream->next_buf && stream->next_buf != stream->curr_buf)
+ list_add_tail(&stream->next_buf->queue, &stream->buf_head);
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+
+ while (!list_empty(&stream->buf_head)) {
+ buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer, queue);
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(&stream->vbq_lock, flags);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ spin_lock_irqsave(&stream->vbq_lock, flags);
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, flags);
+}
+
+static void hdmirx_stop_streaming(struct vb2_queue *queue)
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ v4l2_info(v4l2_dev, "stream start stopping\n");
+ mutex_lock(&hdmirx_dev->stream_lock);
+ WRITE_ONCE(stream->stopping, true);
+
+ /* wait last irq to return the buffer */
+ ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
+ msecs_to_jiffies(500));
+ if (!ret) {
+ v4l2_err(v4l2_dev, "%s: timeout waiting last irq\n",
+ __func__);
+ WRITE_ONCE(stream->stopping, false);
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ return_all_buffers(stream, VB2_BUF_STATE_ERROR);
+ mutex_unlock(&hdmirx_dev->stream_lock);
+ v4l2_info(v4l2_dev, "stream stopping finished\n");
+}
+
+static int hdmirx_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ unsigned long lock_flags = 0;
+ int line_flag;
+
+ if (!hdmirx_dev->got_timing) {
+ v4l2_dbg(1, debug, v4l2_dev, "timing is invalid\n");
+ return 0;
+ }
+
+ mutex_lock(&hdmirx_dev->stream_lock);
+ stream->frame_idx = 0;
+ stream->line_flag_int_cnt = 0;
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+ stream->irq_stat = 0;
+ queue->min_queued_buffers = 1;
+
+ WRITE_ONCE(stream->stopping, false);
+
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+ if (!stream->curr_buf) {
+ if (!list_empty(&stream->buf_head)) {
+ stream->curr_buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer,
+ queue);
+ list_del(&stream->curr_buf->queue);
+ } else {
+ stream->curr_buf = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+
+ v4l2_dbg(2, debug, v4l2_dev,
+ "%s: start_stream cur_buf y_addr:%#x, uv_addr:%#x\n",
+ __func__, stream->curr_buf->buff_addr[HDMIRX_PLANE_Y],
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_Y]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+
+ if (bt->height) {
+ if (bt->interlaced == V4L2_DV_INTERLACED)
+ line_flag = bt->height / 4;
+ else
+ line_flag = bt->height / 2;
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
+ LINE_FLAG_NUM_MASK,
+ LINE_FLAG_NUM(line_flag));
+ } else {
+ v4l2_err(v4l2_dev, "height err: %d\n", bt->height);
+ }
+
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, CED_DYN_CONTROL, 0x1);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, HDMIRX_DMA_EN);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: enable dma", __func__);
+ mutex_unlock(&hdmirx_dev->stream_lock);
+
+ return 0;
+}
+
+/* vb2 queue */
+static const struct vb2_ops hdmirx_vb2_ops = {
+ .queue_setup = hdmirx_queue_setup,
+ .buf_queue = hdmirx_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = hdmirx_stop_streaming,
+ .start_streaming = hdmirx_start_streaming,
+};
+
+static int hdmirx_init_vb2_queue(struct vb2_queue *q,
+ struct hdmirx_stream *stream,
+ enum v4l2_buf_type buf_type)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ q->type = buf_type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stream;
+ q->ops = &hdmirx_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct hdmirx_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &stream->vlock;
+ q->dev = hdmirx_dev->dev;
+ /*
+ * rk3588 doesn't use iommu and works only with dma buffers
+ * that are physically contiguous in memory.
+ */
+ q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
+ return vb2_queue_init(q);
+}
+
+/* video device */
+static const struct v4l2_ioctl_ops hdmirx_v4l2_ioctl_ops = {
+ .vidioc_querycap = hdmirx_querycap,
+ .vidioc_try_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = hdmirx_s_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_cap = hdmirx_enum_fmt_vid_cap_mplane,
+
+ .vidioc_s_dv_timings = hdmirx_s_dv_timings,
+ .vidioc_g_dv_timings = hdmirx_g_dv_timings,
+ .vidioc_enum_dv_timings = hdmirx_enum_dv_timings,
+ .vidioc_query_dv_timings = hdmirx_query_dv_timings,
+ .vidioc_dv_timings_cap = hdmirx_dv_timings_cap,
+ .vidioc_enum_input = hdmirx_enum_input,
+ .vidioc_g_input = hdmirx_get_input,
+ .vidioc_s_input = hdmirx_set_input,
+ .vidioc_g_edid = hdmirx_get_edid,
+ .vidioc_s_edid = hdmirx_set_edid,
+ .vidioc_g_parm = hdmirx_g_parm,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .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 = hdmirx_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations hdmirx_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct video_device *vdev = &stream->vdev;
+ int ret = 0;
+
+ strscpy(vdev->name, "stream_hdmirx", sizeof(vdev->name));
+ INIT_LIST_HEAD(&stream->buf_head);
+ spin_lock_init(&stream->vbq_lock);
+ mutex_init(&stream->vlock);
+ init_waitqueue_head(&stream->wq_stopped);
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+
+ vdev->ioctl_ops = &hdmirx_v4l2_ioctl_ops;
+ vdev->release = video_device_release_empty;
+ vdev->fops = &hdmirx_fops;
+ vdev->minor = -1;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &stream->vlock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ video_set_drvdata(vdev, stream);
+ vdev->vfl_dir = VFL_DIR_RX;
+
+ hdmirx_init_vb2_queue(&stream->buf_queue, stream,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ vdev->queue = &stream->buf_queue;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ hdmirx_reset_dma(hdmirx_dev);
+ hdmirx_dev->got_timing = false;
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_res_change,
+ msecs_to_jiffies(50));
+}
+
+static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (status & (CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ)) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: avp0_st:%#x\n",
+ __func__, status);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_FORCE, 0x0);
+}
+
+static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (status & DEFRAMER_VSYNC_THR_REACHED_IRQ) {
+ v4l2_info(v4l2_dev, "Vertical Sync threshold reached interrupt %#x", status);
+ hdmirx_update_bits(hdmirx_dev, AVPUNIT_1_INT_MASK_N,
+ DEFRAMER_VSYNC_THR_REACHED_MASK_N, 0);
+ *handled = true;
+ }
+}
+
+static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "mu0_st:%#x\n", status);
+ if (status & TIMER_BASE_LOCKED_IRQ) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TIMER_BASE_LOCKED_IRQ, 0);
+ complete(&hdmirx_dev->timer_base_lock);
+ *handled = true;
+ }
+
+ if (status & TMDSQPCLK_OFF_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_OFF_CHG\n", __func__);
+ *handled = true;
+ }
+
+ if (status & TMDSQPCLK_LOCKED_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_LOCKED_CHG\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_FORCE, 0x0);
+}
+
+static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "mu2_st:%#x\n", status);
+ if (status & PHYCREG_CR_WRITE_DONE) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_WRITE_DONE, 0);
+ complete(&hdmirx_dev->cr_write_done);
+ *handled = true;
+ }
+
+ if (status & TMDSVALID_STABLE_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_FORCE, 0x0);
+}
+
+static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: pk2_st:%#x\n", __func__, status);
+ if (status & PKTDEC_AVIIF_RCV_IRQ) {
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, 0);
+ complete(&hdmirx_dev->avi_pkt_rcv);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: AVIIF_RCV_IRQ\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+}
+
+static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: scdc_st:%#x\n", __func__, status);
+ if (status & SCDCTMDSCCFG_CHG) {
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+}
+
+static irqreturn_t hdmirx_hdmi_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct arm_smccc_res res;
+ u32 mu0_st, mu2_st, pk2_st, scdc_st, avp1_st, avp0_st;
+ u32 mu0_mask, mu2_mask, pk2_mask, scdc_mask, avp1_msk, avp0_msk;
+ bool handled = false;
+
+ mu0_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_MASK_N);
+ mu2_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_MASK_N);
+ pk2_mask = hdmirx_readl(hdmirx_dev, PKT_2_INT_MASK_N);
+ scdc_mask = hdmirx_readl(hdmirx_dev, SCDC_INT_MASK_N);
+ mu0_st = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_STATUS);
+ mu2_st = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_STATUS);
+ pk2_st = hdmirx_readl(hdmirx_dev, PKT_2_INT_STATUS);
+ scdc_st = hdmirx_readl(hdmirx_dev, SCDC_INT_STATUS);
+ avp0_st = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_STATUS);
+ avp1_st = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_STATUS);
+ avp0_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_MASK_N);
+ avp1_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_MASK_N);
+ mu0_st &= mu0_mask;
+ mu2_st &= mu2_mask;
+ pk2_st &= pk2_mask;
+ avp1_st &= avp1_msk;
+ avp0_st &= avp0_msk;
+ scdc_st &= scdc_mask;
+
+ if (avp0_st)
+ avpunit_0_int_handler(hdmirx_dev, avp0_st, &handled);
+ if (avp1_st)
+ avpunit_1_int_handler(hdmirx_dev, avp1_st, &handled);
+ if (mu0_st)
+ mainunit_0_int_handler(hdmirx_dev, mu0_st, &handled);
+ if (mu2_st)
+ mainunit_2_int_handler(hdmirx_dev, mu2_st, &handled);
+ if (pk2_st)
+ pkt_2_int_handler(hdmirx_dev, pk2_st, &handled);
+ if (scdc_st)
+ scdc_int_handler(hdmirx_dev, scdc_st, &handled);
+
+ if (!handled) {
+ v4l2_dbg(2, debug, v4l2_dev, "%s: hdmi irq not handled", __func__);
+ v4l2_dbg(2, debug, v4l2_dev,
+ "avp0:%#x, avp1:%#x, mu0:%#x, mu2:%#x, pk2:%#x, scdc:%#x\n",
+ avp0_st, avp1_st, mu0_st, mu2_st, pk2_st, scdc_st);
+ }
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: en_fiq", __func__);
+ arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
+ RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void hdmirx_vb_done(struct hdmirx_stream *stream,
+ struct vb2_v4l2_buffer *vb_done)
+{
+ const struct v4l2_format_info *finfo = stream->out_finfo;
+ u32 i;
+
+ /* Dequeue a filled buffer */
+ for (i = 0; i < finfo->mem_planes; i++) {
+ vb2_set_plane_payload(&vb_done->vb2_buf, i,
+ stream->pixm.plane_fmt[i].sizeimage);
+ }
+
+ vb_done->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ bool *handled)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ struct vb2_v4l2_buffer *vb_done = NULL;
+
+ if (!(stream->irq_stat) && !(stream->irq_stat & LINE_FLAG_INT_EN))
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: last time have no line_flag_irq\n", __func__);
+
+ if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
+ goto DMA_IDLE_OUT;
+
+ if (bt->interlaced != V4L2_DV_INTERLACED ||
+ !(stream->line_flag_int_cnt % 2)) {
+ if (stream->next_buf) {
+ if (stream->curr_buf)
+ vb_done = &stream->curr_buf->vb;
+
+ if (vb_done) {
+ vb_done->vb2_buf.timestamp = ktime_get_ns();
+ vb_done->sequence = stream->frame_idx;
+ hdmirx_vb_done(stream, vb_done);
+ stream->frame_idx++;
+ if (stream->frame_idx == 30)
+ v4l2_info(v4l2_dev, "rcv frames\n");
+ }
+
+ stream->curr_buf = NULL;
+ if (stream->next_buf) {
+ stream->curr_buf = stream->next_buf;
+ stream->next_buf = NULL;
+ }
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: next_buf NULL, skip vb_done\n", __func__);
+ }
+ }
+
+DMA_IDLE_OUT:
+ *handled = true;
+}
+
+static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ bool *handled)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ u32 dma_cfg6;
+
+ stream->line_flag_int_cnt++;
+ if (!(stream->irq_stat) && !(stream->irq_stat & HDMIRX_DMA_IDLE_INT))
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: last have no dma_idle_irq\n", __func__);
+ dma_cfg6 = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
+ if (!(dma_cfg6 & HDMIRX_DMA_EN)) {
+ v4l2_dbg(2, debug, v4l2_dev, "%s: dma not on\n", __func__);
+ goto LINE_FLAG_OUT;
+ }
+
+ if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
+ goto LINE_FLAG_OUT;
+
+ if (bt->interlaced != V4L2_DV_INTERLACED ||
+ !(stream->line_flag_int_cnt % 2)) {
+ if (!stream->next_buf) {
+ spin_lock(&stream->vbq_lock);
+ if (!list_empty(&stream->buf_head)) {
+ stream->next_buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer,
+ queue);
+ list_del(&stream->next_buf->queue);
+ } else {
+ stream->next_buf = NULL;
+ }
+ spin_unlock(&stream->vbq_lock);
+
+ if (stream->next_buf) {
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
+ stream->next_buf->buff_addr[HDMIRX_PLANE_Y]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
+ stream->next_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: no buffer is available\n", __func__);
+ }
+ }
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: interlace:%d, line_flag_int_cnt:%d\n",
+ __func__, bt->interlaced, stream->line_flag_int_cnt);
+ }
+
+LINE_FLAG_OUT:
+ *handled = true;
+}
+
+static irqreturn_t hdmirx_dma_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 dma_stat1, dma_stat13;
+ bool handled = false;
+
+ dma_stat1 = hdmirx_readl(hdmirx_dev, DMA_STATUS1);
+ dma_stat13 = hdmirx_readl(hdmirx_dev, DMA_STATUS13);
+ v4l2_dbg(3, debug, v4l2_dev, "dma_irq st1:%#x, st13:%d\n",
+ dma_stat1, dma_stat13);
+
+ if (READ_ONCE(stream->stopping)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: stop stream\n", __func__);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ WRITE_ONCE(stream->stopping, false);
+ wake_up(&stream->wq_stopped);
+ return IRQ_HANDLED;
+ }
+
+ if (dma_stat1 & HDMIRX_DMA_IDLE_INT)
+ dma_idle_int_handler(hdmirx_dev, &handled);
+
+ if (dma_stat1 & LINE_FLAG_INT_EN)
+ line_flag_int_handler(hdmirx_dev, &handled);
+
+ if (!handled)
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: dma irq not handled, dma_stat1:%#x\n",
+ __func__, dma_stat1);
+
+ stream->irq_stat = dma_stat1;
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+
+ return IRQ_HANDLED;
+}
+
+static void hdmirx_plugin(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct arm_smccc_res res;
+ int ret;
+
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_heartbeat,
+ msecs_to_jiffies(10));
+ arm_smccc_smc(SIP_WDT_CFG, WDT_START, 0, 0, 0, 0, 0, 0, &res);
+ hdmirx_submodule_init(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
+ POWERPROVIDED);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ hdmirx_phy_config(hdmirx_dev);
+ ret = hdmirx_wait_lock_and_get_timing(hdmirx_dev);
+ if (ret) {
+ hdmirx_plugout(hdmirx_dev);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(200));
+ return;
+ }
+ hdmirx_dma_config(hdmirx_dev);
+ hdmirx_interrupts_setup(hdmirx_dev, true);
+}
+
+static void hdmirx_delayed_work_hotplug(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ bool plugin;
+
+ hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
+ delayed_work_hotplug.work);
+
+ mutex_lock(&hdmirx_dev->work_lock);
+ hdmirx_dev->got_timing = false;
+ plugin = tx_5v_power_present(hdmirx_dev);
+ v4l2_ctrl_s_ctrl(hdmirx_dev->detect_tx_5v_ctrl, plugin);
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
+ __func__, plugin);
+
+ if (plugin)
+ hdmirx_plugin(hdmirx_dev);
+ else
+ hdmirx_plugout(hdmirx_dev);
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+}
+
+static void hdmirx_delayed_work_res_change(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ bool plugin;
+
+ hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
+ delayed_work_res_change.work);
+
+ mutex_lock(&hdmirx_dev->work_lock);
+ plugin = tx_5v_power_present(hdmirx_dev);
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
+ __func__, plugin);
+ if (plugin) {
+ hdmirx_interrupts_setup(hdmirx_dev, false);
+ hdmirx_submodule_init(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
+ POWERPROVIDED);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ hdmirx_phy_config(hdmirx_dev);
+
+ if (hdmirx_wait_lock_and_get_timing(hdmirx_dev)) {
+ hdmirx_plugout(hdmirx_dev);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(200));
+ } else {
+ hdmirx_dma_config(hdmirx_dev);
+ hdmirx_interrupts_setup(hdmirx_dev, true);
+ }
+ }
+ mutex_unlock(&hdmirx_dev->work_lock);
+}
+
+static void hdmirx_delayed_work_heartbeat(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct snps_hdmirx_dev *hdmirx_dev = container_of(dwork,
+ struct snps_hdmirx_dev,
+ delayed_work_heartbeat);
+
+ queue_work(system_highpri_wq, &hdmirx_dev->work_wdt_config);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_heartbeat, HZ);
+}
+
+static void hdmirx_work_wdt_config(struct work_struct *work)
+{
+ struct arm_smccc_res res;
+ struct snps_hdmirx_dev *hdmirx_dev = container_of(work,
+ struct snps_hdmirx_dev,
+ work_wdt_config);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ arm_smccc_smc(SIP_WDT_CFG, WDT_PING, 0, 0, 0, 0, 0, 0, &res);
+ v4l2_dbg(3, debug, v4l2_dev, "hb\n");
+}
+
+static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ u32 val;
+
+ val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val);
+
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(10));
+
+ return IRQ_HANDLED;
+}
+
+static const struct hdmirx_cec_ops hdmirx_cec_ops = {
+ .write = hdmirx_writel,
+ .read = hdmirx_readl,
+};
+
+static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ int ret;
+
+ hdmirx_dev->num_clks = devm_clk_bulk_get_all(dev, &hdmirx_dev->clks);
+ if (hdmirx_dev->num_clks < 1)
+ return -ENODEV;
+
+ hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
+ hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
+ hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
+ hdmirx_dev->resets[HDMIRX_RST_BIU].id = "biu";
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, HDMIRX_NUM_RST,
+ hdmirx_dev->resets);
+ if (ret < 0) {
+ dev_err(dev, "failed to get reset controls\n");
+ return ret;
+ }
+
+ hdmirx_dev->detect_5v_gpio =
+ devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
+
+ if (IS_ERR(hdmirx_dev->detect_5v_gpio)) {
+ dev_err(dev, "failed to get hdmirx hot plug detection gpio\n");
+ return PTR_ERR(hdmirx_dev->detect_5v_gpio);
+ }
+
+ hdmirx_dev->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(hdmirx_dev->grf)) {
+ dev_err(dev, "failed to get rockchip,grf\n");
+ return PTR_ERR(hdmirx_dev->grf);
+ }
+
+ hdmirx_dev->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,vo1-grf");
+ if (IS_ERR(hdmirx_dev->vo1_grf)) {
+ dev_err(dev, "failed to get rockchip,vo1-grf\n");
+ return PTR_ERR(hdmirx_dev->vo1_grf);
+ }
+
+ hdmirx_dev->hpd_trigger_level = !device_property_read_bool(dev, "hpd-is-active-low");
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret)
+ dev_warn(dev, "No reserved memory for HDMIRX, use default CMA\n");
+
+ return 0;
+}
+
+static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, SCDC_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, CEC_INT_MASK_N, 0);
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, HDCP_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, HDCP_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff);
+}
+
+static int hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0);
+
+ regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2,
+ (HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) |
+ ((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16));
+ /*
+ * Some interrupts are enabled by default, so we disable
+ * all interrupts and clear interrupts status first.
+ */
+ hdmirx_disable_all_interrupts(hdmirx_dev);
+
+ return 0;
+}
+
+static void hdmirx_load_default_edid(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ int ret;
+ struct v4l2_edid def_edid;
+
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ /* disable hpd and write edid */
+ def_edid.pad = 0;
+ def_edid.start_block = 0;
+ def_edid.blocks = EDID_NUM_BLOCKS_MAX;
+
+ if (IS_ENABLED(CONFIG_HDMIRX_LOAD_DEFAULT_EDID))
+ def_edid.edid = edid_init_data_340M;
+ else
+ def_edid.edid = hdmirx_dev->edid;
+
+ ret = hdmirx_write_edid(hdmirx_dev, &def_edid, true);
+ if (ret)
+ dev_err(hdmirx_dev->dev, "%s: write edid failed\n", __func__);
+}
+
+static void hdmirx_disable_irq(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
+
+ disable_irq(hdmirx_dev->hdmi_irq);
+ disable_irq(hdmirx_dev->dma_irq);
+ disable_irq(hdmirx_dev->det_irq);
+
+ arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_DIS,
+ RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
+
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_hotplug);
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_res_change);
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_heartbeat);
+ flush_work(&hdmirx_dev->work_wdt_config);
+
+ arm_smccc_smc(SIP_WDT_CFG, WDT_STOP, 0, 0, 0, 0, 0, 0, &res);
+}
+
+static int hdmirx_disable(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ clk_bulk_disable_unprepare(hdmirx_dev->num_clks, hdmirx_dev->clks);
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: suspend\n", __func__);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static void hdmirx_enable_irq(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+ enable_irq(hdmirx_dev->dma_irq);
+ enable_irq(hdmirx_dev->det_irq);
+
+ arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
+ RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
+
+ queue_delayed_work(system_unbound_wq, &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(20));
+}
+
+static int hdmirx_enable(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: resume\n", __func__);
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(hdmirx_dev->num_clks, hdmirx_dev->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable hdmirx bulk clks: %d\n", ret);
+ return ret;
+ }
+
+ reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+ usleep_range(150, 160);
+ reset_control_bulk_deassert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+ usleep_range(150, 160);
+
+ return 0;
+}
+
+static int hdmirx_suspend(struct device *dev)
+{
+ hdmirx_disable_irq(dev);
+
+ return hdmirx_disable(dev);
+}
+
+static int hdmirx_resume(struct device *dev)
+{
+ int ret = hdmirx_enable(dev);
+
+ if (ret)
+ return ret;
+
+ hdmirx_enable_irq(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops snps_hdmirx_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(hdmirx_suspend, hdmirx_resume)
+};
+
+static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
+ struct platform_device *pdev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ int ret, irq;
+
+ irq = platform_get_irq_byname(pdev, "hdmi");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get hdmi irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->hdmi_irq = irq;
+ ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
+ "rk_hdmirx-hdmi", hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request hdmi irq\n");
+ return ret;
+ }
+
+ irq = platform_get_irq_byname(pdev, "dma");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get dma irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->dma_irq = irq;
+ ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
+ IRQF_ONESHOT, "rk_hdmirx-dma",
+ hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request dma irq\n");
+ return ret;
+ }
+
+ irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->det_irq = irq;
+ ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "rk_hdmirx-5v", hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
+ struct platform_device *pdev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ struct hdmirx_cec_data cec_data;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, "cec");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get cec irq\n");
+ return irq;
+ }
+
+ hdmirx_dev->cec_notifier = cec_notifier_conn_register(dev, NULL, NULL);
+ if (!hdmirx_dev->cec_notifier)
+ return -EINVAL;
+
+ cec_data.hdmirx = hdmirx_dev;
+ cec_data.dev = hdmirx_dev->dev;
+ cec_data.ops = &hdmirx_cec_ops;
+ cec_data.irq = irq;
+
+ hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
+ if (!hdmirx_dev->cec) {
+ cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hdmirx_probe(struct platform_device *pdev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ struct device *dev = &pdev->dev;
+ struct v4l2_ctrl_handler *hdl;
+ struct hdmirx_stream *stream;
+ struct v4l2_device *v4l2_dev;
+ int ret;
+
+ hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
+ if (!hdmirx_dev)
+ return -ENOMEM;
+
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ hdmirx_dev->dev = dev;
+ dev_set_drvdata(dev, hdmirx_dev);
+
+ ret = hdmirx_parse_dt(hdmirx_dev);
+ if (ret)
+ return ret;
+
+ ret = hdmirx_setup_irq(hdmirx_dev, pdev);
+ if (ret)
+ return ret;
+
+ hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmirx_dev->regs))
+ return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
+ "failed to remap regs resource\n");
+
+ mutex_init(&hdmirx_dev->stream_lock);
+ mutex_init(&hdmirx_dev->work_lock);
+ spin_lock_init(&hdmirx_dev->rst_lock);
+
+ init_completion(&hdmirx_dev->cr_write_done);
+ init_completion(&hdmirx_dev->timer_base_lock);
+ init_completion(&hdmirx_dev->avi_pkt_rcv);
+
+ INIT_WORK(&hdmirx_dev->work_wdt_config, hdmirx_work_wdt_config);
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
+ hdmirx_delayed_work_hotplug);
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
+ hdmirx_delayed_work_res_change);
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_heartbeat,
+ hdmirx_delayed_work_heartbeat);
+
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ hdmirx_dev->timings = cea640x480;
+
+ hdmirx_enable(dev);
+ hdmirx_init(hdmirx_dev);
+
+ v4l2_dev = &hdmirx_dev->v4l2_dev;
+ strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
+
+ hdl = &hdmirx_dev->hdl;
+ v4l2_ctrl_handler_init(hdl, 1);
+
+ hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_RX_POWER_PRESENT,
+ 0, 1, 0, 0);
+
+ hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, 0,
+ V4L2_CID_DV_RX_RGB_RANGE,
+ V4L2_DV_RGB_RANGE_FULL, 0,
+ V4L2_DV_RGB_RANGE_AUTO);
+
+ hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (hdl->error) {
+ dev_err(dev, "v4l2 ctrl handler init failed\n");
+ ret = hdl->error;
+ goto err_pm;
+ }
+ hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
+
+ ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err(dev, "register v4l2 device failed\n");
+ goto err_hdl;
+ }
+
+ stream = &hdmirx_dev->stream;
+ stream->hdmirx_dev = hdmirx_dev;
+ ret = hdmirx_register_stream_vdev(stream);
+ if (ret < 0) {
+ dev_err(dev, "register video device failed\n");
+ goto err_unreg_v4l2_dev;
+ }
+
+ ret = hdmirx_register_cec(hdmirx_dev, pdev);
+ if (ret)
+ goto err_unreg_video_dev;
+
+ hdmirx_load_default_edid(hdmirx_dev);
+
+ hdmirx_enable_irq(dev);
+
+ return 0;
+
+err_unreg_video_dev:
+ video_unregister_device(&hdmirx_dev->stream.vdev);
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
+err_hdl:
+ v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
+err_pm:
+ hdmirx_disable(dev);
+
+ return ret;
+}
+
+static void hdmirx_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ snps_hdmirx_cec_unregister(hdmirx_dev->cec);
+ cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
+
+ hdmirx_disable_irq(dev);
+
+ video_unregister_device(&hdmirx_dev->stream.vdev);
+ v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
+ v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
+
+ hdmirx_disable(dev);
+
+ reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+
+ of_reserved_mem_device_release(dev);
+}
+
+static const struct of_device_id hdmirx_id[] = {
+ { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, hdmirx_id);
+
+static struct platform_driver hdmirx_driver = {
+ .probe = hdmirx_probe,
+ .remove = hdmirx_remove,
+ .driver = {
+ .name = "snps_hdmirx",
+ .of_match_table = hdmirx_id,
+ .pm = &snps_hdmirx_pm_ops,
+ }
+};
+module_platform_driver(hdmirx_driver);
+
+MODULE_DESCRIPTION("Rockchip HDMI Receiver Driver");
+MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
+MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
new file mode 100644
index 000000000000..220ab99ca611
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#ifndef DW_HDMIRX_H
+#define DW_HDMIRX_H
+
+#include <linux/bitops.h>
+
+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
+#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
+
+/* SYS_GRF */
+#define SYS_GRF_SOC_CON1 0x0304
+#define HDMIRXPHY_SRAM_EXT_LD_DONE BIT(1)
+#define HDMIRXPHY_SRAM_BYPASS BIT(0)
+#define SYS_GRF_SOC_STATUS1 0x0384
+#define HDMIRXPHY_SRAM_INIT_DONE BIT(10)
+#define SYS_GRF_CHIP_ID 0x0600
+
+/* VO1_GRF */
+#define VO1_GRF_VO1_CON2 0x0008
+#define HDMIRX_SDAIN_MSK BIT(2)
+#define HDMIRX_SCLIN_MSK BIT(1)
+
+/* HDMIRX PHY */
+#define SUP_DIG_ANA_CREGS_SUP_ANA_NC 0x004f
+
+#define LANE0_DIG_ASIC_RX_OVRD_OUT_0 0x100f
+#define LANE1_DIG_ASIC_RX_OVRD_OUT_0 0x110f
+#define LANE2_DIG_ASIC_RX_OVRD_OUT_0 0x120f
+#define LANE3_DIG_ASIC_RX_OVRD_OUT_0 0x130f
+#define ASIC_ACK_OVRD_EN BIT(1)
+#define ASIC_ACK BIT(0)
+
+#define LANE0_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x104a
+#define LANE1_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x114a
+#define LANE2_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x124a
+#define LANE3_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x134a
+#define FREQ_TUNE_START_VAL_MASK GENMASK(9, 0)
+#define FREQ_TUNE_START_VAL(x) UPDATE(x, 9, 0)
+
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG 0x20c4
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_ADAPT_REF_FOM 0x20c7
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG 0x20e9
+#define CDR_SETTING_BOUNDARY_3_DEFAULT 0x52da
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG 0x20ea
+#define CDR_SETTING_BOUNDARY_4_DEFAULT 0x43cd
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG 0x20eb
+#define CDR_SETTING_BOUNDARY_5_DEFAULT 0x35b3
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG 0x20fb
+#define CDR_SETTING_BOUNDARY_6_DEFAULT 0x2799
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG 0x20fc
+#define CDR_SETTING_BOUNDARY_7_DEFAULT 0x1b65
+
+#define RAWLANE0_DIG_PCS_XF_RX_OVRD_OUT 0x300e
+#define RAWLANE1_DIG_PCS_XF_RX_OVRD_OUT 0x310e
+#define RAWLANE2_DIG_PCS_XF_RX_OVRD_OUT 0x320e
+#define RAWLANE3_DIG_PCS_XF_RX_OVRD_OUT 0x330e
+#define PCS_ACK_WRITE_SELECT BIT(14)
+#define PCS_EN_CTL BIT(1)
+#define PCS_ACK BIT(0)
+
+#define RAWLANE0_DIG_AON_FAST_FLAGS 0x305c
+#define RAWLANE1_DIG_AON_FAST_FLAGS 0x315c
+#define RAWLANE2_DIG_AON_FAST_FLAGS 0x325c
+#define RAWLANE3_DIG_AON_FAST_FLAGS 0x335c
+
+/* HDMIRX Ctrler */
+#define GLOBAL_SWRESET_REQUEST 0x0020
+#define DATAPATH_SWRESETREQ BIT(12)
+#define GLOBAL_SWENABLE 0x0024
+#define PHYCTRL_ENABLE BIT(21)
+#define CEC_ENABLE BIT(16)
+#define TMDS_ENABLE BIT(13)
+#define DATAPATH_ENABLE BIT(12)
+#define PKTFIFO_ENABLE BIT(11)
+#define AVPUNIT_ENABLE BIT(8)
+#define MAIN_ENABLE BIT(0)
+#define GLOBAL_TIMER_REF_BASE 0x0028
+#define CORE_CONFIG 0x0050
+#define CMU_CONFIG0 0x0060
+#define TMDSQPCLK_STABLE_FREQ_MARGIN_MASK GENMASK(30, 16)
+#define TMDSQPCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 30, 16)
+#define AUDCLK_STABLE_FREQ_MARGIN_MASK GENMASK(11, 9)
+#define AUDCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 11, 9)
+#define CMU_STATUS 0x007c
+#define TMDSQPCLK_LOCKED_ST BIT(4)
+#define CMU_TMDSQPCLK_FREQ 0x0084
+#define PHY_CONFIG 0x00c0
+#define LDO_AFE_PROG_MASK GENMASK(24, 23)
+#define LDO_AFE_PROG(x) UPDATE(x, 24, 23)
+#define LDO_PWRDN BIT(21)
+#define TMDS_CLOCK_RATIO BIT(16)
+#define RXDATA_WIDTH BIT(15)
+#define REFFREQ_SEL_MASK GENMASK(11, 9)
+#define REFFREQ_SEL(x) UPDATE(x, 11, 9)
+#define HDMI_DISABLE BIT(8)
+#define PHY_PDDQ BIT(1)
+#define PHY_RESET BIT(0)
+#define PHY_STATUS 0x00c8
+#define HDMI_DISABLE_ACK BIT(1)
+#define PDDQ_ACK BIT(0)
+#define PHYCREG_CONFIG0 0x00e0
+#define PHYCREG_CR_PARA_SELECTION_MODE_MASK GENMASK(1, 0)
+#define PHYCREG_CR_PARA_SELECTION_MODE(x) UPDATE(x, 1, 0)
+#define PHYCREG_CONFIG1 0x00e4
+#define PHYCREG_CONFIG2 0x00e8
+#define PHYCREG_CONFIG3 0x00ec
+#define PHYCREG_CONTROL 0x00f0
+#define PHYCREG_CR_PARA_WRITE_P BIT(1)
+#define PHYCREG_CR_PARA_READ_P BIT(0)
+#define PHYCREG_STATUS 0x00f4
+
+#define MAINUNIT_STATUS 0x0150
+#define TMDSVALID_STABLE_ST BIT(1)
+#define DESCRAND_EN_CONTROL 0x0210
+#define SCRAMB_EN_SEL_QST_MASK GENMASK(1, 0)
+#define SCRAMB_EN_SEL_QST(x) UPDATE(x, 1, 0)
+#define DESCRAND_SYNC_CONTROL 0x0214
+#define RECOVER_UNSYNC_STREAM_QST BIT(0)
+#define DESCRAND_SYNC_SEQ_CONFIG 0x022c
+#define DESCRAND_SYNC_SEQ_ERR_CNT_EN BIT(0)
+#define DESCRAND_SYNC_SEQ_STATUS 0x0234
+#define DEFRAMER_CONFIG0 0x0270
+#define VS_CNT_THR_QST_MASK GENMASK(27, 20)
+#define VS_CNT_THR_QST(x) UPDATE(x, 27, 20)
+#define HS_POL_QST_MASK GENMASK(19, 18)
+#define HS_POL_QST(x) UPDATE(x, 19, 18)
+#define VS_POL_QST_MASK GENMASK(17, 16)
+#define VS_POL_QST(x) UPDATE(x, 17, 16)
+#define VS_REMAPFILTER_EN_QST BIT(8)
+#define VS_FILTER_ORDER_QST_MASK GENMASK(1, 0)
+#define VS_FILTER_ORDER_QST(x) UPDATE(x, 1, 0)
+#define DEFRAMER_VSYNC_CNT_CLEAR 0x0278
+#define VSYNC_CNT_CLR_P BIT(0)
+#define DEFRAMER_STATUS 0x027c
+#define OPMODE_STS_MASK GENMASK(6, 4)
+#define I2C_SLAVE_CONFIG1 0x0164
+#define I2C_SDA_OUT_HOLD_VALUE_QST_MASK GENMASK(15, 8)
+#define I2C_SDA_OUT_HOLD_VALUE_QST(x) UPDATE(x, 15, 8)
+#define I2C_SDA_IN_HOLD_VALUE_QST_MASK GENMASK(7, 0)
+#define I2C_SDA_IN_HOLD_VALUE_QST(x) UPDATE(x, 7, 0)
+#define OPMODE_STS_MASK GENMASK(6, 4)
+#define REPEATER_QST BIT(28)
+#define FASTREAUTH_QST BIT(27)
+#define FEATURES_1DOT1_QST BIT(26)
+#define FASTI2C_QST BIT(25)
+#define EESS_CTL_THR_QST_MASK GENMASK(19, 16)
+#define EESS_CTL_THR_QST(x) UPDATE(x, 19, 16)
+#define OESS_CTL3_THR_QST_MASK GENMASK(11, 8)
+#define OESS_CTL3_THR_QST(x) UPDATE(x, 11, 8)
+#define EESS_OESS_SEL_QST_MASK GENMASK(5, 4)
+#define EESS_OESS_SEL_QST(x) UPDATE(x, 5, 4)
+#define KEY_DECRYPT_EN_QST BIT(0)
+#define KEY_DECRYPT_SEED_QST_MASK GENMASK(15, 0)
+#define KEY_DECRYPT_SEED_QST(x) UPDATE(x, 15, 0)
+#define HDCP_INT_CLEAR 0x50d8
+#define HDCP_1_INT_CLEAR 0x50e8
+#define HDCP2_CONFIG 0x02f0
+#define HDCP2_SWITCH_OVR_VALUE BIT(2)
+#define HDCP2_SWITCH_OVR_EN BIT(1)
+
+#define VIDEO_CONFIG2 0x042c
+#define VPROC_VSYNC_POL_OVR_VALUE BIT(19)
+#define VPROC_VSYNC_POL_OVR_EN BIT(18)
+#define VPROC_HSYNC_POL_OVR_VALUE BIT(17)
+#define VPROC_HSYNC_POL_OVR_EN BIT(16)
+#define VPROC_FMT_OVR_VALUE_MASK GENMASK(6, 4)
+#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
+#define VPROC_FMT_OVR_EN BIT(0)
+
+#define AFIFO_FILL_RESTART BIT(0)
+#define AFIFO_INIT_P BIT(0)
+#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
+#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
+#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
+#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
+#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
+#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
+#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
+
+#define AFIFO_UNDERFLOW_ST BIT(25)
+#define AFIFO_OVERFLOW_ST BIT(24)
+
+#define SPEAKER_ALLOC_OVR_EN BIT(16)
+#define I2S_BPCUV_EN BIT(4)
+#define SPDIF_EN BIT(2)
+#define I2S_EN BIT(1)
+#define AFIFO_THR_PASS_DEMUTEMASK_N BIT(24)
+#define AVMUTE_DEMUTEMASK_N BIT(16)
+#define AFIFO_THR_MUTE_LOW_MUTEMASK_N BIT(9)
+#define AFIFO_THR_MUTE_HIGH_MUTEMASK_N BIT(8)
+#define AVMUTE_MUTEMASK_N BIT(0)
+#define SCDC_CONFIG 0x0580
+#define HPDLOW BIT(1)
+#define POWERPROVIDED BIT(0)
+#define SCDC_REGBANK_STATUS1 0x058c
+#define SCDC_TMDSBITCLKRATIO BIT(1)
+#define SCDC_REGBANK_STATUS3 0x0594
+#define SCDC_REGBANK_CONFIG0 0x05c0
+#define SCDC_SINKVERSION_QST_MASK GENMASK(7, 0)
+#define SCDC_SINKVERSION_QST(x) UPDATE(x, 7, 0)
+#define AGEN_LAYOUT BIT(4)
+#define AGEN_SPEAKER_ALLOC GENMASK(15, 8)
+
+#define CED_CONFIG 0x0760
+#define CED_VIDDATACHECKEN_QST BIT(27)
+#define CED_DATAISCHECKEN_QST BIT(26)
+#define CED_GBCHECKEN_QST BIT(25)
+#define CED_CTRLCHECKEN_QST BIT(24)
+#define CED_CHLOCKMAXER_QST_MASK GENMASK(14, 0)
+#define CED_CHLOCKMAXER_QST(x) UPDATE(x, 14, 0)
+#define CED_DYN_CONFIG 0x0768
+#define CED_DYN_CONTROL 0x076c
+#define PKTEX_BCH_ERRFILT_CONFIG 0x07c4
+#define PKTEX_CHKSUM_ERRFILT_CONFIG 0x07c8
+
+#define PKTDEC_ACR_PH2_1 0x1100
+#define PKTDEC_ACR_PB3_0 0x1104
+#define PKTDEC_ACR_PB7_4 0x1108
+#define PKTDEC_AVIIF_PH2_1 0x1200
+#define PKTDEC_AVIIF_PB3_0 0x1204
+#define PKTDEC_AVIIF_PB7_4 0x1208
+#define VIC_VAL_MASK GENMASK(6, 0)
+#define PKTDEC_AVIIF_PB11_8 0x120c
+#define PKTDEC_AVIIF_PB15_12 0x1210
+#define PKTDEC_AVIIF_PB19_16 0x1214
+#define PKTDEC_AVIIF_PB23_20 0x1218
+#define PKTDEC_AVIIF_PB27_24 0x121c
+
+#define PKTFIFO_CONFIG 0x1500
+#define PKTFIFO_STORE_FILT_CONFIG 0x1504
+#define PKTFIFO_THR_CONFIG0 0x1508
+#define PKTFIFO_THR_CONFIG1 0x150c
+#define PKTFIFO_CONTROL 0x1510
+
+#define VMON_STATUS1 0x1580
+#define VMON_STATUS2 0x1584
+#define VMON_STATUS3 0x1588
+#define VMON_STATUS4 0x158c
+#define VMON_STATUS5 0x1590
+#define VMON_STATUS6 0x1594
+#define VMON_STATUS7 0x1598
+#define VMON_ILACE_DETECT BIT(4)
+
+#define CEC_TX_CONTROL 0x2000
+#define CEC_STATUS 0x2004
+#define CEC_CONFIG 0x2008
+#define RX_AUTO_DRIVE_ACKNOWLEDGE BIT(9)
+#define CEC_ADDR 0x200c
+#define CEC_TX_COUNT 0x2020
+#define CEC_TX_DATA3_0 0x2024
+#define CEC_RX_COUNT_STATUS 0x2040
+#define CEC_RX_DATA3_0 0x2044
+#define CEC_LOCK_CONTROL 0x2054
+#define CEC_RXQUAL_BITTIME_CONFIG 0x2060
+#define CEC_RX_BITTIME_CONFIG 0x2064
+#define CEC_TX_BITTIME_CONFIG 0x2068
+
+#define DMA_CONFIG1 0x4400
+#define UV_WID_MASK GENMASK(31, 28)
+#define UV_WID(x) UPDATE(x, 31, 28)
+#define Y_WID_MASK GENMASK(27, 24)
+#define Y_WID(x) UPDATE(x, 27, 24)
+#define DDR_STORE_FORMAT_MASK GENMASK(15, 12)
+#define DDR_STORE_FORMAT(x) UPDATE(x, 15, 12)
+#define ABANDON_EN BIT(0)
+#define DMA_CONFIG2 0x4404
+#define DMA_CONFIG3 0x4408
+#define DMA_CONFIG4 0x440c // dma irq en
+#define DMA_CONFIG5 0x4410 // dma irq clear status
+#define LINE_FLAG_INT_EN BIT(8)
+#define HDMIRX_DMA_IDLE_INT BIT(7)
+#define HDMIRX_LOCK_DISABLE_INT BIT(6)
+#define LAST_FRAME_AXI_UNFINISH_INT_EN BIT(5)
+#define FIFO_OVERFLOW_INT_EN BIT(2)
+#define FIFO_UNDERFLOW_INT_EN BIT(1)
+#define HDMIRX_AXI_ERROR_INT_EN BIT(0)
+#define DMA_CONFIG6 0x4414
+#define RB_SWAP_EN BIT(9)
+#define HSYNC_TOGGLE_EN BIT(5)
+#define VSYNC_TOGGLE_EN BIT(4)
+#define HDMIRX_DMA_EN BIT(1)
+#define DMA_CONFIG7 0x4418
+#define LINE_FLAG_NUM_MASK GENMASK(31, 16)
+#define LINE_FLAG_NUM(x) UPDATE(x, 31, 16)
+#define LOCK_FRAME_NUM_MASK GENMASK(11, 0)
+#define LOCK_FRAME_NUM(x) UPDATE(x, 11, 0)
+#define DMA_CONFIG8 0x441c
+#define REG_MIRROR_EN BIT(0)
+#define DMA_CONFIG9 0x4420
+#define DMA_CONFIG10 0x4424
+#define DMA_CONFIG11 0x4428
+#define EDID_READ_EN_MASK BIT(8)
+#define EDID_READ_EN(x) UPDATE(x, 8, 8)
+#define EDID_WRITE_EN_MASK BIT(7)
+#define EDID_WRITE_EN(x) UPDATE(x, 7, 7)
+#define EDID_SLAVE_ADDR_MASK GENMASK(6, 0)
+#define EDID_SLAVE_ADDR(x) UPDATE(x, 6, 0)
+#define DMA_STATUS1 0x4430 // dma irq status
+#define DMA_STATUS2 0x4434
+#define DMA_STATUS3 0x4438
+#define DMA_STATUS4 0x443c
+#define DMA_STATUS5 0x4440
+#define DMA_STATUS6 0x4444
+#define DMA_STATUS7 0x4448
+#define DMA_STATUS8 0x444c
+#define DMA_STATUS9 0x4450
+#define DMA_STATUS10 0x4454
+#define HDMIRX_LOCK BIT(3)
+#define DMA_STATUS11 0x4458
+#define HDMIRX_TYPE_MASK GENMASK(8, 7)
+#define HDMIRX_COLOR_DEPTH_MASK GENMASK(6, 3)
+#define HDMIRX_FORMAT_MASK GENMASK(2, 0)
+#define DMA_STATUS12 0x445c
+#define DMA_STATUS13 0x4460
+#define DMA_STATUS14 0x4464
+
+#define MAINUNIT_INTVEC_INDEX 0x5000
+#define MAINUNIT_0_INT_STATUS 0x5010
+#define CECRX_NOTIFY_ERR BIT(12)
+#define CECRX_EOM BIT(11)
+#define CECTX_DRIVE_ERR BIT(10)
+#define CECRX_BUSY BIT(9)
+#define CECTX_BUSY BIT(8)
+#define CECTX_FRAME_DISCARDED BIT(5)
+#define CECTX_NRETRANSMIT_FAIL BIT(4)
+#define CECTX_LINE_ERR BIT(3)
+#define CECTX_ARBLOST BIT(2)
+#define CECTX_NACK BIT(1)
+#define CECTX_DONE BIT(0)
+#define MAINUNIT_0_INT_MASK_N 0x5014
+#define MAINUNIT_0_INT_CLEAR 0x5018
+#define MAINUNIT_0_INT_FORCE 0x501c
+#define TIMER_BASE_LOCKED_IRQ BIT(26)
+#define TMDSQPCLK_OFF_CHG BIT(5)
+#define TMDSQPCLK_LOCKED_CHG BIT(4)
+#define MAINUNIT_1_INT_STATUS 0x5020
+#define MAINUNIT_1_INT_MASK_N 0x5024
+#define MAINUNIT_1_INT_CLEAR 0x5028
+#define MAINUNIT_1_INT_FORCE 0x502c
+#define MAINUNIT_2_INT_STATUS 0x5030
+#define MAINUNIT_2_INT_MASK_N 0x5034
+#define MAINUNIT_2_INT_CLEAR 0x5038
+#define MAINUNIT_2_INT_FORCE 0x503c
+#define PHYCREG_CR_READ_DONE BIT(11)
+#define PHYCREG_CR_WRITE_DONE BIT(10)
+#define TMDSVALID_STABLE_CHG BIT(1)
+
+#define AVPUNIT_0_INT_STATUS 0x5040
+#define AVPUNIT_0_INT_MASK_N 0x5044
+#define AVPUNIT_0_INT_CLEAR 0x5048
+#define AVPUNIT_0_INT_FORCE 0x504c
+#define CED_DYN_CNT_CH2_IRQ BIT(22)
+#define CED_DYN_CNT_CH1_IRQ BIT(21)
+#define CED_DYN_CNT_CH0_IRQ BIT(20)
+#define AVPUNIT_1_INT_STATUS 0x5050
+#define DEFRAMER_VSYNC_THR_REACHED_IRQ BIT(1)
+#define AVPUNIT_1_INT_MASK_N 0x5054
+#define DEFRAMER_VSYNC_THR_REACHED_MASK_N BIT(1)
+#define DEFRAMER_VSYNC_MASK_N BIT(0)
+#define AVPUNIT_1_INT_CLEAR 0x5058
+#define DEFRAMER_VSYNC_THR_REACHED_CLEAR BIT(1)
+#define PKT_0_INT_STATUS 0x5080
+#define PKTDEC_ACR_CHG_IRQ BIT(3)
+#define PKT_0_INT_MASK_N 0x5084
+#define PKTDEC_ACR_CHG_MASK_N BIT(3)
+#define PKT_0_INT_CLEAR 0x5088
+#define PKT_1_INT_STATUS 0x5090
+#define PKT_1_INT_MASK_N 0x5094
+#define PKT_1_INT_CLEAR 0x5098
+#define PKT_2_INT_STATUS 0x50a0
+#define PKTDEC_ACR_RCV_IRQ BIT(3)
+#define PKT_2_INT_MASK_N 0x50a4
+#define PKTDEC_AVIIF_RCV_IRQ BIT(11)
+#define PKTDEC_ACR_RCV_MASK_N BIT(3)
+#define PKT_2_INT_CLEAR 0x50a8
+#define PKTDEC_AVIIF_RCV_CLEAR BIT(11)
+#define PKTDEC_ACR_RCV_CLEAR BIT(3)
+#define SCDC_INT_STATUS 0x50c0
+#define SCDC_INT_MASK_N 0x50c4
+#define SCDC_INT_CLEAR 0x50c8
+#define SCDCTMDSCCFG_CHG BIT(2)
+
+#define CEC_INT_STATUS 0x5100
+#define CEC_INT_MASK_N 0x5104
+#define CEC_INT_CLEAR 0x5108
+
+#endif
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
new file mode 100644
index 000000000000..9f67e2080bb6
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chips.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#include "snps_hdmirx.h"
+#include "snps_hdmirx_cec.h"
+
+static void hdmirx_cec_write(struct hdmirx_cec *cec, int reg, u32 val)
+{
+ cec->ops->write(cec->hdmirx, reg, val);
+}
+
+static u32 hdmirx_cec_read(struct hdmirx_cec *cec, int reg)
+{
+ return cec->ops->read(cec->hdmirx, reg);
+}
+
+static void hdmirx_cec_update_bits(struct hdmirx_cec *cec, int reg, u32 mask,
+ u32 data)
+{
+ u32 val = hdmirx_cec_read(cec, reg) & ~mask;
+
+ val |= (data & mask);
+ hdmirx_cec_write(cec, reg, val);
+}
+
+static int hdmirx_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ cec->addresses = 0;
+ else
+ cec->addresses |= BIT(logical_addr) | BIT(15);
+
+ hdmirx_cec_write(cec, CEC_ADDR, cec->addresses);
+
+ return 0;
+}
+
+/* signal_free_time is handled by the Synopsys Designware
+ * HDMIRX Controller hardware.
+ */
+static int hdmirx_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+ u32 data[4] = {0};
+ int i, data_len, msg_len;
+
+ msg_len = msg->len;
+
+ hdmirx_cec_write(cec, CEC_TX_COUNT, msg_len - 1);
+ for (i = 0; i < msg_len; i++)
+ data[i / 4] |= msg->msg[i] << (i % 4) * 8;
+
+ data_len = DIV_ROUND_UP(msg_len, 4);
+
+ for (i = 0; i < data_len; i++)
+ hdmirx_cec_write(cec, CEC_TX_DATA3_0 + i * 4, data[i]);
+
+ hdmirx_cec_write(cec, CEC_TX_CONTROL, 0x1);
+
+ return 0;
+}
+
+static irqreturn_t hdmirx_cec_hardirq(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+ u32 stat = hdmirx_cec_read(cec, CEC_INT_STATUS);
+ irqreturn_t ret = IRQ_HANDLED;
+ u32 val;
+
+ if (!stat)
+ return IRQ_NONE;
+
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, stat);
+
+ if (stat & CECTX_LINE_ERR) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_DONE) {
+ cec->tx_status = CEC_TX_STATUS_OK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_NACK) {
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_ARBLOST) {
+ cec->tx_status = CEC_TX_STATUS_ARB_LOST;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (stat & CECRX_EOM) {
+ unsigned int len, i;
+
+ val = hdmirx_cec_read(cec, CEC_RX_COUNT_STATUS);
+ /* rxbuffer locked status */
+ if ((val & 0x80))
+ return ret;
+
+ len = (val & 0xf) + 1;
+ if (len > sizeof(cec->rx_msg.msg))
+ len = sizeof(cec->rx_msg.msg);
+
+ for (i = 0; i < len; i++) {
+ if (!(i % 4))
+ val = hdmirx_cec_read(cec, CEC_RX_DATA3_0 + i / 4 * 4);
+ cec->rx_msg.msg[i] = (val >> ((i % 4) * 8)) & 0xff;
+ }
+
+ cec->rx_msg.len = len;
+ smp_wmb(); /* receive RX msg */
+ cec->rx_done = true;
+ hdmirx_cec_write(cec, CEC_LOCK_CONTROL, 0x1);
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t hdmirx_cec_thread(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (cec->tx_done) {
+ cec->tx_done = false;
+ cec_transmit_attempt_done(adap, cec->tx_status);
+ }
+ if (cec->rx_done) {
+ cec->rx_done = false;
+ smp_rmb(); /* RX msg has been received */
+ cec_received_msg(adap, &cec->rx_msg);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hdmirx_cec_enable(struct cec_adapter *adap, bool enable)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (!enable) {
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, 0);
+ if (cec->ops->disable)
+ cec->ops->disable(cec->hdmirx);
+ } else {
+ unsigned int irqs;
+
+ hdmirx_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+ if (cec->ops->enable)
+ cec->ops->enable(cec->hdmirx);
+ hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
+
+ irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
+ }
+
+ return 0;
+}
+
+static const struct cec_adap_ops hdmirx_cec_ops = {
+ .adap_enable = hdmirx_cec_enable,
+ .adap_log_addr = hdmirx_cec_log_addr,
+ .adap_transmit = hdmirx_cec_transmit,
+};
+
+static void hdmirx_cec_del(void *data)
+{
+ struct hdmirx_cec *cec = data;
+
+ cec_delete_adapter(cec->adap);
+}
+
+struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data)
+{
+ struct hdmirx_cec *cec;
+ unsigned int irqs;
+ int ret;
+
+ /*
+ * Our device is just a convenience - we want to link to the real
+ * hardware device here, so that userspace can see the association
+ * between the HDMI hardware and its associated CEC chardev.
+ */
+ cec = devm_kzalloc(data->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return NULL;
+
+ cec->dev = data->dev;
+ cec->irq = data->irq;
+ cec->ops = data->ops;
+ cec->hdmirx = data->hdmirx;
+
+ hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
+ hdmirx_cec_update_bits(cec, CEC_CONFIG, RX_AUTO_DRIVE_ACKNOWLEDGE,
+ RX_AUTO_DRIVE_ACKNOWLEDGE);
+
+ hdmirx_cec_write(cec, CEC_TX_COUNT, 0);
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, ~0);
+
+ cec->adap = cec_allocate_adapter(&hdmirx_cec_ops, cec, "snps-hdmirx",
+ CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+ CEC_CAP_RC | CEC_CAP_PASSTHROUGH |
+ CEC_CAP_MONITOR_ALL,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(cec->adap)) {
+ dev_err(cec->dev, "cec adap allocate failed\n");
+ return NULL;
+ }
+
+ /* override the module pointer */
+ cec->adap->owner = THIS_MODULE;
+
+ ret = devm_add_action(cec->dev, hdmirx_cec_del, cec);
+ if (ret) {
+ cec_delete_adapter(cec->adap);
+ return NULL;
+ }
+
+ irq_set_status_flags(cec->irq, IRQ_NOAUTOEN);
+
+ ret = devm_request_threaded_irq(cec->dev, cec->irq,
+ hdmirx_cec_hardirq,
+ hdmirx_cec_thread, IRQF_ONESHOT,
+ "rk_hdmirx_cec", cec->adap);
+ if (ret) {
+ dev_err(cec->dev, "cec irq request failed\n");
+ return NULL;
+ }
+
+ cec->notify = cec_notifier_cec_adap_register(cec->dev,
+ NULL, cec->adap);
+ if (!cec->notify) {
+ dev_err(cec->dev, "cec notify register failed\n");
+ return NULL;
+ }
+
+ ret = cec_register_adapter(cec->adap, cec->dev);
+ if (ret < 0) {
+ dev_err(cec->dev, "cec register adapter failed\n");
+ cec_unregister_adapter(cec->adap);
+ return NULL;
+ }
+
+ irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
+
+ /*
+ * CEC documentation says we must not call cec_delete_adapter
+ * after a successful call to cec_register_adapter().
+ */
+ devm_remove_action(cec->dev, hdmirx_cec_del, cec);
+
+ enable_irq(cec->irq);
+
+ return cec;
+}
+
+void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec)
+{
+ disable_irq(cec->irq);
+
+ cec_unregister_adapter(cec->adap);
+}
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
new file mode 100644
index 000000000000..c55c403cdb9f
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chips.com>
+ */
+
+#ifndef DW_HDMI_RX_CEC_H
+#define DW_HDMI_RX_CEC_H
+
+struct snps_hdmirx_dev;
+
+struct hdmirx_cec_ops {
+ void (*write)(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val);
+ u32 (*read)(struct snps_hdmirx_dev *hdmirx_dev, int reg);
+ void (*enable)(struct snps_hdmirx_dev *hdmirx);
+ void (*disable)(struct snps_hdmirx_dev *hdmirx);
+};
+
+struct hdmirx_cec_data {
+ struct snps_hdmirx_dev *hdmirx;
+ const struct hdmirx_cec_ops *ops;
+ struct device *dev;
+ int irq;
+};
+
+struct hdmirx_cec {
+ struct snps_hdmirx_dev *hdmirx;
+ struct device *dev;
+ const struct hdmirx_cec_ops *ops;
+ u32 addresses;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+ unsigned int tx_status;
+ bool tx_done;
+ bool rx_done;
+ struct cec_notifier *notify;
+ int irq;
+};
+
+struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data);
+void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec);
+
+#endif /* DW_HDMI_RX_CEC_H */
--
2.39.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
2024-07-19 12:40 ` [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver Shreeya Patel
@ 2024-07-19 12:47 ` Christopher Obbard
0 siblings, 0 replies; 40+ messages in thread
From: Christopher Obbard @ 2024-07-19 12:47 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
Hi Shreeya,
On Fri, 2024-07-19 at 18:10 +0530, Shreeya Patel wrote:
> Add an entry for Synopsys DesignWare HDMI Receiver Controller
> Driver.
>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - No change
>
> Changes in v3 :-
> - No change
>
> Changes in v2 :-
> - Add a patch for MAINTAINERS file changes
>
> MAINTAINERS | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1c87b471941c..0f0e1d58abff 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22138,6 +22138,14 @@ F: drivers/net/pcs/pcs-xpcs.c
> F: drivers/net/pcs/pcs-xpcs.h
> F: include/linux/pcs/pcs-xpcs.h
>
> +SYNOPSYS DESIGNWARE HDMI RX CONTROLLER DRIVER
Perhaps a more descriptive name could be "SYNOPSYS DESIGNWARE HDMI RECEIVER
DRIVER" to better match the IP name:
https://www.synopsys.com/dw/ipdir.php?ds=dwc_hdmi_20_csds_rx
> +M: Shreeya Patel <shreeya.patel@collabora.com
Should this not be:
Shreeya Patel <shreeya.patel@collabora.com>
You seem to be missing the final > at the end of the line ?
With the above fixed:
Reviewed-by: Christopher Obbard <chris.obbard@collabora.com>
> +L: linux-media@vger.kernel.org
> +L: kernel@collabora.com
> +S: Maintained
> +F: Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> +F: drivers/media/platform/synopsys/hdmirx/*
> +
> SYNOPSYS DESIGNWARE I2C DRIVER
> M: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
@ 2024-07-19 22:10 ` Rob Herring (Arm)
2024-07-25 9:46 ` Shreeya Patel
2024-07-20 10:44 ` Johan Jonker
` (2 subsequent siblings)
3 siblings, 1 reply; 40+ messages in thread
From: Rob Herring (Arm) @ 2024-07-19 22:10 UTC (permalink / raw)
To: Shreeya Patel
Cc: kernel, mchehab, conor+dt, linux-media, nelson.costa,
linux-arm-kernel, heiko, mturquette, hverkuil, hverkuil-cisco,
linux-rockchip, shawn.wen, sboyd, Dmitry Osipenko, p.zabel,
jose.abreu, linux-kernel, krzk+dt, devicetree, nicolas.dufresne
On Fri, 19 Jul 2024 18:10:30 +0530, Shreeya Patel wrote:
> Document bindings for the Synopsys DesignWare HDMI RX Controller.
>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - No change
>
> Changes in v3 :-
> - Rename hdmirx_cma to hdmi_receiver_cma
> - Add a Reviewed-by tag
>
> Changes in v2 :-
> - Add a description for the hardware
> - Rename resets, vo1 grf and HPD properties
> - Add a proper description for grf and vo1-grf phandles
> - Rename the HDMI Input node name to hdmi-receiver
> - Improve the subject line
> - Include gpio header file in example to fix dt_binding_check failure
>
> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
> 1 file changed, 132 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
Error: Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.example.dts:53.38-39 syntax error
FATAL ERROR: Unable to parse input tree
make[2]: *** [scripts/Makefile.lib:427: Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.example.dtb] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1430: dt_binding_check] Error 2
make: *** [Makefile:240: __sub-make] Error 2
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240719124032.26852-3-shreeya.patel@collabora.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
2024-07-19 22:10 ` Rob Herring (Arm)
@ 2024-07-20 10:44 ` Johan Jonker
2024-07-22 13:53 ` Shreeya Patel
2024-08-25 7:03 ` Krzysztof Kozlowski
2024-08-26 8:19 ` Michael Riesch
3 siblings, 1 reply; 40+ messages in thread
From: Johan Jonker @ 2024-07-20 10:44 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
On 7/19/24 14:40, Shreeya Patel wrote:
> Document bindings for the Synopsys DesignWare HDMI RX Controller.
>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - No change
>
> Changes in v3 :-
> - Rename hdmirx_cma to hdmi_receiver_cma
> - Add a Reviewed-by tag
>
> Changes in v2 :-
> - Add a description for the hardware
> - Rename resets, vo1 grf and HPD properties
> - Add a proper description for grf and vo1-grf phandles
> - Rename the HDMI Input node name to hdmi-receiver
> - Improve the subject line
> - Include gpio header file in example to fix dt_binding_check failure
>
> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
> 1 file changed, 132 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> new file mode 100644
> index 000000000000..96ae1e2d2816
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> @@ -0,0 +1,132 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
> +
> +---
> +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Synopsys DesignWare HDMI RX Controller
> +
> +maintainers:
> + - Shreeya Patel <shreeya.patel@collabora.com>
> +
> +description:
> + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
> + allowing devices to receive and decode high-resolution video streams
> + from external sources like media players, cameras, laptops, etc.
> +
> +properties:
> + compatible:
> + items:
> + - const: rockchip,rk3588-hdmirx-ctrler
> + - const: snps,dw-hdmi-rx
1: Compatible strings must be SoC orientated.
2: In Linux there's no priority in which string will probed first.
What's the point of having a fallback string when there's no common code, but instead only the first string is used?
+static const struct of_device_id hdmirx_id[] = {
+ { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
+ { },
+};
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 3
> +
> + interrupt-names:
> + items:
> + - const: cec
> + - const: hdmi
> + - const: dma
> +
> + clocks:
> + maxItems: 7
> +
> + clock-names:
> + items:
> + - const: aclk
> + - const: audio
> + - const: cr_para
> + - const: pclk
> + - const: ref
> + - const: hclk_s_hdmirx
> + - const: hclk_vo1
> +
> + power-domains:
> + maxItems: 1
> +
> + resets:
> + maxItems: 4
> +
> + reset-names:
> + items:
> + - const: axi
> + - const: apb
> + - const: ref
> + - const: biu
> +
> + memory-region:
> + maxItems: 1
> +
> + hpd-gpios:
> + description: GPIO specifier for HPD.
> + maxItems: 1
> +
> + rockchip,grf:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description:
> + The phandle of the syscon node for the general register file
> + containing HDMIRX PHY status bits.
> +
> + rockchip,vo1-grf:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description:
> + The phandle of the syscon node for the Video Output GRF register
> + to enable EDID transfer through SDAIN and SCLIN.
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - interrupt-names
> + - clocks
> + - clock-names
> + - power-domains
> + - resets
> + - pinctrl-0
> + - hpd-gpios
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> + #include <dt-bindings/power/rk3588-power.h>
> + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
> + hdmi_receiver: hdmi-receiver@fdee0000 {
> + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
> + reg = <0xfdee0000 0x6000>;
> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "cec", "hdmi", "dma";
> + clocks = <&cru ACLK_HDMIRX>,
> + <&cru CLK_HDMIRX_AUD>,
> + <&cru CLK_CR_PARA>,
> + <&cru PCLK_HDMIRX>,
> + <&cru CLK_HDMIRX_REF>,
> + <&cru PCLK_S_HDMIRX>,
> + <&cru HCLK_VO1>;
> + clock-names = "aclk",
> + "audio",
> + "cr_para",
> + "pclk",
> + "ref",
> + "hclk_s_hdmirx",
> + "hclk_vo1";
> + power-domains = <&power RK3588_PD_VO1>;
> + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
> + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
> + reset-names = "axi", "apb", "ref", "biu";
> + memory-region = <&hdmi_receiver_cma>;
> + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
> + pinctrl-names = "default";
> + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
> + };
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
@ 2024-07-20 11:33 ` Johan Jonker
2024-07-22 14:19 ` Shreeya Patel
2024-07-20 23:43 ` George Stark
` (3 subsequent siblings)
4 siblings, 1 reply; 40+ messages in thread
From: Johan Jonker @ 2024-07-20 11:33 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
On 7/19/24 14:40, Shreeya Patel wrote:
> Add initial support for the Synopsys DesignWare HDMI RX
> Controller Driver used by Rockchip RK3588. The driver
> supports:
> - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> - RGB888, YUV422, YUV444 and YCC420 pixel formats
> - CEC
> - EDID configuration
>
> The hardware also has Audio and HDCP capabilities, but these are
> not yet supported by the driver.
>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - Create a separate config option for selecting the EDID
> and enable it by default
> - Improve the comment related to DV timings and move it
> to the side of hdmirx_get_detected_timings
> - Add 100ms delay before pulling the HPD high
> - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> - Drop the bus info from hdmirx_querycap
> - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> - Set queue->min_queued_buffers to 1
> - Drop q->allow_cache_hints = 0; as it's always 0 by default
> - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> - Drop .read = vb2_fop_read as it's not supported by driver
> - Remove redundant edid_init_data_600M
> - Make HPD low when driver is loaded
> - Add support for reading AVI Infoframe
> - Remove msg_len checks from hdmirx_cec_transmit
> - Add info about the CEC compliance test in the cover letter
> - Add arbitration lost status
> - Validate the physical address inside the EDID
>
> Changes in v3 :-
> - Use v4l2-common helper functions
>
> Changes in v2 :-
> - Fix checkpatch --strict warnings
> - Rename resets, vo1-grf and HPD node names as per the DT changes
>
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/synopsys/Kconfig | 3 +
> drivers/media/platform/synopsys/Makefile | 2 +
> .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> .../media/platform/synopsys/hdmirx/Makefile | 4 +
> .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> 10 files changed, 3524 insertions(+)
> create mode 100644 drivers/media/platform/synopsys/Kconfig
> create mode 100644 drivers/media/platform/synopsys/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 85d2627776b6..9287faafdce5 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -85,6 +85,7 @@ source "drivers/media/platform/rockchip/Kconfig"
> source "drivers/media/platform/samsung/Kconfig"
> source "drivers/media/platform/st/Kconfig"
> source "drivers/media/platform/sunxi/Kconfig"
> +source "drivers/media/platform/synopsys/Kconfig"
> source "drivers/media/platform/ti/Kconfig"
> source "drivers/media/platform/verisilicon/Kconfig"
> source "drivers/media/platform/via/Kconfig"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index ace4e34483dd..6fd7db0541c7 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -28,6 +28,7 @@ obj-y += rockchip/
> obj-y += samsung/
> obj-y += st/
> obj-y += sunxi/
> +obj-y += synopsys/
> obj-y += ti/
> obj-y += verisilicon/
> obj-y += via/
> diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
> new file mode 100644
> index 000000000000..4fd521f78425
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/Kconfig
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +source "drivers/media/platform/synopsys/hdmirx/Kconfig"
> diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
> new file mode 100644
> index 000000000000..3b12c574dd67
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-y += hdmirx/
> diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
> new file mode 100644
> index 000000000000..ab569e59300f
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
> @@ -0,0 +1,27 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config VIDEO_SYNOPSYS_HDMIRX
> + tristate "Synopsys DesignWare HDMI Receiver driver"
> + depends on VIDEO_DEV
> + depends on ARCH_ROCKCHIP
> + select MEDIA_CONTROLLER
> + select VIDEO_V4L2_SUBDEV_API
> + select VIDEOBUF2_DMA_CONTIG
> + select CEC_CORE
> + select CEC_NOTIFIER
> + select HDMI
> + help
> + Support for Synopsys HDMI HDMI RX Controller.
> + This driver supports HDMI 2.0 version.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called synopsys_hdmirx.
> +
> +config HDMIRX_LOAD_DEFAULT_EDID
> + bool "Load default EDID"
> + depends on VIDEO_SYNOPSYS_HDMIRX
> + default "y"
> + help
> + Preload the default EDID (Extended Display Identification Data).
> + EDID contains information about the capabilities of the display,
> + such as supported resolutions, refresh rates, and audio formats.
> diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
> new file mode 100644
> index 000000000000..2fa2d9e25300
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
> +
> +obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
[..]
For FTRACE it is needed that all functions start with the same function prefix.
> +static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
> +static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
> +static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
> +static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
> + u32 bit_mask, u32 expect_val, bool is_grf,
> + u32 ms)
> +static void return_all_buffers(struct hdmirx_stream *stream,
> + enum vb2_buffer_state state)
> +static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
> +static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + bool *handled)
> +static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + bool *handled)
[..]
> +static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + int ret, irq;
> +
> + irq = platform_get_irq_byname(pdev, "hdmi");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get hdmi irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->hdmi_irq = irq;
> + ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
> + "rk_hdmirx-hdmi", hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request hdmi irq\n");
> + return ret;
> + }
> +
> + irq = platform_get_irq_byname(pdev, "dma");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get dma irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->dma_irq = irq;
> + ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
> + IRQF_ONESHOT, "rk_hdmirx-dma",
> + hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request dma irq\n");
> + return ret;
> + }
> +
> + irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->det_irq = irq;
> + ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
> + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
> + "rk_hdmirx-5v", hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + struct hdmirx_cec_data cec_data;
> + int irq;
> +
> + irq = platform_get_irq_byname(pdev, "cec");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get cec irq\n");
> + return irq;
> + }
> +
> + hdmirx_dev->cec_notifier = cec_notifier_conn_register(dev, NULL, NULL);
> + if (!hdmirx_dev->cec_notifier)
> + return -EINVAL;
> +
> + cec_data.hdmirx = hdmirx_dev;
> + cec_data.dev = hdmirx_dev->dev;
> + cec_data.ops = &hdmirx_cec_ops;
> + cec_data.irq = irq;
> +
> + hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
> + if (!hdmirx_dev->cec) {
> + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int hdmirx_probe(struct platform_device *pdev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + struct device *dev = &pdev->dev;
> + struct v4l2_ctrl_handler *hdl;
> + struct hdmirx_stream *stream;
> + struct v4l2_device *v4l2_dev;
> + int ret;
> +
> + hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
> + if (!hdmirx_dev)
> + return -ENOMEM;
> +
> + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->dev = dev;
> + dev_set_drvdata(dev, hdmirx_dev);
> +
> + ret = hdmirx_parse_dt(hdmirx_dev);
> + if (ret)
> + return ret;
> +
> + ret = hdmirx_setup_irq(hdmirx_dev, pdev);
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(hdmirx_dev->regs))
> + return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
> + "failed to remap regs resource\n");
> +
> + mutex_init(&hdmirx_dev->stream_lock);
> + mutex_init(&hdmirx_dev->work_lock);
> + spin_lock_init(&hdmirx_dev->rst_lock);
> +
> + init_completion(&hdmirx_dev->cr_write_done);
> + init_completion(&hdmirx_dev->timer_base_lock);
> + init_completion(&hdmirx_dev->avi_pkt_rcv);
> +
> + INIT_WORK(&hdmirx_dev->work_wdt_config, hdmirx_work_wdt_config);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
> + hdmirx_delayed_work_hotplug);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
> + hdmirx_delayed_work_res_change);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_heartbeat,
> + hdmirx_delayed_work_heartbeat);
> +
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + hdmirx_dev->timings = cea640x480;
> +
> + hdmirx_enable(dev);
> + hdmirx_init(hdmirx_dev);
> +
> + v4l2_dev = &hdmirx_dev->v4l2_dev;
> + strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
> +
> + hdl = &hdmirx_dev->hdl;
> + v4l2_ctrl_handler_init(hdl, 1);
> +
> + hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
> + V4L2_CID_DV_RX_POWER_PRESENT,
> + 0, 1, 0, 0);
> +
> + hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, 0,
> + V4L2_CID_DV_RX_RGB_RANGE,
> + V4L2_DV_RGB_RANGE_FULL, 0,
> + V4L2_DV_RGB_RANGE_AUTO);
> +
> + hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + if (hdl->error) {
> + dev_err(dev, "v4l2 ctrl handler init failed\n");
> + ret = hdl->error;
> + goto err_pm;
> + }
> + hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
> +
> + ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
> + if (ret < 0) {
> + dev_err(dev, "register v4l2 device failed\n");
> + goto err_hdl;
> + }
> +
> + stream = &hdmirx_dev->stream;
> + stream->hdmirx_dev = hdmirx_dev;
> + ret = hdmirx_register_stream_vdev(stream);
> + if (ret < 0) {
> + dev_err(dev, "register video device failed\n");
> + goto err_unreg_v4l2_dev;
> + }
> +
> + ret = hdmirx_register_cec(hdmirx_dev, pdev);
> + if (ret)
> + goto err_unreg_video_dev;
> +
> + hdmirx_load_default_edid(hdmirx_dev);
> +
> + hdmirx_enable_irq(dev);
> +
> + return 0;
> +
> +err_unreg_video_dev:
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> +err_unreg_v4l2_dev:
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +err_hdl:
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> +err_pm:
> + hdmirx_disable(dev);
> +
> + return ret;
> +}
> +
> +static void hdmirx_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> +
> + snps_hdmirx_cec_unregister(hdmirx_dev->cec);
> + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> +
> + hdmirx_disable_irq(dev);
> +
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +
> + hdmirx_disable(dev);
> +
> + reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> +
> + of_reserved_mem_device_release(dev);
> +}
> +
> +static const struct of_device_id hdmirx_id[] = {
> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, hdmirx_id);
> +
> +static struct platform_driver hdmirx_driver = {
> + .probe = hdmirx_probe,
> + .remove = hdmirx_remove,
> + .driver = {
> + .name = "snps_hdmirx",
> + .of_match_table = hdmirx_id,
> + .pm = &snps_hdmirx_pm_ops,
> + }
> +};
> +module_platform_driver(hdmirx_driver);
> +
> +MODULE_DESCRIPTION("Rockchip HDMI Receiver Driver");
While the file is called snps_hdmirx.c and the driver name is "snps_hdmirx" the module description calls it a Rockchip driver.
This patch serie somewhat hints at the use of multiple SoCs and possible multiple brands then a more clear separation between common snps and Rockchip (rk3588) SoC specific is needed?
Johan
> +MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
> +MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
> +MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
2024-07-20 11:33 ` Johan Jonker
@ 2024-07-20 23:43 ` George Stark
2024-07-21 8:51 ` Hans Verkuil
` (2 subsequent siblings)
4 siblings, 0 replies; 40+ messages in thread
From: George Stark @ 2024-07-20 23:43 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
Hello everybody
On 7/19/24 15:40, Shreeya Patel wrote:
> Add initial support for the Synopsys DesignWare HDMI RX
> Controller Driver used by Rockchip RK3588. The driver
> supports:
> - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> - RGB888, YUV422, YUV444 and YCC420 pixel formats
> - CEC
> - EDID configuration
>
> The hardware also has Audio and HDCP capabilities, but these are
> not yet supported by the driver.
>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - Create a separate config option for selecting the EDID
> and enable it by default
> - Improve the comment related to DV timings and move it
> to the side of hdmirx_get_detected_timings
> - Add 100ms delay before pulling the HPD high
> - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> - Drop the bus info from hdmirx_querycap
> - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> - Set queue->min_queued_buffers to 1
> - Drop q->allow_cache_hints = 0; as it's always 0 by default
> - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> - Drop .read = vb2_fop_read as it's not supported by driver
> - Remove redundant edid_init_data_600M
> - Make HPD low when driver is loaded
> - Add support for reading AVI Infoframe
> - Remove msg_len checks from hdmirx_cec_transmit
> - Add info about the CEC compliance test in the cover letter
> - Add arbitration lost status
> - Validate the physical address inside the EDID
>
> Changes in v3 :-
> - Use v4l2-common helper functions
>
> Changes in v2 :-
> - Fix checkpatch --strict warnings
> - Rename resets, vo1-grf and HPD node names as per the DT changes
>
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/synopsys/Kconfig | 3 +
> drivers/media/platform/synopsys/Makefile | 2 +
> .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> .../media/platform/synopsys/hdmirx/Makefile | 4 +
> .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> 10 files changed, 3524 insertions(+)
> create mode 100644 drivers/media/platform/synopsys/Kconfig
> create mode 100644 drivers/media/platform/synopsys/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
>
...
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> new file mode 100644
> index 000000000000..1dfecf256393
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> @@ -0,0 +1,2763 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
...
> +}
> +
> +static int hdmirx_probe(struct platform_device *pdev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + struct device *dev = &pdev->dev;
> + struct v4l2_ctrl_handler *hdl;
> + struct hdmirx_stream *stream;
> + struct v4l2_device *v4l2_dev;
> + int ret;
> +
> + hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
> + if (!hdmirx_dev)
> + return -ENOMEM;
> +
> + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->dev = dev;
> + dev_set_drvdata(dev, hdmirx_dev);
> +
> + ret = hdmirx_parse_dt(hdmirx_dev);
> + if (ret)
> + return ret;
> +
> + ret = hdmirx_setup_irq(hdmirx_dev, pdev);
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(hdmirx_dev->regs))
> + return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
> + "failed to remap regs resource\n");
> +
> + mutex_init(&hdmirx_dev->stream_lock);
> + mutex_init(&hdmirx_dev->work_lock);
AFAIK there's no unified opinion on droping mutex_destroy.
Is it so accepted in media subsystem or should we used devm_mutex_init?
> + spin_lock_init(&hdmirx_dev->rst_lock);
> +
> + init_completion(&hdmirx_dev->cr_write_done);
> + init_completion(&hdmirx_dev->timer_base_lock);
> + init_completion(&hdmirx_dev->avi_pkt_rcv);
> +
> + INIT_WORK(&hdmirx_dev->work_wdt_config, hdmirx_work_wdt_config);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
> + hdmirx_delayed_work_hotplug);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
> + hdmirx_delayed_work_res_change);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_heartbeat,
> + hdmirx_delayed_work_heartbeat);
> +
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + hdmirx_dev->timings = cea640x480;
> +
> + hdmirx_enable(dev);
> + hdmirx_init(hdmirx_dev);
> +
> + v4l2_dev = &hdmirx_dev->v4l2_dev;
> + strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
> +
> + hdl = &hdmirx_dev->hdl;
> + v4l2_ctrl_handler_init(hdl, 1);
> +
> + hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
> + V4L2_CID_DV_RX_POWER_PRESENT,
> + 0, 1, 0, 0);
> +
> + hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, 0,
> + V4L2_CID_DV_RX_RGB_RANGE,
> + V4L2_DV_RGB_RANGE_FULL, 0,
> + V4L2_DV_RGB_RANGE_AUTO);
> +
> + hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + if (hdl->error) {
> + dev_err(dev, "v4l2 ctrl handler init failed\n");
> + ret = hdl->error;
> + goto err_pm;
> + }
> + hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
> +
> + ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
> + if (ret < 0) {
> + dev_err(dev, "register v4l2 device failed\n");
> + goto err_hdl;
> + }
> +
> + stream = &hdmirx_dev->stream;
> + stream->hdmirx_dev = hdmirx_dev;
> + ret = hdmirx_register_stream_vdev(stream);
> + if (ret < 0) {
> + dev_err(dev, "register video device failed\n");
> + goto err_unreg_v4l2_dev;
> + }
> +
> + ret = hdmirx_register_cec(hdmirx_dev, pdev);
> + if (ret)
> + goto err_unreg_video_dev;
> +
> + hdmirx_load_default_edid(hdmirx_dev);
> +
> + hdmirx_enable_irq(dev);
> +
> + return 0;
> +
> +err_unreg_video_dev:
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> +err_unreg_v4l2_dev:
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +err_hdl:
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> +err_pm:
> + hdmirx_disable(dev);
> +
> + return ret;
> +}
...
--
Best regards
George
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
2024-07-20 11:33 ` Johan Jonker
2024-07-20 23:43 ` George Stark
@ 2024-07-21 8:51 ` Hans Verkuil
2024-07-25 9:56 ` Shreeya Patel
2024-07-21 15:06 ` Markus Elfring
2024-07-23 8:48 ` Hans Verkuil
4 siblings, 1 reply; 40+ messages in thread
From: Hans Verkuil @ 2024-07-21 8:51 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
On 19/07/2024 14:40, Shreeya Patel wrote:
> Add initial support for the Synopsys DesignWare HDMI RX
> Controller Driver used by Rockchip RK3588. The driver
> supports:
> - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> - RGB888, YUV422, YUV444 and YCC420 pixel formats
> - CEC
> - EDID configuration
>
> The hardware also has Audio and HDCP capabilities, but these are
> not yet supported by the driver.
FYI: if you want to add HDCP support, then please contact me. I have code
available for that (i.e. the public API part), although it is out of date.
But if you want to enable HDCP in this driver, then I would be very happy
to clean it up and post patches for that. It is something we (i.e. Cisco)
have been using for several years now in out-of-tree drivers.
I will try to review this patch in the next few days.
Regards,
Hans
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
` (2 preceding siblings ...)
2024-07-21 8:51 ` Hans Verkuil
@ 2024-07-21 15:06 ` Markus Elfring
2024-07-23 8:48 ` Hans Verkuil
4 siblings, 0 replies; 40+ messages in thread
From: Markus Elfring @ 2024-07-21 15:06 UTC (permalink / raw)
To: Dingxian Wen, Dmitry Osipenko, Shreeya Patel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip, kernel,
Conor Dooley, Hans Verkuil, Heiko Stübner, Jose Abreu,
Krzysztof Kozlowski, Mauro Carvalho Chehab, Michael Turquette,
Nelson Costa, Nicolas Dufresne, Philipp Zabel, Rob Herring,
Stephen Boyd
Cc: LKML
…
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> @@ -0,0 +1,2763 @@
…
> +static void hdmirx_writel(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val)
> +{
…
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + writel(val, hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
…
Under which circumstances would you become interested to apply a statement
like “guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);”?
https://elixir.bootlin.com/linux/v6.10/source/include/linux/spinlock.h#L574
Regards,
Markus
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-20 10:44 ` Johan Jonker
@ 2024-07-22 13:53 ` Shreeya Patel
2024-07-23 11:16 ` Johan Jonker
0 siblings, 1 reply; 40+ messages in thread
From: Shreeya Patel @ 2024-07-22 13:53 UTC (permalink / raw)
To: Johan Jonker
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip, Dmitry Osipenko
On Saturday, July 20, 2024 16:14 IST, Johan Jonker <jbx6244@yandex.com> wrote:
Hi Johan,
>
>
> On 7/19/24 14:40, Shreeya Patel wrote:
> > Document bindings for the Synopsys DesignWare HDMI RX Controller.
> >
> > Reviewed-by: Rob Herring <robh@kernel.org>
> > Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> > Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> > ---
> >
> > Changes in v4 :-
> > - No change
> >
> > Changes in v3 :-
> > - Rename hdmirx_cma to hdmi_receiver_cma
> > - Add a Reviewed-by tag
> >
> > Changes in v2 :-
> > - Add a description for the hardware
> > - Rename resets, vo1 grf and HPD properties
> > - Add a proper description for grf and vo1-grf phandles
> > - Rename the HDMI Input node name to hdmi-receiver
> > - Improve the subject line
> > - Include gpio header file in example to fix dt_binding_check failure
> >
> > .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
> > 1 file changed, 132 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> > new file mode 100644
> > index 000000000000..96ae1e2d2816
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> > @@ -0,0 +1,132 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
> > +
> > +---
> > +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Synopsys DesignWare HDMI RX Controller
> > +
> > +maintainers:
> > + - Shreeya Patel <shreeya.patel@collabora.com>
> > +
> > +description:
> > + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
> > + allowing devices to receive and decode high-resolution video streams
> > + from external sources like media players, cameras, laptops, etc.
> > +
> > +properties:
> > + compatible:
> > + items:
> > + - const: rockchip,rk3588-hdmirx-ctrler
>
> > + - const: snps,dw-hdmi-rx
>
> 1: Compatible strings must be SoC orientated.
> 2: In Linux there's no priority in which string will probed first.
> What's the point of having a fallback string when there's no common code, but instead only the first string is used?
>
> +static const struct of_device_id hdmirx_id[] = {
> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> + { },
> +};
>
We believe the HDMIRX driver can be used for the Synopsys IP on other SoCs
in the future, which is why we have added snps,dw-hdmi-rx as the fallback compatible.
Currently, we have tested the driver only on the RK3588 Rock5B, so we are using the
rockchip,rk3588-hdmirx-ctrler compatible in the driver instead of the fallback one.
Thanks,
Shreeya Patel
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 3
> > +
> > + interrupt-names:
> > + items:
> > + - const: cec
> > + - const: hdmi
> > + - const: dma
> > +
> > + clocks:
> > + maxItems: 7
> > +
> > + clock-names:
> > + items:
> > + - const: aclk
> > + - const: audio
> > + - const: cr_para
> > + - const: pclk
> > + - const: ref
> > + - const: hclk_s_hdmirx
> > + - const: hclk_vo1
> > +
> > + power-domains:
> > + maxItems: 1
> > +
> > + resets:
> > + maxItems: 4
> > +
> > + reset-names:
> > + items:
> > + - const: axi
> > + - const: apb
> > + - const: ref
> > + - const: biu
> > +
> > + memory-region:
> > + maxItems: 1
> > +
> > + hpd-gpios:
> > + description: GPIO specifier for HPD.
> > + maxItems: 1
> > +
> > + rockchip,grf:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description:
> > + The phandle of the syscon node for the general register file
> > + containing HDMIRX PHY status bits.
> > +
> > + rockchip,vo1-grf:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description:
> > + The phandle of the syscon node for the Video Output GRF register
> > + to enable EDID transfer through SDAIN and SCLIN.
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - interrupt-names
> > + - clocks
> > + - clock-names
> > + - power-domains
> > + - resets
> > + - pinctrl-0
> > + - hpd-gpios
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
> > + #include <dt-bindings/gpio/gpio.h>
> > + #include <dt-bindings/interrupt-controller/arm-gic.h>
> > + #include <dt-bindings/interrupt-controller/irq.h>
> > + #include <dt-bindings/power/rk3588-power.h>
> > + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
> > + hdmi_receiver: hdmi-receiver@fdee0000 {
> > + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
> > + reg = <0xfdee0000 0x6000>;
> > + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
> > + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
> > + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
> > + interrupt-names = "cec", "hdmi", "dma";
> > + clocks = <&cru ACLK_HDMIRX>,
> > + <&cru CLK_HDMIRX_AUD>,
> > + <&cru CLK_CR_PARA>,
> > + <&cru PCLK_HDMIRX>,
> > + <&cru CLK_HDMIRX_REF>,
> > + <&cru PCLK_S_HDMIRX>,
> > + <&cru HCLK_VO1>;
> > + clock-names = "aclk",
> > + "audio",
> > + "cr_para",
> > + "pclk",
> > + "ref",
> > + "hclk_s_hdmirx",
> > + "hclk_vo1";
> > + power-domains = <&power RK3588_PD_VO1>;
> > + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
> > + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
> > + reset-names = "axi", "apb", "ref", "biu";
> > + memory-region = <&hdmi_receiver_cma>;
> > + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
> > + pinctrl-names = "default";
> > + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
> > + };
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-20 11:33 ` Johan Jonker
@ 2024-07-22 14:19 ` Shreeya Patel
0 siblings, 0 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-22 14:19 UTC (permalink / raw)
To: Johan Jonker
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip, Dmitry Osipenko
On Saturday, July 20, 2024 17:03 IST, Johan Jonker <jbx6244@yandex.com> wrote:
Hi Johan,
Please see my response below.
>
>
> On 7/19/24 14:40, Shreeya Patel wrote:
> > Add initial support for the Synopsys DesignWare HDMI RX
> > Controller Driver used by Rockchip RK3588. The driver
> > supports:
> > - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> > - RGB888, YUV422, YUV444 and YCC420 pixel formats
> > - CEC
> > - EDID configuration
> >
> > The hardware also has Audio and HDCP capabilities, but these are
> > not yet supported by the driver.
> >
> > Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> > Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> > Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
> > Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
> > Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> > ---
> >
> > Changes in v4 :-
> > - Create a separate config option for selecting the EDID
> > and enable it by default
> > - Improve the comment related to DV timings and move it
> > to the side of hdmirx_get_detected_timings
> > - Add 100ms delay before pulling the HPD high
> > - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> > - Drop the bus info from hdmirx_querycap
> > - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> > - Set queue->min_queued_buffers to 1
> > - Drop q->allow_cache_hints = 0; as it's always 0 by default
> > - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> > - Drop .read = vb2_fop_read as it's not supported by driver
> > - Remove redundant edid_init_data_600M
> > - Make HPD low when driver is loaded
> > - Add support for reading AVI Infoframe
> > - Remove msg_len checks from hdmirx_cec_transmit
> > - Add info about the CEC compliance test in the cover letter
> > - Add arbitration lost status
> > - Validate the physical address inside the EDID
> >
> > Changes in v3 :-
> > - Use v4l2-common helper functions
> >
> > Changes in v2 :-
> > - Fix checkpatch --strict warnings
> > - Rename resets, vo1-grf and HPD node names as per the DT changes
> >
> > drivers/media/platform/Kconfig | 1 +
> > drivers/media/platform/Makefile | 1 +
> > drivers/media/platform/synopsys/Kconfig | 3 +
> > drivers/media/platform/synopsys/Makefile | 2 +
> > .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> > .../media/platform/synopsys/hdmirx/Makefile | 4 +
> > .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> > .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> > .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> > .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> > 10 files changed, 3524 insertions(+)
> > create mode 100644 drivers/media/platform/synopsys/Kconfig
> > create mode 100644 drivers/media/platform/synopsys/Makefile
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> >
> > diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> > index 85d2627776b6..9287faafdce5 100644
> > --- a/drivers/media/platform/Kconfig
> > +++ b/drivers/media/platform/Kconfig
> > @@ -85,6 +85,7 @@ source "drivers/media/platform/rockchip/Kconfig"
> > source "drivers/media/platform/samsung/Kconfig"
> > source "drivers/media/platform/st/Kconfig"
> > source "drivers/media/platform/sunxi/Kconfig"
> > +source "drivers/media/platform/synopsys/Kconfig"
> > source "drivers/media/platform/ti/Kconfig"
> > source "drivers/media/platform/verisilicon/Kconfig"
> > source "drivers/media/platform/via/Kconfig"
> > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> > index ace4e34483dd..6fd7db0541c7 100644
> > --- a/drivers/media/platform/Makefile
> > +++ b/drivers/media/platform/Makefile
> > @@ -28,6 +28,7 @@ obj-y += rockchip/
> > obj-y += samsung/
> > obj-y += st/
> > obj-y += sunxi/
> > +obj-y += synopsys/
> > obj-y += ti/
> > obj-y += verisilicon/
> > obj-y += via/
> > diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
> > new file mode 100644
> > index 000000000000..4fd521f78425
> > --- /dev/null
> > +++ b/drivers/media/platform/synopsys/Kconfig
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +source "drivers/media/platform/synopsys/hdmirx/Kconfig"
> > diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
> > new file mode 100644
> > index 000000000000..3b12c574dd67
> > --- /dev/null
> > +++ b/drivers/media/platform/synopsys/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +obj-y += hdmirx/
> > diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
> > new file mode 100644
> > index 000000000000..ab569e59300f
> > --- /dev/null
> > +++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
> > @@ -0,0 +1,27 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config VIDEO_SYNOPSYS_HDMIRX
> > + tristate "Synopsys DesignWare HDMI Receiver driver"
> > + depends on VIDEO_DEV
> > + depends on ARCH_ROCKCHIP
> > + select MEDIA_CONTROLLER
> > + select VIDEO_V4L2_SUBDEV_API
> > + select VIDEOBUF2_DMA_CONTIG
> > + select CEC_CORE
> > + select CEC_NOTIFIER
> > + select HDMI
> > + help
> > + Support for Synopsys HDMI HDMI RX Controller.
> > + This driver supports HDMI 2.0 version.
> > +
> > + To compile this driver as a module, choose M here. The module
> > + will be called synopsys_hdmirx.
> > +
> > +config HDMIRX_LOAD_DEFAULT_EDID
> > + bool "Load default EDID"
> > + depends on VIDEO_SYNOPSYS_HDMIRX
> > + default "y"
> > + help
> > + Preload the default EDID (Extended Display Identification Data).
> > + EDID contains information about the capabilities of the display,
> > + such as supported resolutions, refresh rates, and audio formats.
> > diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
> > new file mode 100644
> > index 000000000000..2fa2d9e25300
> > --- /dev/null
> > +++ b/drivers/media/platform/synopsys/hdmirx/Makefile
> > @@ -0,0 +1,4 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
> > +
> > +obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
> > diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
>
> [..]
>
> For FTRACE it is needed that all functions start with the same function prefix.
>
> > +static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
>
> > +static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
>
> > +static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
>
> > +static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
> > + u32 bit_mask, u32 expect_val, bool is_grf,
> > + u32 ms)
>
> > +static void return_all_buffers(struct hdmirx_stream *stream,
> > + enum vb2_buffer_state state)
>
> > +static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
>
> > +static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + int status, bool *handled)
>
> > +static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + int status, bool *handled)
>
> > +static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + int status, bool *handled)
>
> > +static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + int status, bool *handled)
>
> > +static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + int status, bool *handled)
>
> > +static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + int status, bool *handled)
>
> > +static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + bool *handled)
>
> > +static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> > + bool *handled)
>
> [..]
>
> > +static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
> > + struct platform_device *pdev)
> > +{
> > + struct device *dev = hdmirx_dev->dev;
> > + int ret, irq;
> > +
> > + irq = platform_get_irq_byname(pdev, "hdmi");
> > + if (irq < 0) {
> > + dev_err_probe(dev, irq, "failed to get hdmi irq\n");
> > + return irq;
> > + }
> > +
> > + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> > +
> > + hdmirx_dev->hdmi_irq = irq;
> > + ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
> > + "rk_hdmirx-hdmi", hdmirx_dev);
> > + if (ret) {
> > + dev_err_probe(dev, ret, "failed to request hdmi irq\n");
> > + return ret;
> > + }
> > +
> > + irq = platform_get_irq_byname(pdev, "dma");
> > + if (irq < 0) {
> > + dev_err_probe(dev, irq, "failed to get dma irq\n");
> > + return irq;
> > + }
> > +
> > + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> > +
> > + hdmirx_dev->dma_irq = irq;
> > + ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
> > + IRQF_ONESHOT, "rk_hdmirx-dma",
> > + hdmirx_dev);
> > + if (ret) {
> > + dev_err_probe(dev, ret, "failed to request dma irq\n");
> > + return ret;
> > + }
> > +
> > + irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
> > + if (irq < 0) {
> > + dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
> > + return irq;
> > + }
> > +
> > + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> > +
> > + hdmirx_dev->det_irq = irq;
> > + ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
> > + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
> > + "rk_hdmirx-5v", hdmirx_dev);
> > + if (ret) {
> > + dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
> > + struct platform_device *pdev)
> > +{
> > + struct device *dev = hdmirx_dev->dev;
> > + struct hdmirx_cec_data cec_data;
> > + int irq;
> > +
> > + irq = platform_get_irq_byname(pdev, "cec");
> > + if (irq < 0) {
> > + dev_err_probe(dev, irq, "failed to get cec irq\n");
> > + return irq;
> > + }
> > +
> > + hdmirx_dev->cec_notifier = cec_notifier_conn_register(dev, NULL, NULL);
> > + if (!hdmirx_dev->cec_notifier)
> > + return -EINVAL;
> > +
> > + cec_data.hdmirx = hdmirx_dev;
> > + cec_data.dev = hdmirx_dev->dev;
> > + cec_data.ops = &hdmirx_cec_ops;
> > + cec_data.irq = irq;
> > +
> > + hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
> > + if (!hdmirx_dev->cec) {
> > + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int hdmirx_probe(struct platform_device *pdev)
> > +{
> > + struct snps_hdmirx_dev *hdmirx_dev;
> > + struct device *dev = &pdev->dev;
> > + struct v4l2_ctrl_handler *hdl;
> > + struct hdmirx_stream *stream;
> > + struct v4l2_device *v4l2_dev;
> > + int ret;
> > +
> > + hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
> > + if (!hdmirx_dev)
> > + return -ENOMEM;
> > +
> > + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
> > + if (ret)
> > + return ret;
> > +
> > + hdmirx_dev->dev = dev;
> > + dev_set_drvdata(dev, hdmirx_dev);
> > +
> > + ret = hdmirx_parse_dt(hdmirx_dev);
> > + if (ret)
> > + return ret;
> > +
> > + ret = hdmirx_setup_irq(hdmirx_dev, pdev);
> > + if (ret)
> > + return ret;
> > +
> > + hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(hdmirx_dev->regs))
> > + return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
> > + "failed to remap regs resource\n");
> > +
> > + mutex_init(&hdmirx_dev->stream_lock);
> > + mutex_init(&hdmirx_dev->work_lock);
> > + spin_lock_init(&hdmirx_dev->rst_lock);
> > +
> > + init_completion(&hdmirx_dev->cr_write_done);
> > + init_completion(&hdmirx_dev->timer_base_lock);
> > + init_completion(&hdmirx_dev->avi_pkt_rcv);
> > +
> > + INIT_WORK(&hdmirx_dev->work_wdt_config, hdmirx_work_wdt_config);
> > + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
> > + hdmirx_delayed_work_hotplug);
> > + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
> > + hdmirx_delayed_work_res_change);
> > + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_heartbeat,
> > + hdmirx_delayed_work_heartbeat);
> > +
> > + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> > + hdmirx_dev->timings = cea640x480;
> > +
> > + hdmirx_enable(dev);
> > + hdmirx_init(hdmirx_dev);
> > +
> > + v4l2_dev = &hdmirx_dev->v4l2_dev;
> > + strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
> > +
> > + hdl = &hdmirx_dev->hdl;
> > + v4l2_ctrl_handler_init(hdl, 1);
> > +
> > + hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
> > + V4L2_CID_DV_RX_POWER_PRESENT,
> > + 0, 1, 0, 0);
> > +
> > + hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, 0,
> > + V4L2_CID_DV_RX_RGB_RANGE,
> > + V4L2_DV_RGB_RANGE_FULL, 0,
> > + V4L2_DV_RGB_RANGE_AUTO);
> > +
> > + hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > +
> > + if (hdl->error) {
> > + dev_err(dev, "v4l2 ctrl handler init failed\n");
> > + ret = hdl->error;
> > + goto err_pm;
> > + }
> > + hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
> > +
> > + ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
> > + if (ret < 0) {
> > + dev_err(dev, "register v4l2 device failed\n");
> > + goto err_hdl;
> > + }
> > +
> > + stream = &hdmirx_dev->stream;
> > + stream->hdmirx_dev = hdmirx_dev;
> > + ret = hdmirx_register_stream_vdev(stream);
> > + if (ret < 0) {
> > + dev_err(dev, "register video device failed\n");
> > + goto err_unreg_v4l2_dev;
> > + }
> > +
> > + ret = hdmirx_register_cec(hdmirx_dev, pdev);
> > + if (ret)
> > + goto err_unreg_video_dev;
> > +
> > + hdmirx_load_default_edid(hdmirx_dev);
> > +
> > + hdmirx_enable_irq(dev);
> > +
> > + return 0;
> > +
> > +err_unreg_video_dev:
> > + video_unregister_device(&hdmirx_dev->stream.vdev);
> > +err_unreg_v4l2_dev:
> > + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> > +err_hdl:
> > + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> > +err_pm:
> > + hdmirx_disable(dev);
> > +
> > + return ret;
> > +}
> > +
> > +static void hdmirx_remove(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> > +
> > + snps_hdmirx_cec_unregister(hdmirx_dev->cec);
> > + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> > +
> > + hdmirx_disable_irq(dev);
> > +
> > + video_unregister_device(&hdmirx_dev->stream.vdev);
> > + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> > + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> > +
> > + hdmirx_disable(dev);
> > +
> > + reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> > +
> > + of_reserved_mem_device_release(dev);
> > +}
> > +
> > +static const struct of_device_id hdmirx_id[] = {
> > + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(of, hdmirx_id);
> > +
> > +static struct platform_driver hdmirx_driver = {
> > + .probe = hdmirx_probe,
> > + .remove = hdmirx_remove,
> > + .driver = {
> > + .name = "snps_hdmirx",
> > + .of_match_table = hdmirx_id,
> > + .pm = &snps_hdmirx_pm_ops,
> > + }
> > +};
> > +module_platform_driver(hdmirx_driver);
> > +
> > +MODULE_DESCRIPTION("Rockchip HDMI Receiver Driver");
>
> While the file is called snps_hdmirx.c and the driver name is "snps_hdmirx" the module description calls it a Rockchip driver.
> This patch serie somewhat hints at the use of multiple SoCs and possible multiple brands then a more clear separation between common snps and Rockchip (rk3588) SoC specific is needed?
>
This driver was originally developed by Rockchip, and we fixed a few issues
and cleaned it up. As I said, the driver has only been tested on the Rock5B so far,
but we believe it could also work with the Synopsys IP on other SoCs in the future.
Hence, I renamed almost everything from rockchip to synopsys.
However, I missed changing the MODULE_DESCRIPTION here, and it should be
"Synopsys HDMI RX Controller Driver." I'll make the change in v5.
Thanks,
Shreeya Patel
> Johan
>
> > +MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
> > +MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
> > +MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
` (3 preceding siblings ...)
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
@ 2024-07-22 19:39 ` hoff.benjamin.k
2024-07-22 20:26 ` Shreeya Patel
2024-08-03 23:57 ` [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Tim Surber
5 siblings, 1 reply; 40+ messages in thread
From: hoff.benjamin.k @ 2024-07-22 19:39 UTC (permalink / raw)
To: shreeya.patel
Cc: conor+dt, devicetree, heiko, hverkuil-cisco, hverkuil, jose.abreu,
kernel, krzk+dt, linux-arm-kernel, linux-kernel, linux-media,
linux-rockchip, mchehab, mturquette, nelson.costa,
nicolas.dufresne, p.zabel, robh, sboyd, shawn.wen
> Add initial support for the Synopsys DesignWare HDMI RX
> Controller Driver used by Rockchip RK3588. The driver
> supports:
> - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> - RGB888, YUV422, YUV444 and YCC420 pixel formats
> - CEC
> - EDID configuration
>
> The hardware also has Audio and HDCP capabilities, but these are
> not yet supported by the driver.
>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - Create a separate config option for selecting the EDID
> and enable it by default
> - Improve the comment related to DV timings and move it
> to the side of hdmirx_get_detected_timings
> - Add 100ms delay before pulling the HPD high
> - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> - Drop the bus info from hdmirx_querycap
> - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> - Set queue->min_queued_buffers to 1
> - Drop q->allow_cache_hints = 0; as it's always 0 by default
> - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> - Drop .read = vb2_fop_read as it's not supported by driver
> - Remove redundant edid_init_data_600M
> - Make HPD low when driver is loaded
> - Add support for reading AVI Infoframe
> - Remove msg_len checks from hdmirx_cec_transmit
> - Add info about the CEC compliance test in the cover letter
> - Add arbitration lost status
> - Validate the physical address inside the EDID
>
> Changes in v3 :-
> - Use v4l2-common helper functions
>
> Changes in v2 :-
> - Fix checkpatch --strict warnings
> - Rename resets, vo1-grf and HPD node names as per the DT changes
>
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/synopsys/Kconfig | 3 +
> drivers/media/platform/synopsys/Makefile | 2 +
> .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> .../media/platform/synopsys/hdmirx/Makefile | 4 +
> .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> 10 files changed, 3524 insertions(+)
> create mode 100644 drivers/media/platform/synopsys/Kconfig
> create mode 100644 drivers/media/platform/synopsys/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 85d2627776b6..9287faafdce5 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -85,6 +85,7 @@ source "drivers/media/platform/rockchip/Kconfig"
> source "drivers/media/platform/samsung/Kconfig"
> source "drivers/media/platform/st/Kconfig"
> source "drivers/media/platform/sunxi/Kconfig"
> +source "drivers/media/platform/synopsys/Kconfig"
> source "drivers/media/platform/ti/Kconfig"
> source "drivers/media/platform/verisilicon/Kconfig"
> source "drivers/media/platform/via/Kconfig"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index ace4e34483dd..6fd7db0541c7 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -28,6 +28,7 @@ obj-y += rockchip/
> obj-y += samsung/
> obj-y += st/
> obj-y += sunxi/
> +obj-y += synopsys/
> obj-y += ti/
> obj-y += verisilicon/
> obj-y += via/
> diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
> new file mode 100644
> index 000000000000..4fd521f78425
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/Kconfig
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +source "drivers/media/platform/synopsys/hdmirx/Kconfig"
> diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
> new file mode 100644
> index 000000000000..3b12c574dd67
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-y += hdmirx/
> diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
> new file mode 100644
> index 000000000000..ab569e59300f
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
> @@ -0,0 +1,27 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config VIDEO_SYNOPSYS_HDMIRX
> + tristate "Synopsys DesignWare HDMI Receiver driver"
> + depends on VIDEO_DEV
> + depends on ARCH_ROCKCHIP
> + select MEDIA_CONTROLLER
> + select VIDEO_V4L2_SUBDEV_API
> + select VIDEOBUF2_DMA_CONTIG
> + select CEC_CORE
> + select CEC_NOTIFIER
> + select HDMI
> + help
> + Support for Synopsys HDMI HDMI RX Controller.
> + This driver supports HDMI 2.0 version.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called synopsys_hdmirx.
> +
> +config HDMIRX_LOAD_DEFAULT_EDID
> + bool "Load default EDID"
> + depends on VIDEO_SYNOPSYS_HDMIRX
> + default "y"
> + help
> + Preload the default EDID (Extended Display Identification Data).
> + EDID contains information about the capabilities of the display,
> + such as supported resolutions, refresh rates, and audio formats.
> diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
> new file mode 100644
> index 000000000000..2fa2d9e25300
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
> +
> +obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> new file mode 100644
> index 000000000000..1dfecf256393
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> @@ -0,0 +1,2763 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2024 Collabora, Ltd.
> + * Author: Shreeya Patel <shreeya.patel@collabora.com>
> + *
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + * Author: Dingxian Wen <shawn.wen@rock-chips.com>
> + */
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/hdmi.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/workqueue.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-notifier.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-dv-timings.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fh.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/videobuf2-v4l2.h>
> +#include <media/v4l2-common.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#include "snps_hdmirx.h"
> +#include "snps_hdmirx_cec.h"
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "debug level (0-3)");
> +
> +#define EDID_NUM_BLOCKS_MAX 2
> +#define EDID_BLOCK_SIZE 128
> +#define HDMIRX_STORED_BIT_WIDTH 8
> +#define IREF_CLK_FREQ_HZ 428571429
> +#define MEMORY_ALIGN_ROUND_UP_BYTES 64
> +#define HDMIRX_PLANE_Y 0
> +#define HDMIRX_PLANE_CBCR 1
> +#define RK_IRQ_HDMIRX_HDMI 210
> +#define FILTER_FRAME_CNT 6
> +#define RK_SIP_FIQ_CTRL 0x82000024
> +#define SIP_WDT_CFG 0x82000026
> +#define DETECTION_THRESHOLD 7
> +
> +/* fiq control sub func */
> +enum {
> + RK_SIP_FIQ_CTRL_FIQ_EN = 1,
> + RK_SIP_FIQ_CTRL_FIQ_DIS,
> + RK_SIP_FIQ_CTRL_SET_AFF
> +};
> +
> +/* SIP_WDT_CONFIG call types */
> +enum {
> + WDT_START = 0,
> + WDT_STOP = 1,
> + WDT_PING = 2,
> +};
> +
> +enum hdmirx_pix_fmt {
> + HDMIRX_RGB888 = 0,
> + HDMIRX_YUV422 = 1,
> + HDMIRX_YUV444 = 2,
> + HDMIRX_YUV420 = 3,
> +};
> +
> +enum ddr_store_fmt {
> + STORE_RGB888 = 0,
> + STORE_RGBA_ARGB,
> + STORE_YUV420_8BIT,
> + STORE_YUV420_10BIT,
> + STORE_YUV422_8BIT,
> + STORE_YUV422_10BIT,
> + STORE_YUV444_8BIT,
> + STORE_YUV420_16BIT = 8,
> + STORE_YUV422_16BIT = 9,
> +};
> +
> +enum hdmirx_reg_attr {
> + HDMIRX_ATTR_RW = 0,
> + HDMIRX_ATTR_RO = 1,
> + HDMIRX_ATTR_WO = 2,
> + HDMIRX_ATTR_RE = 3,
> +};
> +
> +enum {
> + HDMIRX_RST_A,
> + HDMIRX_RST_P,
> + HDMIRX_RST_REF,
> + HDMIRX_RST_BIU,
> + HDMIRX_NUM_RST,
> +};
> +
> +static const char * const pix_fmt_str[] = {
> + "RGB888",
> + "YUV422",
> + "YUV444",
> + "YUV420",
> +};
> +
> +struct hdmirx_buffer {
> + struct vb2_v4l2_buffer vb;
> + struct list_head queue;
> + u32 buff_addr[VIDEO_MAX_PLANES];
> +};
> +
> +struct hdmirx_stream {
> + struct snps_hdmirx_dev *hdmirx_dev;
> + struct video_device vdev;
> + struct vb2_queue buf_queue;
> + struct list_head buf_head;
> + struct hdmirx_buffer *curr_buf;
> + struct hdmirx_buffer *next_buf;
> + struct v4l2_pix_format_mplane pixm;
> + const struct v4l2_format_info *out_finfo;
> + struct mutex vlock; /* to lock resources associated with video buffer and video device */
> + spinlock_t vbq_lock; /* to lock video buffer queue */
> + bool stopping;
> + wait_queue_head_t wq_stopped;
> + u32 frame_idx;
> + u32 line_flag_int_cnt;
> + u32 irq_stat;
> +};
> +
> +struct snps_hdmirx_dev {
> + struct device *dev;
> + struct device *codec_dev;
> + struct hdmirx_stream stream;
> + struct v4l2_device v4l2_dev;
> + struct v4l2_ctrl_handler hdl;
> + struct v4l2_ctrl *detect_tx_5v_ctrl;
> + struct v4l2_ctrl *rgb_range;
> + struct v4l2_dv_timings timings;
> + struct gpio_desc *detect_5v_gpio;
> + struct work_struct work_wdt_config;
> + struct delayed_work delayed_work_hotplug;
> + struct delayed_work delayed_work_res_change;
> + struct delayed_work delayed_work_heartbeat;
> + struct cec_notifier *cec_notifier;
> + struct hdmirx_cec *cec;
> + struct mutex stream_lock; /* to lock video stream capture */
> + struct mutex work_lock; /* to lock the critical section of hotplug event */
> + struct reset_control_bulk_data resets[HDMIRX_NUM_RST];
> + struct clk_bulk_data *clks;
> + struct regmap *grf;
> + struct regmap *vo1_grf;
> + struct completion cr_write_done;
> + struct completion timer_base_lock;
> + struct completion avi_pkt_rcv;
> + enum hdmirx_pix_fmt pix_fmt;
> + void __iomem *regs;
> + int hdmi_irq;
> + int dma_irq;
> + int det_irq;
> + bool hpd_trigger_level;
> + bool tmds_clk_ratio;
> + bool is_dvi_mode;
> + bool got_timing;
> + u32 num_clks;
> + u32 edid_blocks_written;
> + u32 cur_vic;
> + u32 cur_fmt_fourcc;
> + u32 color_depth;
> + u8 edid[EDID_BLOCK_SIZE * 2];
> + hdmi_codec_plugged_cb plugged_cb;
> + spinlock_t rst_lock; /* to lock register access */
> +};
> +
> +static u8 edid_init_data_340M[] = {
> + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
> + 0x49, 0x70, 0x88, 0x35, 0x01, 0x00, 0x00, 0x00,
> + 0x2D, 0x1F, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78,
> + 0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23,
> + 0x09, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x61, 0x40,
> + 0x01, 0x01, 0x81, 0x00, 0x95, 0x00, 0xA9, 0xC0,
> + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
> + 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
> + 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00, 0x1E,
> + 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20,
> + 0x6E, 0x28, 0x55, 0x00, 0x20, 0xC2, 0x31, 0x00,
> + 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x52,
> + 0x4B, 0x2D, 0x55, 0x48, 0x44, 0x0A, 0x20, 0x20,
> + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
> + 0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A,
> + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA7,
> +
> + 0x02, 0x03, 0x2F, 0xD1, 0x51, 0x07, 0x16, 0x14,
> + 0x05, 0x01, 0x03, 0x12, 0x13, 0x84, 0x22, 0x1F,
> + 0x90, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x23, 0x09,
> + 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x67, 0x03,
> + 0x0C, 0x00, 0x30, 0x00, 0x10, 0x44, 0xE3, 0x05,
> + 0x03, 0x01, 0xE4, 0x0F, 0x00, 0x80, 0x01, 0x02,
> + 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58,
> + 0x2C, 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00,
> + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
> +};
> +
> +static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94;
> +
> +static const struct v4l2_dv_timings_cap hdmirx_timings_cap = {
> + .type = V4L2_DV_BT_656_1120,
> + .reserved = { 0 },
> + V4L2_INIT_BT_TIMINGS(640, 4096, /* min/max width */
> + 480, 2160, /* min/max height */
> + 20000000, 600000000, /* min/max pixelclock */
> + /* standards */
> + V4L2_DV_BT_STD_CEA861,
> + /* capabilities */
> + V4L2_DV_BT_CAP_PROGRESSIVE |
> + V4L2_DV_BT_CAP_INTERLACED)
> +};
> +
> +static void hdmirx_writel(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val)
> +{
> + unsigned long lock_flags = 0;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + writel(val, hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
> +
> +static u32 hdmirx_readl(struct snps_hdmirx_dev *hdmirx_dev, int reg)
> +{
> + unsigned long lock_flags = 0;
> + u32 val;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + val = readl(hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> + return val;
> +}
> +
> +static void hdmirx_reset_dma(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + unsigned long lock_flags = 0;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + reset_control_reset(hdmirx_dev->resets[0].rstc);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
> +
> +static void hdmirx_update_bits(struct snps_hdmirx_dev *hdmirx_dev, int reg,
> + u32 mask, u32 data)
> +{
> + unsigned long lock_flags = 0;
> + u32 val;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + val = readl(hdmirx_dev->regs + reg) & ~mask;
> + val |= (data & mask);
> + writel(val, hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
> +
> +static int hdmirx_subscribe_event(struct v4l2_fh *fh,
> + const struct v4l2_event_subscription *sub)
> +{
> + switch (sub->type) {
> + case V4L2_EVENT_SOURCE_CHANGE:
> + if (fh->vdev->vfl_dir == VFL_DIR_RX)
> + return v4l2_src_change_event_subscribe(fh, sub);
> + break;
> + case V4L2_EVENT_CTRL:
> + return v4l2_ctrl_subscribe_event(fh, sub);
> + default:
> + break;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + bool ret;
> + int val, i, cnt;
> +
> + cnt = 0;
> + for (i = 0; i < 10; i++) {
> + usleep_range(1000, 1100);
> + val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
> + if (val > 0)
> + cnt++;
> + if (cnt >= DETECTION_THRESHOLD)
> + break;
> + }
> +
> + ret = (cnt >= DETECTION_THRESHOLD) ? true : false;
> + v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret);
> +
> + return ret;
> +}
> +
> +static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + u32 mu_status, dma_st10, cmu_st;
> +
> + mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
> + dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
> + cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
> +
> + if ((mu_status & TMDSVALID_STABLE_ST) &&
> + (dma_st10 & HDMIRX_LOCK) &&
> + (cmu_st & TMDSQPCLK_LOCKED_ST))
> + return false;
> +
> + return true;
> +}
> +
> +static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 val, color_depth_reg;
> +
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
> + color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
> +
> + switch (color_depth_reg) {
> + case 0x4:
> + hdmirx_dev->color_depth = 24;
> + break;
> + case 0x5:
> + hdmirx_dev->color_depth = 30;
> + break;
> + case 0x6:
> + hdmirx_dev->color_depth = 36;
> + break;
> + case 0x7:
> + hdmirx_dev->color_depth = 48;
> + break;
> + default:
> + hdmirx_dev->color_depth = 24;
> + break;
> + }
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
> + __func__, hdmirx_dev->color_depth, color_depth_reg);
> +}
> +
> +static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 val;
> +
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
> + hdmirx_dev->pix_fmt = val & HDMIRX_FORMAT_MASK;
> +
> + switch (hdmirx_dev->pix_fmt) {
> + case HDMIRX_RGB888:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + break;
> + case HDMIRX_YUV422:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV16;
> + break;
> + case HDMIRX_YUV444:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV24;
> + break;
> + case HDMIRX_YUV420:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV12;
> + break;
> + default:
> + v4l2_err(v4l2_dev,
> + "%s: err pix_fmt: %d, set RGB888 as default\n",
> + __func__, hdmirx_dev->pix_fmt);
> + hdmirx_dev->pix_fmt = HDMIRX_RGB888;
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + break;
> + }
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s\n", __func__,
> + pix_fmt_str[hdmirx_dev->pix_fmt]);
> +}
> +
> +static void hdmirx_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_bt_timings *bt, bool from_dma)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 hact, vact, htotal, vtotal, fps;
> + u32 hfp, hs, hbp, vfp, vs, vbp;
> + u32 val;
> +
> + if (from_dma) {
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS2);
> + hact = (val >> 16) & 0xffff;
> + vact = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS3);
> + htotal = (val >> 16) & 0xffff;
> + vtotal = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS4);
> + hs = (val >> 16) & 0xffff;
> + vs = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS5);
> + hbp = (val >> 16) & 0xffff;
> + vbp = val & 0xffff;
> + hfp = htotal - hact - hs - hbp;
> + vfp = vtotal - vact - vs - vbp;
> + } else {
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS1);
> + hs = (val >> 16) & 0xffff;
> + hfp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS2);
> + hbp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS3);
> + htotal = (val >> 16) & 0xffff;
> + hact = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS4);
> + vs = (val >> 16) & 0xffff;
> + vfp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS5);
> + vbp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS6);
> + vtotal = (val >> 16) & 0xffff;
> + vact = val & 0xffff;
> + if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
> + hact *= 2;
> + }
> + if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
> + htotal *= 2;
> + fps = (bt->pixelclock + (htotal * vtotal) / 2) / (htotal * vtotal);
> + if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
> + fps *= 2;
> + bt->width = hact;
> + bt->height = vact;
> + bt->hfrontporch = hfp;
> + bt->hsync = hs;
> + bt->hbackporch = hbp;
> + bt->vfrontporch = vfp;
> + bt->vsync = vs;
> + bt->vbackporch = vbp;
> +
> + v4l2_dbg(1, debug, v4l2_dev, "get timings from %s\n", from_dma ? "dma" : "ctrl");
> + v4l2_dbg(1, debug, v4l2_dev, "act:%ux%u, total:%ux%u, fps:%u, pixclk:%llu\n",
> + bt->width, bt->height, htotal, vtotal, fps, bt->pixelclock);
> +
> + v4l2_dbg(2, debug, v4l2_dev, "hfp:%u, hs:%u, hbp:%u, vfp:%u, vs:%u, vbp:%u\n",
> + bt->hfrontporch, bt->hsync, bt->hbackporch,
> + bt->vfrontporch, bt->vsync, bt->vbackporch);
> +}
> +
> +static bool hdmirx_check_timing_valid(struct v4l2_bt_timings *bt)
> +{
> + if (bt->width < 100 || bt->width > 5000 ||
> + bt->height < 100 || bt->height > 5000)
> + return false;
> +
> + if (!bt->hsync || bt->hsync > 200 ||
> + !bt->vsync || bt->vsync > 100)
> + return false;
> +
> + if (!bt->hbackporch || bt->hbackporch > 2000 ||
> + !bt->vbackporch || bt->vbackporch > 2000)
> + return false;
> +
> + if (!bt->hfrontporch || bt->hfrontporch > 2000 ||
> + !bt->vfrontporch || bt->vfrontporch > 2000)
> + return false;
> +
> + return true;
> +}
> +
> +static void hdmirx_get_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + union hdmi_infoframe frame = {};
> + int err, i, b, itr = 0;
> + u8 aviif[3 + 7 * 4];
> + u32 val;
> +
> + aviif[itr++] = HDMI_INFOFRAME_TYPE_AVI;
> + val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PH2_1);
> + aviif[itr++] = val & 0xff;
> + aviif[itr++] = (val >> 8) & 0xff;
> +
> + for (i = 0; i < 7; i++) {
> + val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB3_0 + 4 * i);
> +
> + for (b = 0; b < 4; b++)
> + aviif[itr++] = (val >> (8 * b)) & 0xff;
> + }
> +
> + err = hdmi_infoframe_unpack(&frame, aviif, sizeof(aviif));
> + if (err) {
> + v4l2_err(v4l2_dev, "failed to unpack AVI infoframe\n");
> + return;
> + }
> +
> + v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, frame.avi.quantization_range);
> +}
> +
> +/*
> + * When querying DV timings during preview, if the DMA's timing is stable,
> + * we retrieve the timings directly from the DMA. However, if the current
> + * resolution is negative, obtaining the timing from CTRL may require a
> + * change in the sync polarity, potentially leading to DMA errors.
> + */
> +static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_dv_timings *timings,
> + bool from_dma)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_bt_timings *bt = &timings->bt;
> + u32 field_type, color_depth, deframer_st;
> + u32 val, tmdsqpclk_freq, pix_clk;
> + u64 tmp_data, tmds_clk;
> +
> + memset(timings, 0, sizeof(struct v4l2_dv_timings));
> + timings->type = V4L2_DV_BT_656_1120;
> +
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
> + field_type = (val & HDMIRX_TYPE_MASK) >> 7;
> + hdmirx_get_pix_fmt(hdmirx_dev);
> + bt->interlaced = field_type & BIT(0) ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
> + val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB7_4);
> + hdmirx_dev->cur_vic = val | VIC_VAL_MASK;
> + hdmirx_get_colordepth(hdmirx_dev);
> + color_depth = hdmirx_dev->color_depth;
> + deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
> + hdmirx_dev->is_dvi_mode = deframer_st & OPMODE_STS_MASK ? false : true;
> + tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
> + tmds_clk = tmdsqpclk_freq * 4 * 1000;
> + tmp_data = tmds_clk * 24;
> + do_div(tmp_data, color_depth);
> + pix_clk = tmp_data;
> + bt->pixelclock = pix_clk;
> +
> + hdmirx_get_avi_infoframe(hdmirx_dev);
> +
> + hdmirx_get_timings(hdmirx_dev, bt, from_dma);
> + if (bt->interlaced == V4L2_DV_INTERLACED) {
> + bt->height *= 2;
> + bt->il_vsync = bt->vsync + 1;
> + }
> +
> + v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu\n", tmds_clk);
> + v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, vic:%d, color:%d, mode:%s\n",
> + bt->interlaced, hdmirx_dev->pix_fmt,
> + hdmirx_dev->cur_vic, hdmirx_dev->color_depth,
> + hdmirx_dev->is_dvi_mode ? "dvi" : "hdmi");
> + v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
> +
> + if (!hdmirx_check_timing_valid(bt))
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + return !tx_5v_power_present(hdmirx_dev);
> +}
> +
> +static int hdmirx_query_dv_timings(struct file *file, void *_fh,
> + struct v4l2_dv_timings *timings)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int ret;
> +
> + if (port_no_link(hdmirx_dev)) {
> + v4l2_err(v4l2_dev, "%s: port has no link\n", __func__);
> + return -ENOLINK;
> + }
> +
> + if (signal_not_lock(hdmirx_dev)) {
> + v4l2_err(v4l2_dev, "%s: signal is not locked\n", __func__);
> + return -ENOLCK;
> + }
> +
> + ret = hdmirx_get_detected_timings(hdmirx_dev, timings, true);
> + if (ret)
> + return ret;
> +
> + if (debug)
> + v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
> + "query_dv_timings: ", timings, false);
> +
> + if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
> + v4l2_dbg(1, debug, v4l2_dev, "%s: timings out of range\n", __func__);
> + return -ERANGE;
> + }
> +
> + return 0;
> +}
> +
> +static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: %sable, hpd_trigger_level:%d\n",
> + __func__, en ? "en" : "dis",
> + hdmirx_dev->hpd_trigger_level);
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, HPDLOW, en ? 0 : HPDLOW);
> + en = hdmirx_dev->hpd_trigger_level ? en : !en;
> + hdmirx_writel(hdmirx_dev, CORE_CONFIG, en);
> +}
> +
> +static int hdmirx_write_edid(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_edid *edid, bool hpd_up)
> +{
> + u32 edid_len = edid->blocks * EDID_BLOCK_SIZE;
> + char data[300];
> + u32 i;
> +
> + memset(edid->reserved, 0, sizeof(edid->reserved));
> + if (edid->pad)
> + return -EINVAL;
> +
> + if (edid->start_block)
> + return -EINVAL;
> +
> + if (edid->blocks > EDID_NUM_BLOCKS_MAX) {
> + edid->blocks = EDID_NUM_BLOCKS_MAX;
> + return -E2BIG;
> + }
> +
> + if (!edid->blocks) {
> + hdmirx_dev->edid_blocks_written = 0;
> + return 0;
> + }
> +
> + cec_s_phys_addr_from_edid(hdmirx_dev->cec->adap,
> + (const struct edid *)edid->edid);
> +
> + memset(&hdmirx_dev->edid, 0, sizeof(hdmirx_dev->edid));
> + hdmirx_hpd_ctrl(hdmirx_dev, false);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
> + EDID_READ_EN_MASK |
> + EDID_WRITE_EN_MASK |
> + EDID_SLAVE_ADDR_MASK,
> + EDID_READ_EN(0) |
> + EDID_WRITE_EN(1) |
> + EDID_SLAVE_ADDR(0x50));
> + for (i = 0; i < edid_len; i++)
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG10, edid->edid[i]);
> +
> + /* read out for debug */
> + if (debug >= 2) {
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
> + EDID_READ_EN_MASK |
> + EDID_WRITE_EN_MASK,
> + EDID_READ_EN(1) |
> + EDID_WRITE_EN(0));
> + edid_len = edid_len > sizeof(data) ? sizeof(data) : edid_len;
> + memset(data, 0, sizeof(data));
> + for (i = 0; i < edid_len; i++)
> + data[i] = hdmirx_readl(hdmirx_dev, DMA_STATUS14);
> +
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data,
> + edid_len, false);
> + }
> +
> + /*
> + * You must set EDID_READ_EN & EDID_WRITE_EN bit to 0,
> + * when the read/write edid operation is completed.Otherwise, it
> + * will affect the reading and writing of other registers
> + */
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
> + EDID_READ_EN_MASK | EDID_WRITE_EN_MASK,
> + EDID_READ_EN(0) | EDID_WRITE_EN(0));
> +
> + hdmirx_dev->edid_blocks_written = edid->blocks;
> + memcpy(&hdmirx_dev->edid, edid->edid, edid->blocks * EDID_BLOCK_SIZE);
> + if (hpd_up) {
> + if (tx_5v_power_present(hdmirx_dev)) {
> + /* Add 100ms delay after updating the EDID as per HDMI specs */
> + msleep(100);
> + hdmirx_hpd_ctrl(hdmirx_dev, true);
> + }
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Before clearing interrupt, we need to read the interrupt status.
> + */
> +static inline void hdmirx_clear_interrupt(struct snps_hdmirx_dev *hdmirx_dev,
> + u32 reg, u32 val)
> +{
> + /* (interrupt status register) = (interrupt clear register) - 0x8 */
> + hdmirx_readl(hdmirx_dev, reg - 0x8);
> + hdmirx_writel(hdmirx_dev, reg, val);
> +}
> +
> +static void hdmirx_interrupts_setup(struct snps_hdmirx_dev *hdmirx_dev, bool en)
> +{
> + v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: %sable\n",
> + __func__, en ? "en" : "dis");
> +
> + /* Note: In DVI mode, it needs to be written twice to take effect. */
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
> +
> + if (en) {
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
> + TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG,
> + TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG);
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
> + TMDSVALID_STABLE_CHG, TMDSVALID_STABLE_CHG);
> + hdmirx_update_bits(hdmirx_dev, AVPUNIT_0_INT_MASK_N,
> + CED_DYN_CNT_CH2_IRQ |
> + CED_DYN_CNT_CH1_IRQ |
> + CED_DYN_CNT_CH0_IRQ,
> + CED_DYN_CNT_CH2_IRQ |
> + CED_DYN_CNT_CH1_IRQ |
> + CED_DYN_CNT_CH0_IRQ);
> + } else {
> + hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
> + }
> +}
> +
> +static void hdmirx_plugout(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct arm_smccc_res res;
> +
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
> + hdmirx_interrupts_setup(hdmirx_dev, false);
> + hdmirx_hpd_ctrl(hdmirx_dev, false);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN, 0);
> + hdmirx_reset_dma(hdmirx_dev);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE | PHY_RESET |
> + PHY_PDDQ, HDMI_DISABLE);
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
> + cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_heartbeat);
> + flush_work(&hdmirx_dev->work_wdt_config);
> + arm_smccc_smc(SIP_WDT_CFG, WDT_STOP, 0, 0, 0, 0, 0, 0, &res);
> +}
> +
> +static int hdmirx_set_edid(struct file *file, void *fh, struct v4l2_edid *edid)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct arm_smccc_res res;
> + int ret;
> +
> + disable_irq(hdmirx_dev->hdmi_irq);
> + disable_irq(hdmirx_dev->dma_irq);
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_DIS,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + if (tx_5v_power_present(hdmirx_dev))
> + hdmirx_plugout(hdmirx_dev);
> + ret = hdmirx_write_edid(hdmirx_dev, edid, false);
> + if (ret)
> + return ret;
> +
> + enable_irq(hdmirx_dev->hdmi_irq);
> + enable_irq(hdmirx_dev->dma_irq);
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(500));
> + return 0;
> +}
> +
> +static int hdmirx_get_edid(struct file *file, void *fh, struct v4l2_edid *edid)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + memset(edid->reserved, 0, sizeof(edid->reserved));
> +
> + if (edid->pad)
> + return -EINVAL;
> +
> + if (!edid->start_block && !edid->blocks) {
> + edid->blocks = hdmirx_dev->edid_blocks_written;
> + return 0;
> + }
> +
> + if (!hdmirx_dev->edid_blocks_written)
> + return -ENODATA;
> +
> + if (edid->start_block >= hdmirx_dev->edid_blocks_written || !edid->blocks)
> + return -EINVAL;
> +
> + if (edid->start_block + edid->blocks > hdmirx_dev->edid_blocks_written)
> + edid->blocks = hdmirx_dev->edid_blocks_written - edid->start_block;
> +
> + memcpy(edid->edid, &hdmirx_dev->edid, edid->blocks * EDID_BLOCK_SIZE);
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: read EDID:\n", __func__);
> + if (debug > 0)
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
> + edid->edid, edid->blocks * EDID_BLOCK_SIZE, false);
> +
> + return 0;
> +}
> +
> +static int hdmirx_g_parm(struct file *file, void *priv,
> + struct v4l2_streamparm *parm)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_fract fps;
> +
> + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + fps = v4l2_calc_timeperframe(&hdmirx_dev->timings);
> + parm->parm.capture.timeperframe.numerator = fps.numerator;
> + parm->parm.capture.timeperframe.denominator = fps.denominator;
> +
> + return 0;
> +}
> +
> +static int hdmirx_dv_timings_cap(struct file *file, void *fh,
> + struct v4l2_dv_timings_cap *cap)
> +{
> + *cap = hdmirx_timings_cap;
> + return 0;
> +}
> +
> +static int hdmirx_enum_dv_timings(struct file *file, void *_fh,
> + struct v4l2_enum_dv_timings *timings)
> +{
> + return v4l2_enum_dv_timings_cap(timings, &hdmirx_timings_cap, NULL, NULL);
> +}
> +
> +static void hdmirx_scdc_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_update_bits(hdmirx_dev, I2C_SLAVE_CONFIG1,
> + I2C_SDA_OUT_HOLD_VALUE_QST_MASK |
> + I2C_SDA_IN_HOLD_VALUE_QST_MASK,
> + I2C_SDA_OUT_HOLD_VALUE_QST(0x80) |
> + I2C_SDA_IN_HOLD_VALUE_QST(0x15));
> + hdmirx_update_bits(hdmirx_dev, SCDC_REGBANK_CONFIG0,
> + SCDC_SINKVERSION_QST_MASK,
> + SCDC_SINKVERSION_QST(1));
> +}
> +
> +static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
> + u32 bit_mask, u32 expect_val, bool is_grf,
> + u32 ms)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 i, val;
> +
> + for (i = 0; i < ms; i++) {
> + if (is_grf)
> + regmap_read(hdmirx_dev->grf, reg, &val);
> + else
> + val = hdmirx_readl(hdmirx_dev, reg);
> +
> + if ((val & bit_mask) == expect_val) {
> + v4l2_dbg(2, debug, v4l2_dev,
> + "%s: i:%d, time: %dms\n", __func__, i, ms);
> + break;
> + }
> + usleep_range(1000, 1010);
> + }
> +
> + if (i == ms)
> + return -1;
> +
> + return 0;
> +}
> +
> +static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev,
> + u32 phy_reg, u32 val)
> +{
> + struct device *dev = hdmirx_dev->dev;
> +
> + reinit_completion(&hdmirx_dev->cr_write_done);
> + /* clear irq status */
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + /* en irq */
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
> + PHYCREG_CR_WRITE_DONE, PHYCREG_CR_WRITE_DONE);
> + /* write phy reg addr */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg);
> + /* write phy reg val */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG2, val);
> + /* config write enable */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_WRITE_P);
> +
> + if (!wait_for_completion_timeout(&hdmirx_dev->cr_write_done,
> + msecs_to_jiffies(20))) {
> + dev_err(dev, "%s wait cr write done failed\n", __func__);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static void hdmirx_tmds_clk_ratio_config(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 val;
> +
> + val = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS1);
> + v4l2_dbg(3, debug, v4l2_dev, "%s: scdc_regbank_st:%#x\n", __func__, val);
> + hdmirx_dev->tmds_clk_ratio = (val & SCDC_TMDSBITCLKRATIO) > 0;
> +
> + if (hdmirx_dev->tmds_clk_ratio) {
> + v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX greater than 3.4Gbps\n", __func__);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
> + TMDS_CLOCK_RATIO, TMDS_CLOCK_RATIO);
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX less than 3.4Gbps\n", __func__);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
> + TMDS_CLOCK_RATIO, 0);
> + }
> +}
> +
> +static void hdmirx_phy_config(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> +
> + hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
> + hdmirx_update_bits(hdmirx_dev, SCDC_INT_MASK_N, SCDCTMDSCCFG_CHG,
> + SCDCTMDSCCFG_CHG);
> + /* cr_para_clk 24M */
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, REFFREQ_SEL_MASK, REFFREQ_SEL(0));
> + /* rx data width 40bit valid */
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, RXDATA_WIDTH, RXDATA_WIDTH);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, PHY_RESET);
> + usleep_range(100, 110);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, 0);
> + usleep_range(100, 110);
> + /* select cr para interface */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3);
> +
> + if (wait_reg_bit_status(hdmirx_dev, SYS_GRF_SOC_STATUS1,
> + HDMIRXPHY_SRAM_INIT_DONE,
> + HDMIRXPHY_SRAM_INIT_DONE, true, 10))
> + dev_err(dev, "%s: phy SRAM init failed\n", __func__);
> +
> + regmap_write(hdmirx_dev->grf, SYS_GRF_SOC_CON1,
> + (HDMIRXPHY_SRAM_EXT_LD_DONE << 16) |
> + HDMIRXPHY_SRAM_EXT_LD_DONE);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 1);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
> +
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG,
> + CDR_SETTING_BOUNDARY_3_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG,
> + CDR_SETTING_BOUNDARY_4_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG,
> + CDR_SETTING_BOUNDARY_5_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG,
> + CDR_SETTING_BOUNDARY_6_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG,
> + CDR_SETTING_BOUNDARY_7_DEFAULT);
> +
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_PDDQ, 0);
> + if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, PDDQ_ACK, 0, false, 10))
> + dev_err(dev, "%s: wait pddq ack failed\n", __func__);
> +
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE, 0);
> + if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, HDMI_DISABLE_ACK, 0,
> + false, 50))
> + dev_err(dev, "%s: wait hdmi disable ack failed\n", __func__);
> +
> + hdmirx_tmds_clk_ratio_config(hdmirx_dev);
> +}
> +
> +static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> +
> + reinit_completion(&hdmirx_dev->timer_base_lock);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + /* en irq */
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
> + TIMER_BASE_LOCKED_IRQ, TIMER_BASE_LOCKED_IRQ);
> + /* write irefclk freq */
> + hdmirx_writel(hdmirx_dev, GLOBAL_TIMER_REF_BASE, IREF_CLK_FREQ_HZ);
> +
> + if (!wait_for_completion_timeout(&hdmirx_dev->timer_base_lock,
> + msecs_to_jiffies(20)))
> + dev_err(dev, "%s wait timer base lock failed\n", __func__);
> +
> + hdmirx_update_bits(hdmirx_dev, CMU_CONFIG0,
> + TMDSQPCLK_STABLE_FREQ_MARGIN_MASK |
> + AUDCLK_STABLE_FREQ_MARGIN_MASK,
> + TMDSQPCLK_STABLE_FREQ_MARGIN(2) |
> + AUDCLK_STABLE_FREQ_MARGIN(1));
> + hdmirx_update_bits(hdmirx_dev, DESCRAND_EN_CONTROL,
> + SCRAMB_EN_SEL_QST_MASK, SCRAMB_EN_SEL_QST(1));
> + hdmirx_update_bits(hdmirx_dev, CED_CONFIG,
> + CED_VIDDATACHECKEN_QST |
> + CED_DATAISCHECKEN_QST |
> + CED_GBCHECKEN_QST |
> + CED_CTRLCHECKEN_QST |
> + CED_CHLOCKMAXER_QST_MASK,
> + CED_VIDDATACHECKEN_QST |
> + CED_GBCHECKEN_QST |
> + CED_CTRLCHECKEN_QST |
> + CED_CHLOCKMAXER_QST(0x10));
> + hdmirx_update_bits(hdmirx_dev, DEFRAMER_CONFIG0,
> + VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST_MASK,
> + VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
> +}
> +
> +static void hdmirx_set_negative_pol(struct snps_hdmirx_dev *hdmirx_dev, bool en)
> +{
> + if (en) {
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
> + VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN,
> + VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN);
> + hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
> + VPROC_VSYNC_POL_OVR_VALUE |
> + VPROC_VSYNC_POL_OVR_EN |
> + VPROC_HSYNC_POL_OVR_VALUE |
> + VPROC_HSYNC_POL_OVR_EN,
> + VPROC_VSYNC_POL_OVR_EN |
> + VPROC_HSYNC_POL_OVR_EN);
> + return;
> + }
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
> + VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN, 0);
> +
> + hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
> + VPROC_VSYNC_POL_OVR_VALUE |
> + VPROC_VSYNC_POL_OVR_EN |
> + VPROC_HSYNC_POL_OVR_VALUE |
> + VPROC_HSYNC_POL_OVR_EN, 0);
> +}
> +
> +static int hdmirx_try_to_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_dv_timings *timings,
> + int try_cnt)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int i, cnt = 0, fail_cnt = 0, ret = 0;
> + bool from_dma = false;
> +
> + hdmirx_set_negative_pol(hdmirx_dev, false);
> + for (i = 0; i < try_cnt; i++) {
> + ret = hdmirx_get_detected_timings(hdmirx_dev, timings, from_dma);
> + if (ret) {
> + cnt = 0;
> + fail_cnt++;
> + if (fail_cnt > 3) {
> + hdmirx_set_negative_pol(hdmirx_dev, true);
> + from_dma = true;
> + }
> + } else {
> + cnt++;
> + }
> + if (cnt >= 5)
> + break;
> +
> + usleep_range(10 * 1000, 10 * 1100);
> + }
> +
> + if (try_cnt > 8 && cnt < 5)
> + v4l2_dbg(1, debug, v4l2_dev, "%s: res not stable\n", __func__);
> +
> + return ret;
> +}
> +
> +static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_dv_timings timings;
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + const struct v4l2_event ev_src_chg = {
> + .type = V4L2_EVENT_SOURCE_CHANGE,
> + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
> + };
> +
> + if (hdmirx_try_to_get_timings(hdmirx_dev, &timings, 20)) {
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(20));
> + return;
> + }
> +
> + hdmirx_dev->got_timing = true;
> + v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
> + v4l2_event_queue(&stream->vdev, &ev_src_chg);
> +}
> +
> +static void hdmirx_set_ddr_store_fmt(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + enum ddr_store_fmt store_fmt;
> + u32 dma_cfg1;
> +
> + switch (hdmirx_dev->pix_fmt) {
> + case HDMIRX_RGB888:
> + store_fmt = STORE_RGB888;
> + break;
> + case HDMIRX_YUV444:
> + store_fmt = STORE_YUV444_8BIT;
> + break;
> + case HDMIRX_YUV422:
> + store_fmt = STORE_YUV422_8BIT;
> + break;
> + case HDMIRX_YUV420:
> + store_fmt = STORE_YUV420_8BIT;
> + break;
> + default:
> + store_fmt = STORE_RGB888;
> + break;
> + }
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
> + DDR_STORE_FORMAT_MASK, DDR_STORE_FORMAT(store_fmt));
> + dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
> + v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
> + __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
> +}
> +
> +static int hdmirx_wait_lock_and_get_timing(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 mu_status, scdc_status, dma_st10, cmu_st;
> + u32 i;
> +
> + for (i = 0; i < 300; i++) {
> + mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
> + scdc_status = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS3);
> + dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
> + cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
> +
> + if ((mu_status & TMDSVALID_STABLE_ST) &&
> + (dma_st10 & HDMIRX_LOCK) &&
> + (cmu_st & TMDSQPCLK_LOCKED_ST))
> + break;
> +
> + if (!tx_5v_power_present(hdmirx_dev)) {
> + v4l2_err(v4l2_dev, "%s: HDMI pull out, return\n", __func__);
> + return -1;
> + }
> +
> + hdmirx_tmds_clk_ratio_config(hdmirx_dev);
> + }
> +
> + if (i == 300) {
> + v4l2_err(v4l2_dev, "%s: signal not lock, tmds_clk_ratio:%d\n",
> + __func__, hdmirx_dev->tmds_clk_ratio);
> + v4l2_err(v4l2_dev, "%s: mu_st:%#x, scdc_st:%#x, dma_st10:%#x\n",
> + __func__, mu_status, scdc_status, dma_st10);
> + return -1;
> + }
> +
> + v4l2_info(v4l2_dev, "%s: signal lock ok, i:%d\n", __func__, i);
> + hdmirx_writel(hdmirx_dev, GLOBAL_SWRESET_REQUEST, DATAPATH_SWRESETREQ);
> +
> + reinit_completion(&hdmirx_dev->avi_pkt_rcv);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
> + PKTDEC_AVIIF_RCV_IRQ, PKTDEC_AVIIF_RCV_IRQ);
> +
> + if (!wait_for_completion_timeout(&hdmirx_dev->avi_pkt_rcv,
> + msecs_to_jiffies(300))) {
> + v4l2_err(v4l2_dev, "%s wait avi_pkt_rcv failed\n", __func__);
> + hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
> + PKTDEC_AVIIF_RCV_IRQ, 0);
> + }
> +
> + usleep_range(50 * 1000, 50 * 1010);
> + hdmirx_format_change(hdmirx_dev);
> +
> + return 0;
> +}
> +
> +static void hdmirx_dma_config(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_set_ddr_store_fmt(hdmirx_dev);
> +
> + /* Note: uv_swap, rb can not swap, doc err*/
> + if (hdmirx_dev->cur_fmt_fourcc != V4L2_PIX_FMT_NV16)
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, RB_SWAP_EN);
> + else
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, 0);
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
> + LOCK_FRAME_NUM_MASK,
> + LOCK_FRAME_NUM(2));
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
> + UV_WID_MASK | Y_WID_MASK | ABANDON_EN,
> + UV_WID(1) | Y_WID(2) | ABANDON_EN);
> +}
> +
> +static void hdmirx_submodule_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + /* Note: if not config HDCP2_CONFIG, there will be some errors; */
> + hdmirx_update_bits(hdmirx_dev, HDCP2_CONFIG,
> + HDCP2_SWITCH_OVR_VALUE |
> + HDCP2_SWITCH_OVR_EN,
> + HDCP2_SWITCH_OVR_EN);
> + hdmirx_scdc_init(hdmirx_dev);
> + hdmirx_controller_init(hdmirx_dev);
> +}
> +
> +static int hdmirx_enum_input(struct file *file, void *priv,
> + struct v4l2_input *input)
> +{
> + if (input->index > 0)
> + return -EINVAL;
> +
> + input->type = V4L2_INPUT_TYPE_CAMERA;
> + input->std = 0;
> + strscpy(input->name, "HDMI IN", sizeof(input->name));
> + input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
> +
> + return 0;
> +}
> +
> +static int hdmirx_get_input(struct file *file, void *priv, unsigned int *i)
> +{
> + *i = 0;
> + return 0;
> +}
> +
> +static int hdmirx_set_input(struct file *file, void *priv, unsigned int i)
> +{
> + if (i)
> + return -EINVAL;
> + return 0;
> +}
> +
> +static void hdmirx_set_fmt(struct hdmirx_stream *stream,
> + struct v4l2_pix_format_mplane *pixm, bool try)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_bt_timings *bt = &hdmirx_dev->timings.bt;
> + const struct v4l2_format_info *finfo;
> + unsigned int imagesize = 0;
> + int i;
> +
> + memset(&pixm->plane_fmt[0], 0, sizeof(struct v4l2_plane_pix_format));
> + finfo = v4l2_format_info(pixm->pixelformat);
> + if (!finfo) {
> + finfo = v4l2_format_info(V4L2_PIX_FMT_BGR24);
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: set_fmt:%#x not supported, use def_fmt:%x\n",
> + __func__, pixm->pixelformat, finfo->format);
> + }
> +
> + if (!bt->width || !bt->height)
> + v4l2_dbg(1, debug, v4l2_dev, "%s: invalid resolution:%#xx%#x\n",
> + __func__, bt->width, bt->height);
> +
> + pixm->pixelformat = finfo->format;
> + pixm->width = bt->width;
> + pixm->height = bt->height;
> + pixm->num_planes = finfo->mem_planes;
> + pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
> + pixm->colorspace = V4L2_COLORSPACE_SRGB;
> + pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +
> + if (bt->interlaced == V4L2_DV_INTERLACED)
> + pixm->field = V4L2_FIELD_INTERLACED_TB;
> + else
> + pixm->field = V4L2_FIELD_NONE;
> +
> + memset(pixm->reserved, 0, sizeof(pixm->reserved));
> +
> + v4l2_fill_pixfmt_mp(pixm, finfo->format, pixm->width, pixm->height);
> +
> + for (i = 0; i < pixm->num_planes; i++) {
> + struct v4l2_plane_pix_format *plane_fmt;
> + int width, height, bpl, size, bpp = 0;
> +
> + if (!i) {
> + width = pixm->width;
> + height = pixm->height;
> + } else {
> + width = pixm->width / finfo->hdiv;
> + height = pixm->height / finfo->vdiv;
> + }
> +
> + switch (finfo->format) {
> + case V4L2_PIX_FMT_NV24:
> + case V4L2_PIX_FMT_NV16:
> + case V4L2_PIX_FMT_NV12:
> + case V4L2_PIX_FMT_BGR24:
> + bpp = finfo->bpp[i];
> + break;
> + default:
> + v4l2_dbg(1, debug, v4l2_dev,
> + "fourcc: %#x is not supported\n",
> + finfo->format);
> + break;
> + }
> +
> + bpl = ALIGN(width * bpp, MEMORY_ALIGN_ROUND_UP_BYTES);
> + size = bpl * height;
> + imagesize += size;
> +
> + if (finfo->mem_planes > i) {
> + /* Set bpl and size for each mplane */
> + plane_fmt = pixm->plane_fmt + i;
> + plane_fmt->bytesperline = bpl;
> + plane_fmt->sizeimage = size;
> + }
> +
> + v4l2_dbg(1, debug, v4l2_dev,
> + "C-Plane %i size: %d, Total imagesize: %d\n",
> + i, size, imagesize);
> + }
> +
> + /* Convert to non-MPLANE format as we want to unify non-MPLANE and MPLANE */
> + if (finfo->mem_planes == 1)
> + pixm->plane_fmt[0].sizeimage = imagesize;
> +
> + if (!try) {
> + stream->out_finfo = finfo;
> + stream->pixm = *pixm;
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: req(%d, %d), out(%d, %d), fmt:%#x\n", __func__,
> + pixm->width, pixm->height, stream->pixm.width,
> + stream->pixm.height, finfo->format);
> + }
> +}
> +
> +static int hdmirx_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> +
> + if (f->index >= 1)
> + return -EINVAL;
> +
> + f->pixelformat = hdmirx_dev->cur_fmt_fourcc;
> +
> + return 0;
> +}
> +
> +static int hdmirx_s_fmt_vid_cap_mplane(struct file *file,
> + void *priv, struct v4l2_format *f)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (vb2_is_busy(&stream->buf_queue)) {
> + v4l2_err(v4l2_dev, "%s: queue busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + hdmirx_set_fmt(stream, &f->fmt.pix_mp, false);
> +
> + return 0;
> +}
> +
> +static int hdmirx_g_fmt_vid_cap_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_pix_format_mplane pixm = {};
> +
> + pixm.pixelformat = hdmirx_dev->cur_fmt_fourcc;
> + hdmirx_set_fmt(stream, &pixm, true);
> + f->fmt.pix_mp = pixm;
> +
> + return 0;
> +}
> +
> +static int hdmirx_g_dv_timings(struct file *file, void *_fh,
> + struct v4l2_dv_timings *timings)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 dma_cfg1;
> +
> + *timings = hdmirx_dev->timings;
> + dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
> + v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
> + __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
> +
> + return 0;
> +}
> +
> +static int hdmirx_s_dv_timings(struct file *file, void *_fh,
> + struct v4l2_dv_timings *timings)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (!timings)
> + return -EINVAL;
> +
> + if (debug)
> + v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
> + "s_dv_timings: ", timings, false);
> +
> + if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: timings out of range\n", __func__);
> + return -ERANGE;
> + }
> +
> + /* Check if the timings are part of the CEA-861 timings. */
> + v4l2_find_dv_timings_cap(timings, &hdmirx_timings_cap, 0, NULL, NULL);
> +
> + if (v4l2_match_dv_timings(&hdmirx_dev->timings, timings, 0, false)) {
> + v4l2_dbg(1, debug, v4l2_dev, "%s: no change\n", __func__);
> + return 0;
> + }
> +
> + /*
> + * Changing the timings implies a format change, which is not allowed
> + * while buffers for use with streaming have already been allocated.
> + */
> + if (vb2_is_busy(&stream->buf_queue))
> + return -EBUSY;
> +
> + hdmirx_dev->timings = *timings;
> + /* Update the internal format */
> + hdmirx_set_fmt(stream, &stream->pixm, false);
> +
> + return 0;
> +}
> +
> +static int hdmirx_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct device *dev = stream->hdmirx_dev->dev;
> +
> + strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
> + strscpy(cap->card, dev->driver->name, sizeof(cap->card));
> +
> + return 0;
> +}
> +
> +static int hdmirx_queue_setup(struct vb2_queue *queue,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[],
> + struct device *alloc_ctxs[])
> +{
> + struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + const struct v4l2_pix_format_mplane *pixm = NULL;
> + const struct v4l2_format_info *out_finfo;
> + u32 i, height;
> +
> + pixm = &stream->pixm;
> + out_finfo = stream->out_finfo;
> +
> + if (!num_planes || !out_finfo) {
> + v4l2_err(v4l2_dev, "%s: out_fmt not set\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (*num_planes) {
> + if (*num_planes != pixm->num_planes)
> + return -EINVAL;
> +
> + for (i = 0; i < *num_planes; i++)
> + if (sizes[i] < pixm->plane_fmt[i].sizeimage)
> + return -EINVAL;
> + return 0;
> + }
> +
> + *num_planes = out_finfo->mem_planes;
> + height = pixm->height;
> +
> + for (i = 0; i < out_finfo->mem_planes; i++)
> + sizes[i] = pixm->plane_fmt[i].sizeimage;
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: count %d, size %d\n",
> + v4l2_type_names[queue->type], *num_buffers, sizes[0]);
> +
> + return 0;
> +}
> +
> +/*
> + * The vb2_buffer are stored in hdmirx_buffer, in order to unify
> + * mplane buffer and none-mplane buffer.
> + */
> +static void hdmirx_buf_queue(struct vb2_buffer *vb)
> +{
> + const struct v4l2_format_info *out_finfo;
> + struct vb2_v4l2_buffer *vbuf;
> + struct hdmirx_buffer *hdmirx_buf;
> + struct vb2_queue *queue;
> + struct hdmirx_stream *stream;
> + const struct v4l2_pix_format_mplane *pixm;
> + unsigned long lock_flags = 0;
> + int i;
> +
> + vbuf = to_vb2_v4l2_buffer(vb);
> + hdmirx_buf = container_of(vbuf, struct hdmirx_buffer, vb);
> + queue = vb->vb2_queue;
> + stream = vb2_get_drv_priv(queue);
> + pixm = &stream->pixm;
> + out_finfo = stream->out_finfo;
> +
> + memset(hdmirx_buf->buff_addr, 0, sizeof(hdmirx_buf->buff_addr));
> +
> + /*
> + * If mplanes > 1, every c-plane has its own m-plane,
> + * otherwise, multiple c-planes are in the same m-plane
> + */
> + for (i = 0; i < out_finfo->mem_planes; i++)
> + hdmirx_buf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
> +
> + if (out_finfo->mem_planes == 1) {
> + if (out_finfo->comp_planes == 1) {
> + hdmirx_buf->buff_addr[HDMIRX_PLANE_CBCR] =
> + hdmirx_buf->buff_addr[HDMIRX_PLANE_Y];
> + } else {
> + for (i = 0; i < out_finfo->comp_planes - 1; i++)
> + hdmirx_buf->buff_addr[i + 1] =
> + hdmirx_buf->buff_addr[i] +
> + pixm->plane_fmt[i].bytesperline *
> + pixm->height;
> + }
> + }
> +
> + spin_lock_irqsave(&stream->vbq_lock, lock_flags);
> + list_add_tail(&hdmirx_buf->queue, &stream->buf_head);
> + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
> +}
> +
> +static void return_all_buffers(struct hdmirx_stream *stream,
> + enum vb2_buffer_state state)
> +{
> + struct hdmirx_buffer *buf;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&stream->vbq_lock, flags);
> + if (stream->curr_buf)
> + list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
> + if (stream->next_buf && stream->next_buf != stream->curr_buf)
> + list_add_tail(&stream->next_buf->queue, &stream->buf_head);
> + stream->curr_buf = NULL;
> + stream->next_buf = NULL;
> +
> + while (!list_empty(&stream->buf_head)) {
> + buf = list_first_entry(&stream->buf_head,
> + struct hdmirx_buffer, queue);
> + list_del(&buf->queue);
> + spin_unlock_irqrestore(&stream->vbq_lock, flags);
> + vb2_buffer_done(&buf->vb.vb2_buf, state);
> + spin_lock_irqsave(&stream->vbq_lock, flags);
> + }
> + spin_unlock_irqrestore(&stream->vbq_lock, flags);
> +}
> +
> +static void hdmirx_stop_streaming(struct vb2_queue *queue)
> +{
> + struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int ret;
> +
> + v4l2_info(v4l2_dev, "stream start stopping\n");
> + mutex_lock(&hdmirx_dev->stream_lock);
> + WRITE_ONCE(stream->stopping, true);
> +
> + /* wait last irq to return the buffer */
> + ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
> + msecs_to_jiffies(500));
> + if (!ret) {
> + v4l2_err(v4l2_dev, "%s: timeout waiting last irq\n",
> + __func__);
> + WRITE_ONCE(stream->stopping, false);
> + }
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
> + return_all_buffers(stream, VB2_BUF_STATE_ERROR);
> + mutex_unlock(&hdmirx_dev->stream_lock);
> + v4l2_info(v4l2_dev, "stream stopping finished\n");
> +}
> +
> +static int hdmirx_start_streaming(struct vb2_queue *queue, unsigned int count)
> +{
> + struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_dv_timings timings = hdmirx_dev->timings;
> + struct v4l2_bt_timings *bt = &timings.bt;
> + unsigned long lock_flags = 0;
> + int line_flag;
> +
> + if (!hdmirx_dev->got_timing) {
> + v4l2_dbg(1, debug, v4l2_dev, "timing is invalid\n");
> + return 0;
> + }
> +
> + mutex_lock(&hdmirx_dev->stream_lock);
> + stream->frame_idx = 0;
> + stream->line_flag_int_cnt = 0;
> + stream->curr_buf = NULL;
> + stream->next_buf = NULL;
> + stream->irq_stat = 0;
> + queue->min_queued_buffers = 1;
> +
> + WRITE_ONCE(stream->stopping, false);
> +
> + spin_lock_irqsave(&stream->vbq_lock, lock_flags);
> + if (!stream->curr_buf) {
> + if (!list_empty(&stream->buf_head)) {
> + stream->curr_buf = list_first_entry(&stream->buf_head,
> + struct hdmirx_buffer,
> + queue);
> + list_del(&stream->curr_buf->queue);
> + } else {
> + stream->curr_buf = NULL;
> + }
> + }
> + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
> +
> + v4l2_dbg(2, debug, v4l2_dev,
> + "%s: start_stream cur_buf y_addr:%#x, uv_addr:%#x\n",
> + __func__, stream->curr_buf->buff_addr[HDMIRX_PLANE_Y],
> + stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
> + stream->curr_buf->buff_addr[HDMIRX_PLANE_Y]);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
> + stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
> +
> + if (bt->height) {
> + if (bt->interlaced == V4L2_DV_INTERLACED)
> + line_flag = bt->height / 4;
> + else
> + line_flag = bt->height / 2;
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
> + LINE_FLAG_NUM_MASK,
> + LINE_FLAG_NUM(line_flag));
> + } else {
> + v4l2_err(v4l2_dev, "height err: %d\n", bt->height);
> + }
> +
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, CED_DYN_CONTROL, 0x1);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, HDMIRX_DMA_EN);
> + v4l2_dbg(1, debug, v4l2_dev, "%s: enable dma", __func__);
> + mutex_unlock(&hdmirx_dev->stream_lock);
> +
> + return 0;
> +}
> +
> +/* vb2 queue */
> +static const struct vb2_ops hdmirx_vb2_ops = {
> + .queue_setup = hdmirx_queue_setup,
> + .buf_queue = hdmirx_buf_queue,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .stop_streaming = hdmirx_stop_streaming,
> + .start_streaming = hdmirx_start_streaming,
> +};
> +
> +static int hdmirx_init_vb2_queue(struct vb2_queue *q,
> + struct hdmirx_stream *stream,
> + enum v4l2_buf_type buf_type)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> +
> + q->type = buf_type;
> + q->io_modes = VB2_MMAP | VB2_DMABUF;
> + q->drv_priv = stream;
> + q->ops = &hdmirx_vb2_ops;
> + q->mem_ops = &vb2_dma_contig_memops;
> + q->buf_struct_size = sizeof(struct hdmirx_buffer);
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->lock = &stream->vlock;
> + q->dev = hdmirx_dev->dev;
> + /*
> + * rk3588 doesn't use iommu and works only with dma buffers
> + * that are physically contiguous in memory.
> + */
> + q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> + return vb2_queue_init(q);
> +}
> +
> +/* video device */
> +static const struct v4l2_ioctl_ops hdmirx_v4l2_ioctl_ops = {
> + .vidioc_querycap = hdmirx_querycap,
> + .vidioc_try_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
> + .vidioc_s_fmt_vid_cap_mplane = hdmirx_s_fmt_vid_cap_mplane,
> + .vidioc_g_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
> + .vidioc_enum_fmt_vid_cap = hdmirx_enum_fmt_vid_cap_mplane,
> +
> + .vidioc_s_dv_timings = hdmirx_s_dv_timings,
> + .vidioc_g_dv_timings = hdmirx_g_dv_timings,
> + .vidioc_enum_dv_timings = hdmirx_enum_dv_timings,
> + .vidioc_query_dv_timings = hdmirx_query_dv_timings,
> + .vidioc_dv_timings_cap = hdmirx_dv_timings_cap,
> + .vidioc_enum_input = hdmirx_enum_input,
> + .vidioc_g_input = hdmirx_get_input,
> + .vidioc_s_input = hdmirx_set_input,
> + .vidioc_g_edid = hdmirx_get_edid,
> + .vidioc_s_edid = hdmirx_set_edid,
> + .vidioc_g_parm = hdmirx_g_parm,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .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 = hdmirx_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static const struct v4l2_file_operations hdmirx_fops = {
> + .owner = THIS_MODULE,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .unlocked_ioctl = video_ioctl2,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> +};
> +
> +static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct video_device *vdev = &stream->vdev;
> + int ret = 0;
> +
> + strscpy(vdev->name, "stream_hdmirx", sizeof(vdev->name));
> + INIT_LIST_HEAD(&stream->buf_head);
> + spin_lock_init(&stream->vbq_lock);
> + mutex_init(&stream->vlock);
> + init_waitqueue_head(&stream->wq_stopped);
> + stream->curr_buf = NULL;
> + stream->next_buf = NULL;
> +
> + vdev->ioctl_ops = &hdmirx_v4l2_ioctl_ops;
> + vdev->release = video_device_release_empty;
> + vdev->fops = &hdmirx_fops;
> + vdev->minor = -1;
> + vdev->v4l2_dev = v4l2_dev;
> + vdev->lock = &stream->vlock;
> + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> + V4L2_CAP_STREAMING;
> + video_set_drvdata(vdev, stream);
> + vdev->vfl_dir = VFL_DIR_RX;
> +
> + hdmirx_init_vb2_queue(&stream->buf_queue, stream,
> + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> + vdev->queue = &stream->buf_queue;
> +
> + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
> + if (ret < 0) {
> + v4l2_err(v4l2_dev, "video_register_device failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN, 0);
> + hdmirx_reset_dma(hdmirx_dev);
> + hdmirx_dev->got_timing = false;
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_res_change,
> + msecs_to_jiffies(50));
> +}
> +
> +static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (status & (CED_DYN_CNT_CH2_IRQ |
> + CED_DYN_CNT_CH1_IRQ |
> + CED_DYN_CNT_CH0_IRQ)) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: avp0_st:%#x\n",
> + __func__, status);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_FORCE, 0x0);
> +}
> +
> +static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (status & DEFRAMER_VSYNC_THR_REACHED_IRQ) {
> + v4l2_info(v4l2_dev, "Vertical Sync threshold reached interrupt %#x", status);
> + hdmirx_update_bits(hdmirx_dev, AVPUNIT_1_INT_MASK_N,
> + DEFRAMER_VSYNC_THR_REACHED_MASK_N, 0);
> + *handled = true;
> + }
> +}
> +
> +static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "mu0_st:%#x\n", status);
> + if (status & TIMER_BASE_LOCKED_IRQ) {
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
> + TIMER_BASE_LOCKED_IRQ, 0);
> + complete(&hdmirx_dev->timer_base_lock);
> + *handled = true;
> + }
> +
> + if (status & TMDSQPCLK_OFF_CHG) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_OFF_CHG\n", __func__);
> + *handled = true;
> + }
> +
> + if (status & TMDSQPCLK_LOCKED_CHG) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_LOCKED_CHG\n", __func__);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_FORCE, 0x0);
> +}
> +
> +static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "mu2_st:%#x\n", status);
> + if (status & PHYCREG_CR_WRITE_DONE) {
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
> + PHYCREG_CR_WRITE_DONE, 0);
> + complete(&hdmirx_dev->cr_write_done);
> + *handled = true;
> + }
> +
> + if (status & TMDSVALID_STABLE_CHG) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_FORCE, 0x0);
> +}
> +
> +static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: pk2_st:%#x\n", __func__, status);
> + if (status & PKTDEC_AVIIF_RCV_IRQ) {
> + hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
> + PKTDEC_AVIIF_RCV_IRQ, 0);
> + complete(&hdmirx_dev->avi_pkt_rcv);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: AVIIF_RCV_IRQ\n", __func__);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
> +}
> +
> +static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: scdc_st:%#x\n", __func__, status);
> + if (status & SCDCTMDSCCFG_CHG) {
> + hdmirx_tmds_clk_ratio_config(hdmirx_dev);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
> +}
> +
> +static irqreturn_t hdmirx_hdmi_irq_handler(int irq, void *dev_id)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_id;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct arm_smccc_res res;
> + u32 mu0_st, mu2_st, pk2_st, scdc_st, avp1_st, avp0_st;
> + u32 mu0_mask, mu2_mask, pk2_mask, scdc_mask, avp1_msk, avp0_msk;
> + bool handled = false;
> +
> + mu0_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_MASK_N);
> + mu2_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_MASK_N);
> + pk2_mask = hdmirx_readl(hdmirx_dev, PKT_2_INT_MASK_N);
> + scdc_mask = hdmirx_readl(hdmirx_dev, SCDC_INT_MASK_N);
> + mu0_st = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_STATUS);
> + mu2_st = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_STATUS);
> + pk2_st = hdmirx_readl(hdmirx_dev, PKT_2_INT_STATUS);
> + scdc_st = hdmirx_readl(hdmirx_dev, SCDC_INT_STATUS);
> + avp0_st = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_STATUS);
> + avp1_st = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_STATUS);
> + avp0_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_MASK_N);
> + avp1_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_MASK_N);
> + mu0_st &= mu0_mask;
> + mu2_st &= mu2_mask;
> + pk2_st &= pk2_mask;
> + avp1_st &= avp1_msk;
> + avp0_st &= avp0_msk;
> + scdc_st &= scdc_mask;
> +
> + if (avp0_st)
> + avpunit_0_int_handler(hdmirx_dev, avp0_st, &handled);
> + if (avp1_st)
> + avpunit_1_int_handler(hdmirx_dev, avp1_st, &handled);
> + if (mu0_st)
> + mainunit_0_int_handler(hdmirx_dev, mu0_st, &handled);
> + if (mu2_st)
> + mainunit_2_int_handler(hdmirx_dev, mu2_st, &handled);
> + if (pk2_st)
> + pkt_2_int_handler(hdmirx_dev, pk2_st, &handled);
> + if (scdc_st)
> + scdc_int_handler(hdmirx_dev, scdc_st, &handled);
> +
> + if (!handled) {
> + v4l2_dbg(2, debug, v4l2_dev, "%s: hdmi irq not handled", __func__);
> + v4l2_dbg(2, debug, v4l2_dev,
> + "avp0:%#x, avp1:%#x, mu0:%#x, mu2:%#x, pk2:%#x, scdc:%#x\n",
> + avp0_st, avp1_st, mu0_st, mu2_st, pk2_st, scdc_st);
> + }
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: en_fiq", __func__);
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + return handled ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static void hdmirx_vb_done(struct hdmirx_stream *stream,
> + struct vb2_v4l2_buffer *vb_done)
> +{
> + const struct v4l2_format_info *finfo = stream->out_finfo;
> + u32 i;
> +
> + /* Dequeue a filled buffer */
> + for (i = 0; i < finfo->mem_planes; i++) {
> + vb2_set_plane_payload(&vb_done->vb2_buf, i,
> + stream->pixm.plane_fmt[i].sizeimage);
> + }
> +
> + vb_done->vb2_buf.timestamp = ktime_get_ns();
> + vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + bool *handled)
> +{
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_dv_timings timings = hdmirx_dev->timings;
> + struct v4l2_bt_timings *bt = &timings.bt;
> + struct vb2_v4l2_buffer *vb_done = NULL;
> +
> + if (!(stream->irq_stat) && !(stream->irq_stat & LINE_FLAG_INT_EN))
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: last time have no line_flag_irq\n", __func__);
> +
> + if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
> + goto DMA_IDLE_OUT;
> +
> + if (bt->interlaced != V4L2_DV_INTERLACED ||
> + !(stream->line_flag_int_cnt % 2)) {
> + if (stream->next_buf) {
> + if (stream->curr_buf)
> + vb_done = &stream->curr_buf->vb;
> +
> + if (vb_done) {
> + vb_done->vb2_buf.timestamp = ktime_get_ns();
> + vb_done->sequence = stream->frame_idx;
> + hdmirx_vb_done(stream, vb_done);
> + stream->frame_idx++;
> + if (stream->frame_idx == 30)
> + v4l2_info(v4l2_dev, "rcv frames\n");
> + }
> +
> + stream->curr_buf = NULL;
> + if (stream->next_buf) {
> + stream->curr_buf = stream->next_buf;
> + stream->next_buf = NULL;
> + }
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev,
> + "%s: next_buf NULL, skip vb_done\n", __func__);
> + }
> + }
> +
> +DMA_IDLE_OUT:
> + *handled = true;
> +}
> +
> +static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + bool *handled)
> +{
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_dv_timings timings = hdmirx_dev->timings;
> + struct v4l2_bt_timings *bt = &timings.bt;
> + u32 dma_cfg6;
> +
> + stream->line_flag_int_cnt++;
> + if (!(stream->irq_stat) && !(stream->irq_stat & HDMIRX_DMA_IDLE_INT))
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: last have no dma_idle_irq\n", __func__);
> + dma_cfg6 = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
> + if (!(dma_cfg6 & HDMIRX_DMA_EN)) {
> + v4l2_dbg(2, debug, v4l2_dev, "%s: dma not on\n", __func__);
> + goto LINE_FLAG_OUT;
> + }
> +
> + if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
> + goto LINE_FLAG_OUT;
> +
> + if (bt->interlaced != V4L2_DV_INTERLACED ||
> + !(stream->line_flag_int_cnt % 2)) {
> + if (!stream->next_buf) {
> + spin_lock(&stream->vbq_lock);
> + if (!list_empty(&stream->buf_head)) {
> + stream->next_buf = list_first_entry(&stream->buf_head,
> + struct hdmirx_buffer,
> + queue);
> + list_del(&stream->next_buf->queue);
> + } else {
> + stream->next_buf = NULL;
> + }
> + spin_unlock(&stream->vbq_lock);
> +
> + if (stream->next_buf) {
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
> + stream->next_buf->buff_addr[HDMIRX_PLANE_Y]);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
> + stream->next_buf->buff_addr[HDMIRX_PLANE_CBCR]);
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev,
> + "%s: no buffer is available\n", __func__);
> + }
> + }
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev, "%s: interlace:%d, line_flag_int_cnt:%d\n",
> + __func__, bt->interlaced, stream->line_flag_int_cnt);
> + }
> +
> +LINE_FLAG_OUT:
> + *handled = true;
> +}
> +
> +static irqreturn_t hdmirx_dma_irq_handler(int irq, void *dev_id)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_id;
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 dma_stat1, dma_stat13;
> + bool handled = false;
> +
> + dma_stat1 = hdmirx_readl(hdmirx_dev, DMA_STATUS1);
> + dma_stat13 = hdmirx_readl(hdmirx_dev, DMA_STATUS13);
> + v4l2_dbg(3, debug, v4l2_dev, "dma_irq st1:%#x, st13:%d\n",
> + dma_stat1, dma_stat13);
> +
> + if (READ_ONCE(stream->stopping)) {
> + v4l2_dbg(1, debug, v4l2_dev, "%s: stop stream\n", __func__);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN, 0);
> + WRITE_ONCE(stream->stopping, false);
> + wake_up(&stream->wq_stopped);
> + return IRQ_HANDLED;
> + }
> +
> + if (dma_stat1 & HDMIRX_DMA_IDLE_INT)
> + dma_idle_int_handler(hdmirx_dev, &handled);
> +
> + if (dma_stat1 & LINE_FLAG_INT_EN)
> + line_flag_int_handler(hdmirx_dev, &handled);
> +
> + if (!handled)
> + v4l2_dbg(3, debug, v4l2_dev,
> + "%s: dma irq not handled, dma_stat1:%#x\n",
> + __func__, dma_stat1);
> +
> + stream->irq_stat = dma_stat1;
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void hdmirx_plugin(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct arm_smccc_res res;
> + int ret;
> +
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_heartbeat,
> + msecs_to_jiffies(10));
> + arm_smccc_smc(SIP_WDT_CFG, WDT_START, 0, 0, 0, 0, 0, 0, &res);
> + hdmirx_submodule_init(hdmirx_dev);
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
> + POWERPROVIDED);
> + hdmirx_hpd_ctrl(hdmirx_dev, true);
> + hdmirx_phy_config(hdmirx_dev);
> + ret = hdmirx_wait_lock_and_get_timing(hdmirx_dev);
> + if (ret) {
> + hdmirx_plugout(hdmirx_dev);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(200));
> + return;
> + }
> + hdmirx_dma_config(hdmirx_dev);
> + hdmirx_interrupts_setup(hdmirx_dev, true);
> +}
> +
> +static void hdmirx_delayed_work_hotplug(struct work_struct *work)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + bool plugin;
> +
> + hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
> + delayed_work_hotplug.work);
> +
> + mutex_lock(&hdmirx_dev->work_lock);
> + hdmirx_dev->got_timing = false;
> + plugin = tx_5v_power_present(hdmirx_dev);
> + v4l2_ctrl_s_ctrl(hdmirx_dev->detect_tx_5v_ctrl, plugin);
> + v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
> + __func__, plugin);
> +
> + if (plugin)
> + hdmirx_plugin(hdmirx_dev);
> + else
> + hdmirx_plugout(hdmirx_dev);
> +
> + mutex_unlock(&hdmirx_dev->work_lock);
> +}
> +
> +static void hdmirx_delayed_work_res_change(struct work_struct *work)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + bool plugin;
> +
> + hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
> + delayed_work_res_change.work);
> +
> + mutex_lock(&hdmirx_dev->work_lock);
> + plugin = tx_5v_power_present(hdmirx_dev);
> + v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
> + __func__, plugin);
> + if (plugin) {
> + hdmirx_interrupts_setup(hdmirx_dev, false);
> + hdmirx_submodule_init(hdmirx_dev);
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
> + POWERPROVIDED);
> + hdmirx_hpd_ctrl(hdmirx_dev, true);
> + hdmirx_phy_config(hdmirx_dev);
> +
> + if (hdmirx_wait_lock_and_get_timing(hdmirx_dev)) {
> + hdmirx_plugout(hdmirx_dev);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(200));
> + } else {
> + hdmirx_dma_config(hdmirx_dev);
> + hdmirx_interrupts_setup(hdmirx_dev, true);
> + }
> + }
> + mutex_unlock(&hdmirx_dev->work_lock);
> +}
> +
> +static void hdmirx_delayed_work_heartbeat(struct work_struct *work)
> +{
> + struct delayed_work *dwork = to_delayed_work(work);
> + struct snps_hdmirx_dev *hdmirx_dev = container_of(dwork,
> + struct snps_hdmirx_dev,
> + delayed_work_heartbeat);
> +
> + queue_work(system_highpri_wq, &hdmirx_dev->work_wdt_config);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_heartbeat, HZ);
> +}
> +
> +static void hdmirx_work_wdt_config(struct work_struct *work)
> +{
> + struct arm_smccc_res res;
> + struct snps_hdmirx_dev *hdmirx_dev = container_of(work,
> + struct snps_hdmirx_dev,
> + work_wdt_config);
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + arm_smccc_smc(SIP_WDT_CFG, WDT_PING, 0, 0, 0, 0, 0, 0, &res);
> + v4l2_dbg(3, debug, v4l2_dev, "hb\n");
> +}
> +
> +static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_id;
> + u32 val;
> +
> + val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
> + v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val);
> +
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(10));
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct hdmirx_cec_ops hdmirx_cec_ops = {
> + .write = hdmirx_writel,
> + .read = hdmirx_readl,
> +};
> +
> +static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + int ret;
> +
> + hdmirx_dev->num_clks = devm_clk_bulk_get_all(dev, &hdmirx_dev->clks);
> + if (hdmirx_dev->num_clks < 1)
> + return -ENODEV;
> +
> + hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
> + hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
> + hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
> + hdmirx_dev->resets[HDMIRX_RST_BIU].id = "biu";
> +
> + ret = devm_reset_control_bulk_get_exclusive(dev, HDMIRX_NUM_RST,
> + hdmirx_dev->resets);
> + if (ret < 0) {
> + dev_err(dev, "failed to get reset controls\n");
> + return ret;
> + }
> +
> + hdmirx_dev->detect_5v_gpio =
> + devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
> +
> + if (IS_ERR(hdmirx_dev->detect_5v_gpio)) {
> + dev_err(dev, "failed to get hdmirx hot plug detection gpio\n");
> + return PTR_ERR(hdmirx_dev->detect_5v_gpio);
> + }
> +
> + hdmirx_dev->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(hdmirx_dev->grf)) {
> + dev_err(dev, "failed to get rockchip,grf\n");
> + return PTR_ERR(hdmirx_dev->grf);
> + }
> +
> + hdmirx_dev->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,vo1-grf");
> + if (IS_ERR(hdmirx_dev->vo1_grf)) {
> + dev_err(dev, "failed to get rockchip,vo1-grf\n");
> + return PTR_ERR(hdmirx_dev->vo1_grf);
> + }
> +
> + hdmirx_dev->hpd_trigger_level = !device_property_read_bool(dev, "hpd-is-active-low");
> +
> + ret = of_reserved_mem_device_init(dev);
> + if (ret)
> + dev_warn(dev, "No reserved memory for HDMIRX, use default CMA\n");
> +
> + return 0;
> +}
> +
> +static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_1_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_1_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, PKT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, PKT_1_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, PKT_2_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, SCDC_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, CEC_INT_MASK_N, 0);
> +
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, HDCP_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, HDCP_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff);
> +}
> +
> +static int hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0);
> +
> + regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2,
> + (HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) |
> + ((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16));
> + /*
> + * Some interrupts are enabled by default, so we disable
> + * all interrupts and clear interrupts status first.
> + */
> + hdmirx_disable_all_interrupts(hdmirx_dev);
> +
> + return 0;
> +}
> +
> +static void hdmirx_load_default_edid(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + int ret;
> + struct v4l2_edid def_edid;
> +
> + hdmirx_hpd_ctrl(hdmirx_dev, false);
> +
> + /* disable hpd and write edid */
> + def_edid.pad = 0;
> + def_edid.start_block = 0;
> + def_edid.blocks = EDID_NUM_BLOCKS_MAX;
> +
> + if (IS_ENABLED(CONFIG_HDMIRX_LOAD_DEFAULT_EDID))
> + def_edid.edid = edid_init_data_340M;
> + else
> + def_edid.edid = hdmirx_dev->edid;
> +
> + ret = hdmirx_write_edid(hdmirx_dev, &def_edid, true);
> + if (ret)
> + dev_err(hdmirx_dev->dev, "%s: write edid failed\n", __func__);
> +}
> +
> +static void hdmirx_disable_irq(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct arm_smccc_res res;
> +
> + disable_irq(hdmirx_dev->hdmi_irq);
> + disable_irq(hdmirx_dev->dma_irq);
> + disable_irq(hdmirx_dev->det_irq);
> +
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_DIS,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_hotplug);
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_res_change);
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_heartbeat);
> + flush_work(&hdmirx_dev->work_wdt_config);
> +
> + arm_smccc_smc(SIP_WDT_CFG, WDT_STOP, 0, 0, 0, 0, 0, 0, &res);
> +}
> +
> +static int hdmirx_disable(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + clk_bulk_disable_unprepare(hdmirx_dev->num_clks, hdmirx_dev->clks);
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: suspend\n", __func__);
> +
> + return pinctrl_pm_select_sleep_state(dev);
> +}
> +
> +static void hdmirx_enable_irq(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct arm_smccc_res res;
> +
> + enable_irq(hdmirx_dev->hdmi_irq);
> + enable_irq(hdmirx_dev->dma_irq);
> + enable_irq(hdmirx_dev->det_irq);
> +
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + queue_delayed_work(system_unbound_wq, &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(20));
> +}
> +
> +static int hdmirx_enable(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int ret;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: resume\n", __func__);
> + ret = pinctrl_pm_select_default_state(dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(hdmirx_dev->num_clks, hdmirx_dev->clks);
> + if (ret) {
> + dev_err(dev, "failed to enable hdmirx bulk clks: %d\n", ret);
> + return ret;
> + }
> +
> + reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> + usleep_range(150, 160);
> + reset_control_bulk_deassert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> + usleep_range(150, 160);
> +
> + return 0;
> +}
> +
> +static int hdmirx_suspend(struct device *dev)
> +{
> + hdmirx_disable_irq(dev);
> +
> + return hdmirx_disable(dev);
> +}
> +
> +static int hdmirx_resume(struct device *dev)
> +{
> + int ret = hdmirx_enable(dev);
> +
> + if (ret)
> + return ret;
> +
> + hdmirx_enable_irq(dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops snps_hdmirx_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(hdmirx_suspend, hdmirx_resume)
> +};
> +
> +static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + int ret, irq;
> +
> + irq = platform_get_irq_byname(pdev, "hdmi");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get hdmi irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->hdmi_irq = irq;
> + ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
> + "rk_hdmirx-hdmi", hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request hdmi irq\n");
> + return ret;
> + }
> +
> + irq = platform_get_irq_byname(pdev, "dma");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get dma irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->dma_irq = irq;
> + ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
> + IRQF_ONESHOT, "rk_hdmirx-dma",
> + hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request dma irq\n");
> + return ret;
> + }
> +
> + irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->det_irq = irq;
> + ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
> + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
> + "rk_hdmirx-5v", hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + struct hdmirx_cec_data cec_data;
> + int irq;
> +
> + irq = platform_get_irq_byname(pdev, "cec");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get cec irq\n");
> + return irq;
> + }
> +
> + hdmirx_dev->cec_notifier = cec_notifier_conn_register(dev, NULL, NULL);
> + if (!hdmirx_dev->cec_notifier)
> + return -EINVAL;
> +
> + cec_data.hdmirx = hdmirx_dev;
> + cec_data.dev = hdmirx_dev->dev;
> + cec_data.ops = &hdmirx_cec_ops;
> + cec_data.irq = irq;
> +
> + hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
> + if (!hdmirx_dev->cec) {
> + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int hdmirx_probe(struct platform_device *pdev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + struct device *dev = &pdev->dev;
> + struct v4l2_ctrl_handler *hdl;
> + struct hdmirx_stream *stream;
> + struct v4l2_device *v4l2_dev;
> + int ret;
> +
> + hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
> + if (!hdmirx_dev)
> + return -ENOMEM;
> +
> + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->dev = dev;
> + dev_set_drvdata(dev, hdmirx_dev);
> +
> + ret = hdmirx_parse_dt(hdmirx_dev);
> + if (ret)
> + return ret;
> +
> + ret = hdmirx_setup_irq(hdmirx_dev, pdev);
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(hdmirx_dev->regs))
> + return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
> + "failed to remap regs resource\n");
> +
> + mutex_init(&hdmirx_dev->stream_lock);
> + mutex_init(&hdmirx_dev->work_lock);
> + spin_lock_init(&hdmirx_dev->rst_lock);
> +
> + init_completion(&hdmirx_dev->cr_write_done);
> + init_completion(&hdmirx_dev->timer_base_lock);
> + init_completion(&hdmirx_dev->avi_pkt_rcv);
> +
> + INIT_WORK(&hdmirx_dev->work_wdt_config, hdmirx_work_wdt_config);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
> + hdmirx_delayed_work_hotplug);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
> + hdmirx_delayed_work_res_change);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_heartbeat,
> + hdmirx_delayed_work_heartbeat);
> +
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + hdmirx_dev->timings = cea640x480;
> +
> + hdmirx_enable(dev);
> + hdmirx_init(hdmirx_dev);
> +
> + v4l2_dev = &hdmirx_dev->v4l2_dev;
> + strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
> +
> + hdl = &hdmirx_dev->hdl;
> + v4l2_ctrl_handler_init(hdl, 1);
> +
> + hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
> + V4L2_CID_DV_RX_POWER_PRESENT,
> + 0, 1, 0, 0);
> +
> + hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, 0,
> + V4L2_CID_DV_RX_RGB_RANGE,
> + V4L2_DV_RGB_RANGE_FULL, 0,
> + V4L2_DV_RGB_RANGE_AUTO);
> +
> + hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + if (hdl->error) {
> + dev_err(dev, "v4l2 ctrl handler init failed\n");
> + ret = hdl->error;
> + goto err_pm;
> + }
> + hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
> +
> + ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
> + if (ret < 0) {
> + dev_err(dev, "register v4l2 device failed\n");
> + goto err_hdl;
> + }
> +
> + stream = &hdmirx_dev->stream;
> + stream->hdmirx_dev = hdmirx_dev;
> + ret = hdmirx_register_stream_vdev(stream);
> + if (ret < 0) {
> + dev_err(dev, "register video device failed\n");
> + goto err_unreg_v4l2_dev;
> + }
> +
> + ret = hdmirx_register_cec(hdmirx_dev, pdev);
> + if (ret)
> + goto err_unreg_video_dev;
> +
> + hdmirx_load_default_edid(hdmirx_dev);
> +
> + hdmirx_enable_irq(dev);
> +
> + return 0;
> +
> +err_unreg_video_dev:
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> +err_unreg_v4l2_dev:
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +err_hdl:
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> +err_pm:
> + hdmirx_disable(dev);
> +
> + return ret;
> +}
> +
> +static void hdmirx_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> +
> + snps_hdmirx_cec_unregister(hdmirx_dev->cec);
> + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> +
> + hdmirx_disable_irq(dev);
> +
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +
> + hdmirx_disable(dev);
> +
> + reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> +
> + of_reserved_mem_device_release(dev);
> +}
> +
> +static const struct of_device_id hdmirx_id[] = {
> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, hdmirx_id);
>
>
According to the platform_driver struct, they like the `remove_new` over remove.
When I was compiling for Armbian, I was getting a type mismatch that prevented compililng
See here: https://github.com/torvalds/linux/blob/933069701c1b507825b514317d4edd5d3fd9d417/include/linux/platform_device.h#L236
>
> +
> +static struct platform_driver hdmirx_driver = {
> + .probe = hdmirx_probe,
> + .remove = hdmirx_remove,
> + .driver = {
> + .name = "snps_hdmirx",
> + .of_match_table = hdmirx_id,
> + .pm = &snps_hdmirx_pm_ops,
> + }
> +};
> +module_platform_driver(hdmirx_driver);
> +
> +MODULE_DESCRIPTION("Rockchip HDMI Receiver Driver");
> +MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
> +MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> new file mode 100644
> index 000000000000..220ab99ca611
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> @@ -0,0 +1,394 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + *
> + * Author: Dingxian Wen <shawn.wen@rock-chips.com>
> + */
> +
> +#ifndef DW_HDMIRX_H
> +#define DW_HDMIRX_H
> +
> +#include <linux/bitops.h>
> +
> +#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
> +#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
> +
> +/* SYS_GRF */
> +#define SYS_GRF_SOC_CON1 0x0304
> +#define HDMIRXPHY_SRAM_EXT_LD_DONE BIT(1)
> +#define HDMIRXPHY_SRAM_BYPASS BIT(0)
> +#define SYS_GRF_SOC_STATUS1 0x0384
> +#define HDMIRXPHY_SRAM_INIT_DONE BIT(10)
> +#define SYS_GRF_CHIP_ID 0x0600
> +
> +/* VO1_GRF */
> +#define VO1_GRF_VO1_CON2 0x0008
> +#define HDMIRX_SDAIN_MSK BIT(2)
> +#define HDMIRX_SCLIN_MSK BIT(1)
> +
> +/* HDMIRX PHY */
> +#define SUP_DIG_ANA_CREGS_SUP_ANA_NC 0x004f
> +
> +#define LANE0_DIG_ASIC_RX_OVRD_OUT_0 0x100f
> +#define LANE1_DIG_ASIC_RX_OVRD_OUT_0 0x110f
> +#define LANE2_DIG_ASIC_RX_OVRD_OUT_0 0x120f
> +#define LANE3_DIG_ASIC_RX_OVRD_OUT_0 0x130f
> +#define ASIC_ACK_OVRD_EN BIT(1)
> +#define ASIC_ACK BIT(0)
> +
> +#define LANE0_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x104a
> +#define LANE1_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x114a
> +#define LANE2_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x124a
> +#define LANE3_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x134a
> +#define FREQ_TUNE_START_VAL_MASK GENMASK(9, 0)
> +#define FREQ_TUNE_START_VAL(x) UPDATE(x, 9, 0)
> +
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG 0x20c4
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_ADAPT_REF_FOM 0x20c7
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG 0x20e9
> +#define CDR_SETTING_BOUNDARY_3_DEFAULT 0x52da
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG 0x20ea
> +#define CDR_SETTING_BOUNDARY_4_DEFAULT 0x43cd
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG 0x20eb
> +#define CDR_SETTING_BOUNDARY_5_DEFAULT 0x35b3
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG 0x20fb
> +#define CDR_SETTING_BOUNDARY_6_DEFAULT 0x2799
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG 0x20fc
> +#define CDR_SETTING_BOUNDARY_7_DEFAULT 0x1b65
> +
> +#define RAWLANE0_DIG_PCS_XF_RX_OVRD_OUT 0x300e
> +#define RAWLANE1_DIG_PCS_XF_RX_OVRD_OUT 0x310e
> +#define RAWLANE2_DIG_PCS_XF_RX_OVRD_OUT 0x320e
> +#define RAWLANE3_DIG_PCS_XF_RX_OVRD_OUT 0x330e
> +#define PCS_ACK_WRITE_SELECT BIT(14)
> +#define PCS_EN_CTL BIT(1)
> +#define PCS_ACK BIT(0)
> +
> +#define RAWLANE0_DIG_AON_FAST_FLAGS 0x305c
> +#define RAWLANE1_DIG_AON_FAST_FLAGS 0x315c
> +#define RAWLANE2_DIG_AON_FAST_FLAGS 0x325c
> +#define RAWLANE3_DIG_AON_FAST_FLAGS 0x335c
> +
> +/* HDMIRX Ctrler */
> +#define GLOBAL_SWRESET_REQUEST 0x0020
> +#define DATAPATH_SWRESETREQ BIT(12)
> +#define GLOBAL_SWENABLE 0x0024
> +#define PHYCTRL_ENABLE BIT(21)
> +#define CEC_ENABLE BIT(16)
> +#define TMDS_ENABLE BIT(13)
> +#define DATAPATH_ENABLE BIT(12)
> +#define PKTFIFO_ENABLE BIT(11)
> +#define AVPUNIT_ENABLE BIT(8)
> +#define MAIN_ENABLE BIT(0)
> +#define GLOBAL_TIMER_REF_BASE 0x0028
> +#define CORE_CONFIG 0x0050
> +#define CMU_CONFIG0 0x0060
> +#define TMDSQPCLK_STABLE_FREQ_MARGIN_MASK GENMASK(30, 16)
> +#define TMDSQPCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 30, 16)
> +#define AUDCLK_STABLE_FREQ_MARGIN_MASK GENMASK(11, 9)
> +#define AUDCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 11, 9)
> +#define CMU_STATUS 0x007c
> +#define TMDSQPCLK_LOCKED_ST BIT(4)
> +#define CMU_TMDSQPCLK_FREQ 0x0084
> +#define PHY_CONFIG 0x00c0
> +#define LDO_AFE_PROG_MASK GENMASK(24, 23)
> +#define LDO_AFE_PROG(x) UPDATE(x, 24, 23)
> +#define LDO_PWRDN BIT(21)
> +#define TMDS_CLOCK_RATIO BIT(16)
> +#define RXDATA_WIDTH BIT(15)
> +#define REFFREQ_SEL_MASK GENMASK(11, 9)
> +#define REFFREQ_SEL(x) UPDATE(x, 11, 9)
> +#define HDMI_DISABLE BIT(8)
> +#define PHY_PDDQ BIT(1)
> +#define PHY_RESET BIT(0)
> +#define PHY_STATUS 0x00c8
> +#define HDMI_DISABLE_ACK BIT(1)
> +#define PDDQ_ACK BIT(0)
> +#define PHYCREG_CONFIG0 0x00e0
> +#define PHYCREG_CR_PARA_SELECTION_MODE_MASK GENMASK(1, 0)
> +#define PHYCREG_CR_PARA_SELECTION_MODE(x) UPDATE(x, 1, 0)
> +#define PHYCREG_CONFIG1 0x00e4
> +#define PHYCREG_CONFIG2 0x00e8
> +#define PHYCREG_CONFIG3 0x00ec
> +#define PHYCREG_CONTROL 0x00f0
> +#define PHYCREG_CR_PARA_WRITE_P BIT(1)
> +#define PHYCREG_CR_PARA_READ_P BIT(0)
> +#define PHYCREG_STATUS 0x00f4
> +
> +#define MAINUNIT_STATUS 0x0150
> +#define TMDSVALID_STABLE_ST BIT(1)
> +#define DESCRAND_EN_CONTROL 0x0210
> +#define SCRAMB_EN_SEL_QST_MASK GENMASK(1, 0)
> +#define SCRAMB_EN_SEL_QST(x) UPDATE(x, 1, 0)
> +#define DESCRAND_SYNC_CONTROL 0x0214
> +#define RECOVER_UNSYNC_STREAM_QST BIT(0)
> +#define DESCRAND_SYNC_SEQ_CONFIG 0x022c
> +#define DESCRAND_SYNC_SEQ_ERR_CNT_EN BIT(0)
> +#define DESCRAND_SYNC_SEQ_STATUS 0x0234
> +#define DEFRAMER_CONFIG0 0x0270
> +#define VS_CNT_THR_QST_MASK GENMASK(27, 20)
> +#define VS_CNT_THR_QST(x) UPDATE(x, 27, 20)
> +#define HS_POL_QST_MASK GENMASK(19, 18)
> +#define HS_POL_QST(x) UPDATE(x, 19, 18)
> +#define VS_POL_QST_MASK GENMASK(17, 16)
> +#define VS_POL_QST(x) UPDATE(x, 17, 16)
> +#define VS_REMAPFILTER_EN_QST BIT(8)
> +#define VS_FILTER_ORDER_QST_MASK GENMASK(1, 0)
> +#define VS_FILTER_ORDER_QST(x) UPDATE(x, 1, 0)
> +#define DEFRAMER_VSYNC_CNT_CLEAR 0x0278
> +#define VSYNC_CNT_CLR_P BIT(0)
> +#define DEFRAMER_STATUS 0x027c
> +#define OPMODE_STS_MASK GENMASK(6, 4)
> +#define I2C_SLAVE_CONFIG1 0x0164
> +#define I2C_SDA_OUT_HOLD_VALUE_QST_MASK GENMASK(15, 8)
> +#define I2C_SDA_OUT_HOLD_VALUE_QST(x) UPDATE(x, 15, 8)
> +#define I2C_SDA_IN_HOLD_VALUE_QST_MASK GENMASK(7, 0)
> +#define I2C_SDA_IN_HOLD_VALUE_QST(x) UPDATE(x, 7, 0)
> +#define OPMODE_STS_MASK GENMASK(6, 4)
> +#define REPEATER_QST BIT(28)
> +#define FASTREAUTH_QST BIT(27)
> +#define FEATURES_1DOT1_QST BIT(26)
> +#define FASTI2C_QST BIT(25)
> +#define EESS_CTL_THR_QST_MASK GENMASK(19, 16)
> +#define EESS_CTL_THR_QST(x) UPDATE(x, 19, 16)
> +#define OESS_CTL3_THR_QST_MASK GENMASK(11, 8)
> +#define OESS_CTL3_THR_QST(x) UPDATE(x, 11, 8)
> +#define EESS_OESS_SEL_QST_MASK GENMASK(5, 4)
> +#define EESS_OESS_SEL_QST(x) UPDATE(x, 5, 4)
> +#define KEY_DECRYPT_EN_QST BIT(0)
> +#define KEY_DECRYPT_SEED_QST_MASK GENMASK(15, 0)
> +#define KEY_DECRYPT_SEED_QST(x) UPDATE(x, 15, 0)
> +#define HDCP_INT_CLEAR 0x50d8
> +#define HDCP_1_INT_CLEAR 0x50e8
> +#define HDCP2_CONFIG 0x02f0
> +#define HDCP2_SWITCH_OVR_VALUE BIT(2)
> +#define HDCP2_SWITCH_OVR_EN BIT(1)
> +
> +#define VIDEO_CONFIG2 0x042c
> +#define VPROC_VSYNC_POL_OVR_VALUE BIT(19)
> +#define VPROC_VSYNC_POL_OVR_EN BIT(18)
> +#define VPROC_HSYNC_POL_OVR_VALUE BIT(17)
> +#define VPROC_HSYNC_POL_OVR_EN BIT(16)
> +#define VPROC_FMT_OVR_VALUE_MASK GENMASK(6, 4)
> +#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
> +#define VPROC_FMT_OVR_EN BIT(0)
> +
> +#define AFIFO_FILL_RESTART BIT(0)
> +#define AFIFO_INIT_P BIT(0)
> +#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
> +#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
> +#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
> +#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
> +#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
> +#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
> +#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
> +#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
> +
> +#define AFIFO_UNDERFLOW_ST BIT(25)
> +#define AFIFO_OVERFLOW_ST BIT(24)
> +
> +#define SPEAKER_ALLOC_OVR_EN BIT(16)
> +#define I2S_BPCUV_EN BIT(4)
> +#define SPDIF_EN BIT(2)
> +#define I2S_EN BIT(1)
> +#define AFIFO_THR_PASS_DEMUTEMASK_N BIT(24)
> +#define AVMUTE_DEMUTEMASK_N BIT(16)
> +#define AFIFO_THR_MUTE_LOW_MUTEMASK_N BIT(9)
> +#define AFIFO_THR_MUTE_HIGH_MUTEMASK_N BIT(8)
> +#define AVMUTE_MUTEMASK_N BIT(0)
> +#define SCDC_CONFIG 0x0580
> +#define HPDLOW BIT(1)
> +#define POWERPROVIDED BIT(0)
> +#define SCDC_REGBANK_STATUS1 0x058c
> +#define SCDC_TMDSBITCLKRATIO BIT(1)
> +#define SCDC_REGBANK_STATUS3 0x0594
> +#define SCDC_REGBANK_CONFIG0 0x05c0
> +#define SCDC_SINKVERSION_QST_MASK GENMASK(7, 0)
> +#define SCDC_SINKVERSION_QST(x) UPDATE(x, 7, 0)
> +#define AGEN_LAYOUT BIT(4)
> +#define AGEN_SPEAKER_ALLOC GENMASK(15, 8)
> +
> +#define CED_CONFIG 0x0760
> +#define CED_VIDDATACHECKEN_QST BIT(27)
> +#define CED_DATAISCHECKEN_QST BIT(26)
> +#define CED_GBCHECKEN_QST BIT(25)
> +#define CED_CTRLCHECKEN_QST BIT(24)
> +#define CED_CHLOCKMAXER_QST_MASK GENMASK(14, 0)
> +#define CED_CHLOCKMAXER_QST(x) UPDATE(x, 14, 0)
> +#define CED_DYN_CONFIG 0x0768
> +#define CED_DYN_CONTROL 0x076c
> +#define PKTEX_BCH_ERRFILT_CONFIG 0x07c4
> +#define PKTEX_CHKSUM_ERRFILT_CONFIG 0x07c8
> +
> +#define PKTDEC_ACR_PH2_1 0x1100
> +#define PKTDEC_ACR_PB3_0 0x1104
> +#define PKTDEC_ACR_PB7_4 0x1108
> +#define PKTDEC_AVIIF_PH2_1 0x1200
> +#define PKTDEC_AVIIF_PB3_0 0x1204
> +#define PKTDEC_AVIIF_PB7_4 0x1208
> +#define VIC_VAL_MASK GENMASK(6, 0)
> +#define PKTDEC_AVIIF_PB11_8 0x120c
> +#define PKTDEC_AVIIF_PB15_12 0x1210
> +#define PKTDEC_AVIIF_PB19_16 0x1214
> +#define PKTDEC_AVIIF_PB23_20 0x1218
> +#define PKTDEC_AVIIF_PB27_24 0x121c
> +
> +#define PKTFIFO_CONFIG 0x1500
> +#define PKTFIFO_STORE_FILT_CONFIG 0x1504
> +#define PKTFIFO_THR_CONFIG0 0x1508
> +#define PKTFIFO_THR_CONFIG1 0x150c
> +#define PKTFIFO_CONTROL 0x1510
> +
> +#define VMON_STATUS1 0x1580
> +#define VMON_STATUS2 0x1584
> +#define VMON_STATUS3 0x1588
> +#define VMON_STATUS4 0x158c
> +#define VMON_STATUS5 0x1590
> +#define VMON_STATUS6 0x1594
> +#define VMON_STATUS7 0x1598
> +#define VMON_ILACE_DETECT BIT(4)
> +
> +#define CEC_TX_CONTROL 0x2000
> +#define CEC_STATUS 0x2004
> +#define CEC_CONFIG 0x2008
> +#define RX_AUTO_DRIVE_ACKNOWLEDGE BIT(9)
> +#define CEC_ADDR 0x200c
> +#define CEC_TX_COUNT 0x2020
> +#define CEC_TX_DATA3_0 0x2024
> +#define CEC_RX_COUNT_STATUS 0x2040
> +#define CEC_RX_DATA3_0 0x2044
> +#define CEC_LOCK_CONTROL 0x2054
> +#define CEC_RXQUAL_BITTIME_CONFIG 0x2060
> +#define CEC_RX_BITTIME_CONFIG 0x2064
> +#define CEC_TX_BITTIME_CONFIG 0x2068
> +
> +#define DMA_CONFIG1 0x4400
> +#define UV_WID_MASK GENMASK(31, 28)
> +#define UV_WID(x) UPDATE(x, 31, 28)
> +#define Y_WID_MASK GENMASK(27, 24)
> +#define Y_WID(x) UPDATE(x, 27, 24)
> +#define DDR_STORE_FORMAT_MASK GENMASK(15, 12)
> +#define DDR_STORE_FORMAT(x) UPDATE(x, 15, 12)
> +#define ABANDON_EN BIT(0)
> +#define DMA_CONFIG2 0x4404
> +#define DMA_CONFIG3 0x4408
> +#define DMA_CONFIG4 0x440c // dma irq en
> +#define DMA_CONFIG5 0x4410 // dma irq clear status
> +#define LINE_FLAG_INT_EN BIT(8)
> +#define HDMIRX_DMA_IDLE_INT BIT(7)
> +#define HDMIRX_LOCK_DISABLE_INT BIT(6)
> +#define LAST_FRAME_AXI_UNFINISH_INT_EN BIT(5)
> +#define FIFO_OVERFLOW_INT_EN BIT(2)
> +#define FIFO_UNDERFLOW_INT_EN BIT(1)
> +#define HDMIRX_AXI_ERROR_INT_EN BIT(0)
> +#define DMA_CONFIG6 0x4414
> +#define RB_SWAP_EN BIT(9)
> +#define HSYNC_TOGGLE_EN BIT(5)
> +#define VSYNC_TOGGLE_EN BIT(4)
> +#define HDMIRX_DMA_EN BIT(1)
> +#define DMA_CONFIG7 0x4418
> +#define LINE_FLAG_NUM_MASK GENMASK(31, 16)
> +#define LINE_FLAG_NUM(x) UPDATE(x, 31, 16)
> +#define LOCK_FRAME_NUM_MASK GENMASK(11, 0)
> +#define LOCK_FRAME_NUM(x) UPDATE(x, 11, 0)
> +#define DMA_CONFIG8 0x441c
> +#define REG_MIRROR_EN BIT(0)
> +#define DMA_CONFIG9 0x4420
> +#define DMA_CONFIG10 0x4424
> +#define DMA_CONFIG11 0x4428
> +#define EDID_READ_EN_MASK BIT(8)
> +#define EDID_READ_EN(x) UPDATE(x, 8, 8)
> +#define EDID_WRITE_EN_MASK BIT(7)
> +#define EDID_WRITE_EN(x) UPDATE(x, 7, 7)
> +#define EDID_SLAVE_ADDR_MASK GENMASK(6, 0)
> +#define EDID_SLAVE_ADDR(x) UPDATE(x, 6, 0)
> +#define DMA_STATUS1 0x4430 // dma irq status
> +#define DMA_STATUS2 0x4434
> +#define DMA_STATUS3 0x4438
> +#define DMA_STATUS4 0x443c
> +#define DMA_STATUS5 0x4440
> +#define DMA_STATUS6 0x4444
> +#define DMA_STATUS7 0x4448
> +#define DMA_STATUS8 0x444c
> +#define DMA_STATUS9 0x4450
> +#define DMA_STATUS10 0x4454
> +#define HDMIRX_LOCK BIT(3)
> +#define DMA_STATUS11 0x4458
> +#define HDMIRX_TYPE_MASK GENMASK(8, 7)
> +#define HDMIRX_COLOR_DEPTH_MASK GENMASK(6, 3)
> +#define HDMIRX_FORMAT_MASK GENMASK(2, 0)
> +#define DMA_STATUS12 0x445c
> +#define DMA_STATUS13 0x4460
> +#define DMA_STATUS14 0x4464
> +
> +#define MAINUNIT_INTVEC_INDEX 0x5000
> +#define MAINUNIT_0_INT_STATUS 0x5010
> +#define CECRX_NOTIFY_ERR BIT(12)
> +#define CECRX_EOM BIT(11)
> +#define CECTX_DRIVE_ERR BIT(10)
> +#define CECRX_BUSY BIT(9)
> +#define CECTX_BUSY BIT(8)
> +#define CECTX_FRAME_DISCARDED BIT(5)
> +#define CECTX_NRETRANSMIT_FAIL BIT(4)
> +#define CECTX_LINE_ERR BIT(3)
> +#define CECTX_ARBLOST BIT(2)
> +#define CECTX_NACK BIT(1)
> +#define CECTX_DONE BIT(0)
> +#define MAINUNIT_0_INT_MASK_N 0x5014
> +#define MAINUNIT_0_INT_CLEAR 0x5018
> +#define MAINUNIT_0_INT_FORCE 0x501c
> +#define TIMER_BASE_LOCKED_IRQ BIT(26)
> +#define TMDSQPCLK_OFF_CHG BIT(5)
> +#define TMDSQPCLK_LOCKED_CHG BIT(4)
> +#define MAINUNIT_1_INT_STATUS 0x5020
> +#define MAINUNIT_1_INT_MASK_N 0x5024
> +#define MAINUNIT_1_INT_CLEAR 0x5028
> +#define MAINUNIT_1_INT_FORCE 0x502c
> +#define MAINUNIT_2_INT_STATUS 0x5030
> +#define MAINUNIT_2_INT_MASK_N 0x5034
> +#define MAINUNIT_2_INT_CLEAR 0x5038
> +#define MAINUNIT_2_INT_FORCE 0x503c
> +#define PHYCREG_CR_READ_DONE BIT(11)
> +#define PHYCREG_CR_WRITE_DONE BIT(10)
> +#define TMDSVALID_STABLE_CHG BIT(1)
> +
> +#define AVPUNIT_0_INT_STATUS 0x5040
> +#define AVPUNIT_0_INT_MASK_N 0x5044
> +#define AVPUNIT_0_INT_CLEAR 0x5048
> +#define AVPUNIT_0_INT_FORCE 0x504c
> +#define CED_DYN_CNT_CH2_IRQ BIT(22)
> +#define CED_DYN_CNT_CH1_IRQ BIT(21)
> +#define CED_DYN_CNT_CH0_IRQ BIT(20)
> +#define AVPUNIT_1_INT_STATUS 0x5050
> +#define DEFRAMER_VSYNC_THR_REACHED_IRQ BIT(1)
> +#define AVPUNIT_1_INT_MASK_N 0x5054
> +#define DEFRAMER_VSYNC_THR_REACHED_MASK_N BIT(1)
> +#define DEFRAMER_VSYNC_MASK_N BIT(0)
> +#define AVPUNIT_1_INT_CLEAR 0x5058
> +#define DEFRAMER_VSYNC_THR_REACHED_CLEAR BIT(1)
> +#define PKT_0_INT_STATUS 0x5080
> +#define PKTDEC_ACR_CHG_IRQ BIT(3)
> +#define PKT_0_INT_MASK_N 0x5084
> +#define PKTDEC_ACR_CHG_MASK_N BIT(3)
> +#define PKT_0_INT_CLEAR 0x5088
> +#define PKT_1_INT_STATUS 0x5090
> +#define PKT_1_INT_MASK_N 0x5094
> +#define PKT_1_INT_CLEAR 0x5098
> +#define PKT_2_INT_STATUS 0x50a0
> +#define PKTDEC_ACR_RCV_IRQ BIT(3)
> +#define PKT_2_INT_MASK_N 0x50a4
> +#define PKTDEC_AVIIF_RCV_IRQ BIT(11)
> +#define PKTDEC_ACR_RCV_MASK_N BIT(3)
> +#define PKT_2_INT_CLEAR 0x50a8
> +#define PKTDEC_AVIIF_RCV_CLEAR BIT(11)
> +#define PKTDEC_ACR_RCV_CLEAR BIT(3)
> +#define SCDC_INT_STATUS 0x50c0
> +#define SCDC_INT_MASK_N 0x50c4
> +#define SCDC_INT_CLEAR 0x50c8
> +#define SCDCTMDSCCFG_CHG BIT(2)
> +
> +#define CEC_INT_STATUS 0x5100
> +#define CEC_INT_MASK_N 0x5104
> +#define CEC_INT_CLEAR 0x5108
> +
> +#endif
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> new file mode 100644
> index 000000000000..9f67e2080bb6
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> @@ -0,0 +1,285 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + *
> + * Author: Shunqing Chen <csq@rock-chips.com>
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-notifier.h>
> +
> +#include "snps_hdmirx.h"
> +#include "snps_hdmirx_cec.h"
> +
> +static void hdmirx_cec_write(struct hdmirx_cec *cec, int reg, u32 val)
> +{
> + cec->ops->write(cec->hdmirx, reg, val);
> +}
> +
> +static u32 hdmirx_cec_read(struct hdmirx_cec *cec, int reg)
> +{
> + return cec->ops->read(cec->hdmirx, reg);
> +}
> +
> +static void hdmirx_cec_update_bits(struct hdmirx_cec *cec, int reg, u32 mask,
> + u32 data)
> +{
> + u32 val = hdmirx_cec_read(cec, reg) & ~mask;
> +
> + val |= (data & mask);
> + hdmirx_cec_write(cec, reg, val);
> +}
> +
> +static int hdmirx_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
> +{
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> +
> + if (logical_addr == CEC_LOG_ADDR_INVALID)
> + cec->addresses = 0;
> + else
> + cec->addresses |= BIT(logical_addr) | BIT(15);
> +
> + hdmirx_cec_write(cec, CEC_ADDR, cec->addresses);
> +
> + return 0;
> +}
> +
> +/* signal_free_time is handled by the Synopsys Designware
> + * HDMIRX Controller hardware.
> + */
> +static int hdmirx_cec_transmit(struct cec_adapter *adap, u8 attempts,
> + u32 signal_free_time, struct cec_msg *msg)
> +{
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> + u32 data[4] = {0};
> + int i, data_len, msg_len;
> +
> + msg_len = msg->len;
> +
> + hdmirx_cec_write(cec, CEC_TX_COUNT, msg_len - 1);
> + for (i = 0; i < msg_len; i++)
> + data[i / 4] |= msg->msg[i] << (i % 4) * 8;
> +
> + data_len = DIV_ROUND_UP(msg_len, 4);
> +
> + for (i = 0; i < data_len; i++)
> + hdmirx_cec_write(cec, CEC_TX_DATA3_0 + i * 4, data[i]);
> +
> + hdmirx_cec_write(cec, CEC_TX_CONTROL, 0x1);
> +
> + return 0;
> +}
> +
> +static irqreturn_t hdmirx_cec_hardirq(int irq, void *data)
> +{
> + struct cec_adapter *adap = data;
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> + u32 stat = hdmirx_cec_read(cec, CEC_INT_STATUS);
> + irqreturn_t ret = IRQ_HANDLED;
> + u32 val;
> +
> + if (!stat)
> + return IRQ_NONE;
> +
> + hdmirx_cec_write(cec, CEC_INT_CLEAR, stat);
> +
> + if (stat & CECTX_LINE_ERR) {
> + cec->tx_status = CEC_TX_STATUS_ERROR;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + } else if (stat & CECTX_DONE) {
> + cec->tx_status = CEC_TX_STATUS_OK;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + } else if (stat & CECTX_NACK) {
> + cec->tx_status = CEC_TX_STATUS_NACK;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + } else if (stat & CECTX_ARBLOST) {
> + cec->tx_status = CEC_TX_STATUS_ARB_LOST;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + }
> +
> + if (stat & CECRX_EOM) {
> + unsigned int len, i;
> +
> + val = hdmirx_cec_read(cec, CEC_RX_COUNT_STATUS);
> + /* rxbuffer locked status */
> + if ((val & 0x80))
> + return ret;
> +
> + len = (val & 0xf) + 1;
> + if (len > sizeof(cec->rx_msg.msg))
> + len = sizeof(cec->rx_msg.msg);
> +
> + for (i = 0; i < len; i++) {
> + if (!(i % 4))
> + val = hdmirx_cec_read(cec, CEC_RX_DATA3_0 + i / 4 * 4);
> + cec->rx_msg.msg[i] = (val >> ((i % 4) * 8)) & 0xff;
> + }
> +
> + cec->rx_msg.len = len;
> + smp_wmb(); /* receive RX msg */
> + cec->rx_done = true;
> + hdmirx_cec_write(cec, CEC_LOCK_CONTROL, 0x1);
> +
> + ret = IRQ_WAKE_THREAD;
> + }
> +
> + return ret;
> +}
> +
> +static irqreturn_t hdmirx_cec_thread(int irq, void *data)
> +{
> + struct cec_adapter *adap = data;
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> +
> + if (cec->tx_done) {
> + cec->tx_done = false;
> + cec_transmit_attempt_done(adap, cec->tx_status);
> + }
> + if (cec->rx_done) {
> + cec->rx_done = false;
> + smp_rmb(); /* RX msg has been received */
> + cec_received_msg(adap, &cec->rx_msg);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int hdmirx_cec_enable(struct cec_adapter *adap, bool enable)
> +{
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> +
> + if (!enable) {
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
> + hdmirx_cec_write(cec, CEC_INT_CLEAR, 0);
> + if (cec->ops->disable)
> + cec->ops->disable(cec->hdmirx);
> + } else {
> + unsigned int irqs;
> +
> + hdmirx_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
> + if (cec->ops->enable)
> + cec->ops->enable(cec->hdmirx);
> + hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
> +
> + irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
> + }
> +
> + return 0;
> +}
> +
> +static const struct cec_adap_ops hdmirx_cec_ops = {
> + .adap_enable = hdmirx_cec_enable,
> + .adap_log_addr = hdmirx_cec_log_addr,
> + .adap_transmit = hdmirx_cec_transmit,
> +};
> +
> +static void hdmirx_cec_del(void *data)
> +{
> + struct hdmirx_cec *cec = data;
> +
> + cec_delete_adapter(cec->adap);
> +}
> +
> +struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data)
> +{
> + struct hdmirx_cec *cec;
> + unsigned int irqs;
> + int ret;
> +
> + /*
> + * Our device is just a convenience - we want to link to the real
> + * hardware device here, so that userspace can see the association
> + * between the HDMI hardware and its associated CEC chardev.
> + */
> + cec = devm_kzalloc(data->dev, sizeof(*cec), GFP_KERNEL);
> + if (!cec)
> + return NULL;
> +
> + cec->dev = data->dev;
> + cec->irq = data->irq;
> + cec->ops = data->ops;
> + cec->hdmirx = data->hdmirx;
> +
> + hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
> + hdmirx_cec_update_bits(cec, CEC_CONFIG, RX_AUTO_DRIVE_ACKNOWLEDGE,
> + RX_AUTO_DRIVE_ACKNOWLEDGE);
> +
> + hdmirx_cec_write(cec, CEC_TX_COUNT, 0);
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
> + hdmirx_cec_write(cec, CEC_INT_CLEAR, ~0);
> +
> + cec->adap = cec_allocate_adapter(&hdmirx_cec_ops, cec, "snps-hdmirx",
> + CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
> + CEC_CAP_RC | CEC_CAP_PASSTHROUGH |
> + CEC_CAP_MONITOR_ALL,
> + CEC_MAX_LOG_ADDRS);
> + if (IS_ERR(cec->adap)) {
> + dev_err(cec->dev, "cec adap allocate failed\n");
> + return NULL;
> + }
> +
> + /* override the module pointer */
> + cec->adap->owner = THIS_MODULE;
> +
> + ret = devm_add_action(cec->dev, hdmirx_cec_del, cec);
> + if (ret) {
> + cec_delete_adapter(cec->adap);
> + return NULL;
> + }
> +
> + irq_set_status_flags(cec->irq, IRQ_NOAUTOEN);
> +
> + ret = devm_request_threaded_irq(cec->dev, cec->irq,
> + hdmirx_cec_hardirq,
> + hdmirx_cec_thread, IRQF_ONESHOT,
> + "rk_hdmirx_cec", cec->adap);
> + if (ret) {
> + dev_err(cec->dev, "cec irq request failed\n");
> + return NULL;
> + }
> +
> + cec->notify = cec_notifier_cec_adap_register(cec->dev,
> + NULL, cec->adap);
> + if (!cec->notify) {
> + dev_err(cec->dev, "cec notify register failed\n");
> + return NULL;
> + }
> +
> + ret = cec_register_adapter(cec->adap, cec->dev);
> + if (ret < 0) {
> + dev_err(cec->dev, "cec register adapter failed\n");
> + cec_unregister_adapter(cec->adap);
> + return NULL;
> + }
> +
> + irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
> +
> + /*
> + * CEC documentation says we must not call cec_delete_adapter
> + * after a successful call to cec_register_adapter().
> + */
> + devm_remove_action(cec->dev, hdmirx_cec_del, cec);
> +
> + enable_irq(cec->irq);
> +
> + return cec;
> +}
> +
> +void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec)
> +{
> + disable_irq(cec->irq);
> +
> + cec_unregister_adapter(cec->adap);
> +}
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> new file mode 100644
> index 000000000000..c55c403cdb9f
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + *
> + * Author: Shunqing Chen <csq@rock-chips.com>
> + */
> +
> +#ifndef DW_HDMI_RX_CEC_H
> +#define DW_HDMI_RX_CEC_H
> +
> +struct snps_hdmirx_dev;
> +
> +struct hdmirx_cec_ops {
> + void (*write)(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val);
> + u32 (*read)(struct snps_hdmirx_dev *hdmirx_dev, int reg);
> + void (*enable)(struct snps_hdmirx_dev *hdmirx);
> + void (*disable)(struct snps_hdmirx_dev *hdmirx);
> +};
> +
> +struct hdmirx_cec_data {
> + struct snps_hdmirx_dev *hdmirx;
> + const struct hdmirx_cec_ops *ops;
> + struct device *dev;
> + int irq;
> +};
> +
> +struct hdmirx_cec {
> + struct snps_hdmirx_dev *hdmirx;
> + struct device *dev;
> + const struct hdmirx_cec_ops *ops;
> + u32 addresses;
> + struct cec_adapter *adap;
> + struct cec_msg rx_msg;
> + unsigned int tx_status;
> + bool tx_done;
> + bool rx_done;
> + struct cec_notifier *notify;
> + int irq;
> +};
> +
> +struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data);
> +void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec);
> +
> +#endif /* DW_HDMI_RX_CEC_H */
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-22 19:39 ` hoff.benjamin.k
@ 2024-07-22 20:26 ` Shreeya Patel
0 siblings, 0 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-22 20:26 UTC (permalink / raw)
To: hoff.benjamin.k
Cc: conor+dt, devicetree, heiko, hverkuil-cisco, hverkuil, jose.abreu,
kernel, krzk+dt, linux-arm-kernel, linux-kernel, linux-media,
linux-rockchip, mchehab, mturquette, nelson.costa,
nicolas.dufresne, p.zabel, robh, sboyd, shawn.wen
On Tuesday, July 23, 2024 01:09 IST, hoff.benjamin.k@gmail.com wrote:
Hi Benjamin,
> > Add initial support for the Synopsys DesignWare HDMI RX
> > Controller Driver used by Rockchip RK3588. The driver
> > supports:
> > - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> > - RGB888, YUV422, YUV444 and YCC420 pixel formats
> > - CEC
> > - EDID configuration
> >
> > The hardware also has Audio and HDCP capabilities, but these are
> > not yet supported by the driver.
> >
> > Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> > Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> > Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
> > Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
> > Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> > ---
> >
...
> > +
> > +static const struct of_device_id hdmirx_id[] = {
> > + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> > + { },
> > +};
> > +MODULE_DEVICE_TABLE(of, hdmirx_id);
> >
> >
> According to the platform_driver struct, they like the `remove_new` over remove.
> When I was compiling for Armbian, I was getting a type mismatch that prevented compililng
>
> See here: https://github.com/torvalds/linux/blob/933069701c1b507825b514317d4edd5d3fd9d417/include/linux/platform_device.h#L236
>
This patch series is based on linux-next and there seems to be some recent changes
related to .remove and .remove_new. New drivers are supposed to use .remove()
See the reason here :-
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/include/linux/platform_device.h?h=next-20240722#n239
If you apply the patches on top of linux-next then there shouldn't be any
compilation error.
> >
> > +
> > +static struct platform_driver hdmirx_driver = {
> > + .probe = hdmirx_probe,
> > + .remove = hdmirx_remove,
> > + .driver = {
> > + .name = "snps_hdmirx",
> > + .of_match_table = hdmirx_id,
> > + .pm = &snps_hdmirx_pm_ops,
> > + }
> > +};
> > +module_platform_driver(hdmirx_driver);
> > +
> > +MODULE_DESCRIPTION("Rockchip HDMI Receiver Driver");
> > +MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
> > +MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
> > +MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
` (3 preceding siblings ...)
2024-07-21 15:06 ` Markus Elfring
@ 2024-07-23 8:48 ` Hans Verkuil
2024-09-23 22:24 ` Dmitry Osipenko
2024-09-23 22:36 ` Dmitry Osipenko
4 siblings, 2 replies; 40+ messages in thread
From: Hans Verkuil @ 2024-07-23 8:48 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
On 19/07/2024 14:40, Shreeya Patel wrote:
> Add initial support for the Synopsys DesignWare HDMI RX
> Controller Driver used by Rockchip RK3588. The driver
> supports:
> - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> - RGB888, YUV422, YUV444 and YCC420 pixel formats
> - CEC
> - EDID configuration
>
> The hardware also has Audio and HDCP capabilities, but these are
> not yet supported by the driver.
>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Co-developed-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> ---
>
> Changes in v4 :-
> - Create a separate config option for selecting the EDID
> and enable it by default
> - Improve the comment related to DV timings and move it
> to the side of hdmirx_get_detected_timings
> - Add 100ms delay before pulling the HPD high
> - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> - Drop the bus info from hdmirx_querycap
> - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> - Set queue->min_queued_buffers to 1
> - Drop q->allow_cache_hints = 0; as it's always 0 by default
> - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> - Drop .read = vb2_fop_read as it's not supported by driver
> - Remove redundant edid_init_data_600M
> - Make HPD low when driver is loaded
> - Add support for reading AVI Infoframe
> - Remove msg_len checks from hdmirx_cec_transmit
> - Add info about the CEC compliance test in the cover letter
> - Add arbitration lost status
> - Validate the physical address inside the EDID
>
> Changes in v3 :-
> - Use v4l2-common helper functions
>
> Changes in v2 :-
> - Fix checkpatch --strict warnings
> - Rename resets, vo1-grf and HPD node names as per the DT changes
>
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/synopsys/Kconfig | 3 +
> drivers/media/platform/synopsys/Makefile | 2 +
> .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> .../media/platform/synopsys/hdmirx/Makefile | 4 +
> .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> 10 files changed, 3524 insertions(+)
> create mode 100644 drivers/media/platform/synopsys/Kconfig
> create mode 100644 drivers/media/platform/synopsys/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 85d2627776b6..9287faafdce5 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -85,6 +85,7 @@ source "drivers/media/platform/rockchip/Kconfig"
> source "drivers/media/platform/samsung/Kconfig"
> source "drivers/media/platform/st/Kconfig"
> source "drivers/media/platform/sunxi/Kconfig"
> +source "drivers/media/platform/synopsys/Kconfig"
> source "drivers/media/platform/ti/Kconfig"
> source "drivers/media/platform/verisilicon/Kconfig"
> source "drivers/media/platform/via/Kconfig"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index ace4e34483dd..6fd7db0541c7 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -28,6 +28,7 @@ obj-y += rockchip/
> obj-y += samsung/
> obj-y += st/
> obj-y += sunxi/
> +obj-y += synopsys/
> obj-y += ti/
> obj-y += verisilicon/
> obj-y += via/
> diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
> new file mode 100644
> index 000000000000..4fd521f78425
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/Kconfig
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +source "drivers/media/platform/synopsys/hdmirx/Kconfig"
> diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
> new file mode 100644
> index 000000000000..3b12c574dd67
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-y += hdmirx/
> diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
> new file mode 100644
> index 000000000000..ab569e59300f
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
> @@ -0,0 +1,27 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config VIDEO_SYNOPSYS_HDMIRX
> + tristate "Synopsys DesignWare HDMI Receiver driver"
> + depends on VIDEO_DEV
> + depends on ARCH_ROCKCHIP
> + select MEDIA_CONTROLLER
> + select VIDEO_V4L2_SUBDEV_API
> + select VIDEOBUF2_DMA_CONTIG
> + select CEC_CORE
> + select CEC_NOTIFIER
> + select HDMI
> + help
> + Support for Synopsys HDMI HDMI RX Controller.
> + This driver supports HDMI 2.0 version.
> +
> + To compile this driver as a module, choose M here. The module
> + will be called synopsys_hdmirx.
> +
> +config HDMIRX_LOAD_DEFAULT_EDID
This needs to be prefixed with VIDEO_SYNOPSYS_HDMIRX since this is specific
to this driver.
> + bool "Load default EDID"
> + depends on VIDEO_SYNOPSYS_HDMIRX
> + default "y"
Drop this line. This should be something that needs to be explicitly enabled,
by default it should come up with HPD low and no EDID loaded. In almost all
practical use cases (i.e. when used in a real product) you want to provide
your own EDID.
> + help
> + Preload the default EDID (Extended Display Identification Data).
> + EDID contains information about the capabilities of the display,
> + such as supported resolutions, refresh rates, and audio formats.
> diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
> new file mode 100644
> index 000000000000..2fa2d9e25300
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0
> +synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
> +
> +obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> new file mode 100644
> index 000000000000..1dfecf256393
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> @@ -0,0 +1,2763 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2024 Collabora, Ltd.
> + * Author: Shreeya Patel <shreeya.patel@collabora.com>
> + *
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + * Author: Dingxian Wen <shawn.wen@rock-chips.com>
> + */
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/hdmi.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/workqueue.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-notifier.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-dv-timings.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fh.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/videobuf2-v4l2.h>
> +#include <media/v4l2-common.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#include "snps_hdmirx.h"
> +#include "snps_hdmirx_cec.h"
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "debug level (0-3)");
> +
> +#define EDID_NUM_BLOCKS_MAX 2
> +#define EDID_BLOCK_SIZE 128
> +#define HDMIRX_STORED_BIT_WIDTH 8
> +#define IREF_CLK_FREQ_HZ 428571429
> +#define MEMORY_ALIGN_ROUND_UP_BYTES 64
> +#define HDMIRX_PLANE_Y 0
> +#define HDMIRX_PLANE_CBCR 1
> +#define RK_IRQ_HDMIRX_HDMI 210
> +#define FILTER_FRAME_CNT 6
> +#define RK_SIP_FIQ_CTRL 0x82000024
> +#define SIP_WDT_CFG 0x82000026
> +#define DETECTION_THRESHOLD 7
> +
> +/* fiq control sub func */
> +enum {
> + RK_SIP_FIQ_CTRL_FIQ_EN = 1,
> + RK_SIP_FIQ_CTRL_FIQ_DIS,
> + RK_SIP_FIQ_CTRL_SET_AFF
> +};
> +
> +/* SIP_WDT_CONFIG call types */
> +enum {
> + WDT_START = 0,
> + WDT_STOP = 1,
> + WDT_PING = 2,
> +};
> +
> +enum hdmirx_pix_fmt {
> + HDMIRX_RGB888 = 0,
> + HDMIRX_YUV422 = 1,
> + HDMIRX_YUV444 = 2,
> + HDMIRX_YUV420 = 3,
> +};
> +
> +enum ddr_store_fmt {
> + STORE_RGB888 = 0,
> + STORE_RGBA_ARGB,
> + STORE_YUV420_8BIT,
> + STORE_YUV420_10BIT,
> + STORE_YUV422_8BIT,
> + STORE_YUV422_10BIT,
> + STORE_YUV444_8BIT,
> + STORE_YUV420_16BIT = 8,
> + STORE_YUV422_16BIT = 9,
> +};
> +
> +enum hdmirx_reg_attr {
> + HDMIRX_ATTR_RW = 0,
> + HDMIRX_ATTR_RO = 1,
> + HDMIRX_ATTR_WO = 2,
> + HDMIRX_ATTR_RE = 3,
> +};
> +
> +enum {
> + HDMIRX_RST_A,
> + HDMIRX_RST_P,
> + HDMIRX_RST_REF,
> + HDMIRX_RST_BIU,
> + HDMIRX_NUM_RST,
> +};
> +
> +static const char * const pix_fmt_str[] = {
> + "RGB888",
> + "YUV422",
> + "YUV444",
> + "YUV420",
> +};
> +
> +struct hdmirx_buffer {
> + struct vb2_v4l2_buffer vb;
> + struct list_head queue;
> + u32 buff_addr[VIDEO_MAX_PLANES];
> +};
> +
> +struct hdmirx_stream {
> + struct snps_hdmirx_dev *hdmirx_dev;
> + struct video_device vdev;
> + struct vb2_queue buf_queue;
> + struct list_head buf_head;
> + struct hdmirx_buffer *curr_buf;
> + struct hdmirx_buffer *next_buf;
> + struct v4l2_pix_format_mplane pixm;
> + const struct v4l2_format_info *out_finfo;
> + struct mutex vlock; /* to lock resources associated with video buffer and video device */
> + spinlock_t vbq_lock; /* to lock video buffer queue */
> + bool stopping;
> + wait_queue_head_t wq_stopped;
> + u32 frame_idx;
> + u32 line_flag_int_cnt;
> + u32 irq_stat;
> +};
> +
> +struct snps_hdmirx_dev {
> + struct device *dev;
> + struct device *codec_dev;
> + struct hdmirx_stream stream;
> + struct v4l2_device v4l2_dev;
> + struct v4l2_ctrl_handler hdl;
> + struct v4l2_ctrl *detect_tx_5v_ctrl;
> + struct v4l2_ctrl *rgb_range;
> + struct v4l2_dv_timings timings;
> + struct gpio_desc *detect_5v_gpio;
> + struct work_struct work_wdt_config;
> + struct delayed_work delayed_work_hotplug;
> + struct delayed_work delayed_work_res_change;
> + struct delayed_work delayed_work_heartbeat;
> + struct cec_notifier *cec_notifier;
> + struct hdmirx_cec *cec;
> + struct mutex stream_lock; /* to lock video stream capture */
> + struct mutex work_lock; /* to lock the critical section of hotplug event */
> + struct reset_control_bulk_data resets[HDMIRX_NUM_RST];
> + struct clk_bulk_data *clks;
> + struct regmap *grf;
> + struct regmap *vo1_grf;
> + struct completion cr_write_done;
> + struct completion timer_base_lock;
> + struct completion avi_pkt_rcv;
> + enum hdmirx_pix_fmt pix_fmt;
> + void __iomem *regs;
> + int hdmi_irq;
> + int dma_irq;
> + int det_irq;
> + bool hpd_trigger_level;
> + bool tmds_clk_ratio;
> + bool is_dvi_mode;
> + bool got_timing;
> + u32 num_clks;
> + u32 edid_blocks_written;
> + u32 cur_vic;
> + u32 cur_fmt_fourcc;
> + u32 color_depth;
> + u8 edid[EDID_BLOCK_SIZE * 2];
> + hdmi_codec_plugged_cb plugged_cb;
> + spinlock_t rst_lock; /* to lock register access */
> +};
> +
> +static u8 edid_init_data_340M[] = {
This should be under #ifdef CONFIG_HDMIRX_LOAD_DEFAULT_EDID, since there is
no point to have this if you are not using it.
> + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
> + 0x49, 0x70, 0x88, 0x35, 0x01, 0x00, 0x00, 0x00,
> + 0x2D, 0x1F, 0x01, 0x03, 0x80, 0x78, 0x44, 0x78,
> + 0x0A, 0xCF, 0x74, 0xA3, 0x57, 0x4C, 0xB0, 0x23,
> + 0x09, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x61, 0x40,
> + 0x01, 0x01, 0x81, 0x00, 0x95, 0x00, 0xA9, 0xC0,
> + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
> + 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
> + 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00, 0x1E,
> + 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20,
> + 0x6E, 0x28, 0x55, 0x00, 0x20, 0xC2, 0x31, 0x00,
> + 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x52,
> + 0x4B, 0x2D, 0x55, 0x48, 0x44, 0x0A, 0x20, 0x20,
> + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
> + 0x00, 0x3B, 0x46, 0x1F, 0x8C, 0x3C, 0x00, 0x0A,
> + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xA7,
> +
> + 0x02, 0x03, 0x2F, 0xD1, 0x51, 0x07, 0x16, 0x14,
> + 0x05, 0x01, 0x03, 0x12, 0x13, 0x84, 0x22, 0x1F,
> + 0x90, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x23, 0x09,
> + 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x67, 0x03,
> + 0x0C, 0x00, 0x30, 0x00, 0x10, 0x44, 0xE3, 0x05,
> + 0x03, 0x01, 0xE4, 0x0F, 0x00, 0x80, 0x01, 0x02,
> + 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58,
> + 0x2C, 0x45, 0x00, 0x20, 0xC2, 0x31, 0x00, 0x00,
> + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
Checking this EDID with 'edid-decode -c' I see a lot of warnings and
failures.
You can build edid-decode yourself:
https://git.linuxtv.org/edid-decode.git/
or use this webpage where you can paste in the hexdump:
https://hverkuil.home.xs4all.nl/edid-decode/edid-decode.html
I suspect this was taken from some real product, but in a kernel
driver it is better to use a more vendor-agnostic EDID.
Note that v4l2-ctl contains some vendor-agnostic EDIDs, e.g.:
v4l2-ctl --show-edid type=hdmi-4k-600mhz
That might be more appropriate.
> +};
I would move this EDID to just before the function that uses it, that way
they are together and can be put under the same #ifdef.
> +
> +static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94;
> +
> +static const struct v4l2_dv_timings_cap hdmirx_timings_cap = {
> + .type = V4L2_DV_BT_656_1120,
> + .reserved = { 0 },
> + V4L2_INIT_BT_TIMINGS(640, 4096, /* min/max width */
> + 480, 2160, /* min/max height */
> + 20000000, 600000000, /* min/max pixelclock */
> + /* standards */
> + V4L2_DV_BT_STD_CEA861,
> + /* capabilities */
> + V4L2_DV_BT_CAP_PROGRESSIVE |
> + V4L2_DV_BT_CAP_INTERLACED)
> +};
> +
> +static void hdmirx_writel(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val)
> +{
> + unsigned long lock_flags = 0;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + writel(val, hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
> +
> +static u32 hdmirx_readl(struct snps_hdmirx_dev *hdmirx_dev, int reg)
> +{
> + unsigned long lock_flags = 0;
> + u32 val;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + val = readl(hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> + return val;
> +}
> +
> +static void hdmirx_reset_dma(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + unsigned long lock_flags = 0;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + reset_control_reset(hdmirx_dev->resets[0].rstc);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
> +
> +static void hdmirx_update_bits(struct snps_hdmirx_dev *hdmirx_dev, int reg,
> + u32 mask, u32 data)
> +{
> + unsigned long lock_flags = 0;
> + u32 val;
> +
> + spin_lock_irqsave(&hdmirx_dev->rst_lock, lock_flags);
> + val = readl(hdmirx_dev->regs + reg) & ~mask;
> + val |= (data & mask);
> + writel(val, hdmirx_dev->regs + reg);
> + spin_unlock_irqrestore(&hdmirx_dev->rst_lock, lock_flags);
> +}
> +
> +static int hdmirx_subscribe_event(struct v4l2_fh *fh,
> + const struct v4l2_event_subscription *sub)
> +{
> + switch (sub->type) {
> + case V4L2_EVENT_SOURCE_CHANGE:
> + if (fh->vdev->vfl_dir == VFL_DIR_RX)
> + return v4l2_src_change_event_subscribe(fh, sub);
> + break;
> + case V4L2_EVENT_CTRL:
> + return v4l2_ctrl_subscribe_event(fh, sub);
> + default:
> + break;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + bool ret;
> + int val, i, cnt;
> +
> + cnt = 0;
> + for (i = 0; i < 10; i++) {
> + usleep_range(1000, 1100);
> + val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
> + if (val > 0)
> + cnt++;
> + if (cnt >= DETECTION_THRESHOLD)
> + break;
> + }
> +
> + ret = (cnt >= DETECTION_THRESHOLD) ? true : false;
> + v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret);
> +
> + return ret;
> +}
> +
> +static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + u32 mu_status, dma_st10, cmu_st;
> +
> + mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
> + dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
> + cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
> +
> + if ((mu_status & TMDSVALID_STABLE_ST) &&
> + (dma_st10 & HDMIRX_LOCK) &&
> + (cmu_st & TMDSQPCLK_LOCKED_ST))
> + return false;
> +
> + return true;
> +}
> +
> +static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 val, color_depth_reg;
> +
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
> + color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
> +
> + switch (color_depth_reg) {
> + case 0x4:
> + hdmirx_dev->color_depth = 24;
> + break;
> + case 0x5:
> + hdmirx_dev->color_depth = 30;
> + break;
> + case 0x6:
> + hdmirx_dev->color_depth = 36;
> + break;
> + case 0x7:
> + hdmirx_dev->color_depth = 48;
> + break;
> + default:
> + hdmirx_dev->color_depth = 24;
> + break;
> + }
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
> + __func__, hdmirx_dev->color_depth, color_depth_reg);
> +}
> +
> +static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 val;
> +
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
> + hdmirx_dev->pix_fmt = val & HDMIRX_FORMAT_MASK;
> +
> + switch (hdmirx_dev->pix_fmt) {
> + case HDMIRX_RGB888:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + break;
> + case HDMIRX_YUV422:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV16;
> + break;
> + case HDMIRX_YUV444:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV24;
> + break;
> + case HDMIRX_YUV420:
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV12;
> + break;
> + default:
> + v4l2_err(v4l2_dev,
> + "%s: err pix_fmt: %d, set RGB888 as default\n",
> + __func__, hdmirx_dev->pix_fmt);
> + hdmirx_dev->pix_fmt = HDMIRX_RGB888;
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + break;
> + }
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s\n", __func__,
> + pix_fmt_str[hdmirx_dev->pix_fmt]);
> +}
> +
> +static void hdmirx_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_bt_timings *bt, bool from_dma)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 hact, vact, htotal, vtotal, fps;
> + u32 hfp, hs, hbp, vfp, vs, vbp;
> + u32 val;
> +
> + if (from_dma) {
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS2);
> + hact = (val >> 16) & 0xffff;
> + vact = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS3);
> + htotal = (val >> 16) & 0xffff;
> + vtotal = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS4);
> + hs = (val >> 16) & 0xffff;
> + vs = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS5);
> + hbp = (val >> 16) & 0xffff;
> + vbp = val & 0xffff;
> + hfp = htotal - hact - hs - hbp;
> + vfp = vtotal - vact - vs - vbp;
> + } else {
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS1);
> + hs = (val >> 16) & 0xffff;
> + hfp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS2);
> + hbp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS3);
> + htotal = (val >> 16) & 0xffff;
> + hact = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS4);
> + vs = (val >> 16) & 0xffff;
> + vfp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS5);
> + vbp = val & 0xffff;
> + val = hdmirx_readl(hdmirx_dev, VMON_STATUS6);
> + vtotal = (val >> 16) & 0xffff;
> + vact = val & 0xffff;
> + if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
> + hact *= 2;
> + }
> + if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
> + htotal *= 2;
> + fps = (bt->pixelclock + (htotal * vtotal) / 2) / (htotal * vtotal);
> + if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
> + fps *= 2;
> + bt->width = hact;
> + bt->height = vact;
> + bt->hfrontporch = hfp;
> + bt->hsync = hs;
> + bt->hbackporch = hbp;
> + bt->vfrontporch = vfp;
> + bt->vsync = vs;
> + bt->vbackporch = vbp;
> +
> + v4l2_dbg(1, debug, v4l2_dev, "get timings from %s\n", from_dma ? "dma" : "ctrl");
> + v4l2_dbg(1, debug, v4l2_dev, "act:%ux%u, total:%ux%u, fps:%u, pixclk:%llu\n",
> + bt->width, bt->height, htotal, vtotal, fps, bt->pixelclock);
> +
> + v4l2_dbg(2, debug, v4l2_dev, "hfp:%u, hs:%u, hbp:%u, vfp:%u, vs:%u, vbp:%u\n",
> + bt->hfrontporch, bt->hsync, bt->hbackporch,
> + bt->vfrontporch, bt->vsync, bt->vbackporch);
> +}
> +
> +static bool hdmirx_check_timing_valid(struct v4l2_bt_timings *bt)
> +{
> + if (bt->width < 100 || bt->width > 5000 ||
> + bt->height < 100 || bt->height > 5000)
> + return false;
> +
> + if (!bt->hsync || bt->hsync > 200 ||
> + !bt->vsync || bt->vsync > 100)
> + return false;
> +
> + if (!bt->hbackporch || bt->hbackporch > 2000 ||
> + !bt->vbackporch || bt->vbackporch > 2000)
> + return false;
> +
> + if (!bt->hfrontporch || bt->hfrontporch > 2000 ||
> + !bt->vfrontporch || bt->vfrontporch > 2000)
> + return false;
What exactly does this test for? Is this against hardware capabilities
or just sanity checks? This should be documented.
> +
> + return true;
> +}
> +
> +static void hdmirx_get_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + union hdmi_infoframe frame = {};
> + int err, i, b, itr = 0;
> + u8 aviif[3 + 7 * 4];
> + u32 val;
> +
> + aviif[itr++] = HDMI_INFOFRAME_TYPE_AVI;
> + val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PH2_1);
> + aviif[itr++] = val & 0xff;
> + aviif[itr++] = (val >> 8) & 0xff;
> +
> + for (i = 0; i < 7; i++) {
> + val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB3_0 + 4 * i);
> +
> + for (b = 0; b < 4; b++)
> + aviif[itr++] = (val >> (8 * b)) & 0xff;
> + }
> +
> + err = hdmi_infoframe_unpack(&frame, aviif, sizeof(aviif));
> + if (err) {
> + v4l2_err(v4l2_dev, "failed to unpack AVI infoframe\n");
> + return;
> + }
> +
> + v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, frame.avi.quantization_range);
> +}
> +
> +/*
> + * When querying DV timings during preview, if the DMA's timing is stable,
> + * we retrieve the timings directly from the DMA. However, if the current
> + * resolution is negative, obtaining the timing from CTRL may require a
> + * change in the sync polarity, potentially leading to DMA errors.
> + */
> +static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_dv_timings *timings,
> + bool from_dma)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_bt_timings *bt = &timings->bt;
> + u32 field_type, color_depth, deframer_st;
> + u32 val, tmdsqpclk_freq, pix_clk;
> + u64 tmp_data, tmds_clk;
> +
> + memset(timings, 0, sizeof(struct v4l2_dv_timings));
> + timings->type = V4L2_DV_BT_656_1120;
> +
> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
> + field_type = (val & HDMIRX_TYPE_MASK) >> 7;
> + hdmirx_get_pix_fmt(hdmirx_dev);
Hold on, this changes the pixel format based on the detected video format.
Does that mean this hardware does not have a colorspace conversion block?
All the HDMI receiver hardware I have seen always had a CSC matrix and
could convert between 4:4:4, 4:2:2 and 4:2:0.
> + bt->interlaced = field_type & BIT(0) ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
> + val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB7_4);
> + hdmirx_dev->cur_vic = val | VIC_VAL_MASK;
> + hdmirx_get_colordepth(hdmirx_dev);
> + color_depth = hdmirx_dev->color_depth;
> + deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
> + hdmirx_dev->is_dvi_mode = deframer_st & OPMODE_STS_MASK ? false : true;
> + tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
> + tmds_clk = tmdsqpclk_freq * 4 * 1000;
> + tmp_data = tmds_clk * 24;
> + do_div(tmp_data, color_depth);
> + pix_clk = tmp_data;
> + bt->pixelclock = pix_clk;
> +
> + hdmirx_get_avi_infoframe(hdmirx_dev);
I would move this down to just before the return 0. I.e., only update
if the timing is valid.
> +
> + hdmirx_get_timings(hdmirx_dev, bt, from_dma);
> + if (bt->interlaced == V4L2_DV_INTERLACED) {
> + bt->height *= 2;
> + bt->il_vsync = bt->vsync + 1;
> + }
> +
> + v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu\n", tmds_clk);
> + v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, vic:%d, color:%d, mode:%s\n",
> + bt->interlaced, hdmirx_dev->pix_fmt,
> + hdmirx_dev->cur_vic, hdmirx_dev->color_depth,
> + hdmirx_dev->is_dvi_mode ? "dvi" : "hdmi");
> + v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
> +
> + if (!hdmirx_check_timing_valid(bt))
> + return -EINVAL;
-ERANGE would be more appropriate.
https://hverkuil.home.xs4all.nl/spec/userspace-api/v4l/vidioc-query-dv-timings.html:
ERANGE: Timings were found, but they are out of range of the hardware capabilities.
> +
> + return 0;
> +}
> +
> +static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + return !tx_5v_power_present(hdmirx_dev);
> +}
> +
> +static int hdmirx_query_dv_timings(struct file *file, void *_fh,
> + struct v4l2_dv_timings *timings)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int ret;
> +
> + if (port_no_link(hdmirx_dev)) {
> + v4l2_err(v4l2_dev, "%s: port has no link\n", __func__);
> + return -ENOLINK;
> + }
> +
> + if (signal_not_lock(hdmirx_dev)) {
> + v4l2_err(v4l2_dev, "%s: signal is not locked\n", __func__);
> + return -ENOLCK;
> + }
> +
> + ret = hdmirx_get_detected_timings(hdmirx_dev, timings, true);
> + if (ret)
> + return ret;
> +
> + if (debug)
> + v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
> + "query_dv_timings: ", timings, false);
> +
> + if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
> + v4l2_dbg(1, debug, v4l2_dev, "%s: timings out of range\n", __func__);
> + return -ERANGE;
> + }
> +
> + return 0;
> +}
> +
> +static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: %sable, hpd_trigger_level:%d\n",
> + __func__, en ? "en" : "dis",
> + hdmirx_dev->hpd_trigger_level);
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, HPDLOW, en ? 0 : HPDLOW);
> + en = hdmirx_dev->hpd_trigger_level ? en : !en;
> + hdmirx_writel(hdmirx_dev, CORE_CONFIG, en);
> +}
> +
> +static int hdmirx_write_edid(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_edid *edid, bool hpd_up)
> +{
> + u32 edid_len = edid->blocks * EDID_BLOCK_SIZE;
> + char data[300];
> + u32 i;
> +
> + memset(edid->reserved, 0, sizeof(edid->reserved));
> + if (edid->pad)
> + return -EINVAL;
> +
> + if (edid->start_block)
> + return -EINVAL;
> +
> + if (edid->blocks > EDID_NUM_BLOCKS_MAX) {
> + edid->blocks = EDID_NUM_BLOCKS_MAX;
> + return -E2BIG;
> + }
These checks really belong in hdmirx_set_edid.
It's all a bit messy: I think this function should just write the EDID
and update the CEC physical address.
EDID validation should happen in hdmirx_set_edid. Setting the HPD
(both hardware and the corresponding V4L2 control) should be done in
hdmirx_set_edid and hdmirx_load_default_edid.
That should make it a lot easier to understand, I think.
> +
> + if (!edid->blocks) {
> + hdmirx_dev->edid_blocks_written = 0;
This should call cec_phys_addr_invalidate().
> + return 0;
> + }
> +
You need to validate the physical address of the new EDID first
(v4l2_phys_addr_validate). But do that in hdmirx_set_edid.
> + cec_s_phys_addr_from_edid(hdmirx_dev->cec->adap,
> + (const struct edid *)edid->edid);
> +
> + memset(&hdmirx_dev->edid, 0, sizeof(hdmirx_dev->edid));
> + hdmirx_hpd_ctrl(hdmirx_dev, false);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
> + EDID_READ_EN_MASK |
> + EDID_WRITE_EN_MASK |
> + EDID_SLAVE_ADDR_MASK,
> + EDID_READ_EN(0) |
> + EDID_WRITE_EN(1) |
> + EDID_SLAVE_ADDR(0x50));
> + for (i = 0; i < edid_len; i++)
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG10, edid->edid[i]);
> +
> + /* read out for debug */
> + if (debug >= 2) {
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
> + EDID_READ_EN_MASK |
> + EDID_WRITE_EN_MASK,
> + EDID_READ_EN(1) |
> + EDID_WRITE_EN(0));
> + edid_len = edid_len > sizeof(data) ? sizeof(data) : edid_len;
> + memset(data, 0, sizeof(data));
> + for (i = 0; i < edid_len; i++)
> + data[i] = hdmirx_readl(hdmirx_dev, DMA_STATUS14);
> +
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data,
> + edid_len, false);
> + }
> +
> + /*
> + * You must set EDID_READ_EN & EDID_WRITE_EN bit to 0,
> + * when the read/write edid operation is completed.Otherwise, it
> + * will affect the reading and writing of other registers
> + */
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
> + EDID_READ_EN_MASK | EDID_WRITE_EN_MASK,
> + EDID_READ_EN(0) | EDID_WRITE_EN(0));
> +
> + hdmirx_dev->edid_blocks_written = edid->blocks;
> + memcpy(&hdmirx_dev->edid, edid->edid, edid->blocks * EDID_BLOCK_SIZE);
> + if (hpd_up) {
> + if (tx_5v_power_present(hdmirx_dev)) {
> + /* Add 100ms delay after updating the EDID as per HDMI specs */
> + msleep(100);
OK, so if hpd_up is true, then this is called from load_default_edid, i.e.
when the device is initialized. In that case you do not need to sleep since
the HPD will have been low for longer than 100ms.
Note that hpd_up is a poor name in that case, it is not clear that it is
related to the load_default_edid.
> + hdmirx_hpd_ctrl(hdmirx_dev, true);
As mentioned above, HPD handling should be done in the callers of this function.
> + }
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Before clearing interrupt, we need to read the interrupt status.
> + */
> +static inline void hdmirx_clear_interrupt(struct snps_hdmirx_dev *hdmirx_dev,
> + u32 reg, u32 val)
> +{
> + /* (interrupt status register) = (interrupt clear register) - 0x8 */
> + hdmirx_readl(hdmirx_dev, reg - 0x8);
> + hdmirx_writel(hdmirx_dev, reg, val);
> +}
> +
> +static void hdmirx_interrupts_setup(struct snps_hdmirx_dev *hdmirx_dev, bool en)
> +{
> + v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: %sable\n",
> + __func__, en ? "en" : "dis");
> +
> + /* Note: In DVI mode, it needs to be written twice to take effect. */
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
> +
> + if (en) {
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
> + TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG,
> + TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG);
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
> + TMDSVALID_STABLE_CHG, TMDSVALID_STABLE_CHG);
> + hdmirx_update_bits(hdmirx_dev, AVPUNIT_0_INT_MASK_N,
> + CED_DYN_CNT_CH2_IRQ |
> + CED_DYN_CNT_CH1_IRQ |
> + CED_DYN_CNT_CH0_IRQ,
> + CED_DYN_CNT_CH2_IRQ |
> + CED_DYN_CNT_CH1_IRQ |
> + CED_DYN_CNT_CH0_IRQ);
> + } else {
> + hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
> + }
> +}
> +
> +static void hdmirx_plugout(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct arm_smccc_res res;
> +
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
> + hdmirx_interrupts_setup(hdmirx_dev, false);
> + hdmirx_hpd_ctrl(hdmirx_dev, false);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN, 0);
> + hdmirx_reset_dma(hdmirx_dev);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE | PHY_RESET |
> + PHY_PDDQ, HDMI_DISABLE);
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
> + cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_heartbeat);
> + flush_work(&hdmirx_dev->work_wdt_config);
> + arm_smccc_smc(SIP_WDT_CFG, WDT_STOP, 0, 0, 0, 0, 0, 0, &res);
> +}
> +
> +static int hdmirx_set_edid(struct file *file, void *fh, struct v4l2_edid *edid)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct arm_smccc_res res;
> + int ret;
> +
> + disable_irq(hdmirx_dev->hdmi_irq);
> + disable_irq(hdmirx_dev->dma_irq);
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_DIS,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + if (tx_5v_power_present(hdmirx_dev))
> + hdmirx_plugout(hdmirx_dev);
> + ret = hdmirx_write_edid(hdmirx_dev, edid, false);
> + if (ret)
> + return ret;
> +
> + enable_irq(hdmirx_dev->hdmi_irq);
> + enable_irq(hdmirx_dev->dma_irq);
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(500));
500 is way overkill. Set it to 110 (just a bit over 100 ms).
> + return 0;
> +}
> +
> +static int hdmirx_get_edid(struct file *file, void *fh, struct v4l2_edid *edid)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + memset(edid->reserved, 0, sizeof(edid->reserved));
> +
> + if (edid->pad)
> + return -EINVAL;
> +
> + if (!edid->start_block && !edid->blocks) {
> + edid->blocks = hdmirx_dev->edid_blocks_written;
> + return 0;
> + }
> +
> + if (!hdmirx_dev->edid_blocks_written)
> + return -ENODATA;
> +
> + if (edid->start_block >= hdmirx_dev->edid_blocks_written || !edid->blocks)
> + return -EINVAL;
> +
> + if (edid->start_block + edid->blocks > hdmirx_dev->edid_blocks_written)
> + edid->blocks = hdmirx_dev->edid_blocks_written - edid->start_block;
> +
> + memcpy(edid->edid, &hdmirx_dev->edid, edid->blocks * EDID_BLOCK_SIZE);
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: read EDID:\n", __func__);
> + if (debug > 0)
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
> + edid->edid, edid->blocks * EDID_BLOCK_SIZE, false);
> +
> + return 0;
> +}
> +
> +static int hdmirx_g_parm(struct file *file, void *priv,
> + struct v4l2_streamparm *parm)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_fract fps;
> +
> + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return -EINVAL;
> +
> + fps = v4l2_calc_timeperframe(&hdmirx_dev->timings);
> + parm->parm.capture.timeperframe.numerator = fps.numerator;
> + parm->parm.capture.timeperframe.denominator = fps.denominator;
> +
> + return 0;
> +}
> +
> +static int hdmirx_dv_timings_cap(struct file *file, void *fh,
> + struct v4l2_dv_timings_cap *cap)
> +{
> + *cap = hdmirx_timings_cap;
> + return 0;
> +}
> +
> +static int hdmirx_enum_dv_timings(struct file *file, void *_fh,
> + struct v4l2_enum_dv_timings *timings)
> +{
> + return v4l2_enum_dv_timings_cap(timings, &hdmirx_timings_cap, NULL, NULL);
> +}
> +
> +static void hdmirx_scdc_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_update_bits(hdmirx_dev, I2C_SLAVE_CONFIG1,
> + I2C_SDA_OUT_HOLD_VALUE_QST_MASK |
> + I2C_SDA_IN_HOLD_VALUE_QST_MASK,
> + I2C_SDA_OUT_HOLD_VALUE_QST(0x80) |
> + I2C_SDA_IN_HOLD_VALUE_QST(0x15));
> + hdmirx_update_bits(hdmirx_dev, SCDC_REGBANK_CONFIG0,
> + SCDC_SINKVERSION_QST_MASK,
> + SCDC_SINKVERSION_QST(1));
> +}
> +
> +static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
> + u32 bit_mask, u32 expect_val, bool is_grf,
> + u32 ms)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 i, val;
> +
> + for (i = 0; i < ms; i++) {
> + if (is_grf)
> + regmap_read(hdmirx_dev->grf, reg, &val);
> + else
> + val = hdmirx_readl(hdmirx_dev, reg);
> +
> + if ((val & bit_mask) == expect_val) {
> + v4l2_dbg(2, debug, v4l2_dev,
> + "%s: i:%d, time: %dms\n", __func__, i, ms);
> + break;
> + }
> + usleep_range(1000, 1010);
> + }
> +
> + if (i == ms)
> + return -1;
> +
> + return 0;
> +}
> +
> +static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev,
> + u32 phy_reg, u32 val)
> +{
> + struct device *dev = hdmirx_dev->dev;
> +
> + reinit_completion(&hdmirx_dev->cr_write_done);
> + /* clear irq status */
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + /* en irq */
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
> + PHYCREG_CR_WRITE_DONE, PHYCREG_CR_WRITE_DONE);
> + /* write phy reg addr */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg);
> + /* write phy reg val */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG2, val);
> + /* config write enable */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_WRITE_P);
> +
> + if (!wait_for_completion_timeout(&hdmirx_dev->cr_write_done,
> + msecs_to_jiffies(20))) {
> + dev_err(dev, "%s wait cr write done failed\n", __func__);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static void hdmirx_tmds_clk_ratio_config(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 val;
> +
> + val = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS1);
> + v4l2_dbg(3, debug, v4l2_dev, "%s: scdc_regbank_st:%#x\n", __func__, val);
> + hdmirx_dev->tmds_clk_ratio = (val & SCDC_TMDSBITCLKRATIO) > 0;
> +
> + if (hdmirx_dev->tmds_clk_ratio) {
> + v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX greater than 3.4Gbps\n", __func__);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
> + TMDS_CLOCK_RATIO, TMDS_CLOCK_RATIO);
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX less than 3.4Gbps\n", __func__);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
> + TMDS_CLOCK_RATIO, 0);
> + }
> +}
> +
> +static void hdmirx_phy_config(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> +
> + hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
> + hdmirx_update_bits(hdmirx_dev, SCDC_INT_MASK_N, SCDCTMDSCCFG_CHG,
> + SCDCTMDSCCFG_CHG);
> + /* cr_para_clk 24M */
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, REFFREQ_SEL_MASK, REFFREQ_SEL(0));
> + /* rx data width 40bit valid */
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, RXDATA_WIDTH, RXDATA_WIDTH);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, PHY_RESET);
> + usleep_range(100, 110);
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, 0);
> + usleep_range(100, 110);
> + /* select cr para interface */
> + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3);
> +
> + if (wait_reg_bit_status(hdmirx_dev, SYS_GRF_SOC_STATUS1,
> + HDMIRXPHY_SRAM_INIT_DONE,
> + HDMIRXPHY_SRAM_INIT_DONE, true, 10))
> + dev_err(dev, "%s: phy SRAM init failed\n", __func__);
> +
> + regmap_write(hdmirx_dev->grf, SYS_GRF_SOC_CON1,
> + (HDMIRXPHY_SRAM_EXT_LD_DONE << 16) |
> + HDMIRXPHY_SRAM_EXT_LD_DONE);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 1);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
> + hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
> +
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG,
> + CDR_SETTING_BOUNDARY_3_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG,
> + CDR_SETTING_BOUNDARY_4_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG,
> + CDR_SETTING_BOUNDARY_5_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG,
> + CDR_SETTING_BOUNDARY_6_DEFAULT);
> + hdmirx_phy_register_write(hdmirx_dev,
> + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG,
> + CDR_SETTING_BOUNDARY_7_DEFAULT);
> +
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_PDDQ, 0);
> + if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, PDDQ_ACK, 0, false, 10))
> + dev_err(dev, "%s: wait pddq ack failed\n", __func__);
> +
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE, 0);
> + if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, HDMI_DISABLE_ACK, 0,
> + false, 50))
> + dev_err(dev, "%s: wait hdmi disable ack failed\n", __func__);
> +
> + hdmirx_tmds_clk_ratio_config(hdmirx_dev);
> +}
> +
> +static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> +
> + reinit_completion(&hdmirx_dev->timer_base_lock);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + /* en irq */
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
> + TIMER_BASE_LOCKED_IRQ, TIMER_BASE_LOCKED_IRQ);
> + /* write irefclk freq */
> + hdmirx_writel(hdmirx_dev, GLOBAL_TIMER_REF_BASE, IREF_CLK_FREQ_HZ);
> +
> + if (!wait_for_completion_timeout(&hdmirx_dev->timer_base_lock,
> + msecs_to_jiffies(20)))
> + dev_err(dev, "%s wait timer base lock failed\n", __func__);
> +
> + hdmirx_update_bits(hdmirx_dev, CMU_CONFIG0,
> + TMDSQPCLK_STABLE_FREQ_MARGIN_MASK |
> + AUDCLK_STABLE_FREQ_MARGIN_MASK,
> + TMDSQPCLK_STABLE_FREQ_MARGIN(2) |
> + AUDCLK_STABLE_FREQ_MARGIN(1));
> + hdmirx_update_bits(hdmirx_dev, DESCRAND_EN_CONTROL,
> + SCRAMB_EN_SEL_QST_MASK, SCRAMB_EN_SEL_QST(1));
> + hdmirx_update_bits(hdmirx_dev, CED_CONFIG,
> + CED_VIDDATACHECKEN_QST |
> + CED_DATAISCHECKEN_QST |
> + CED_GBCHECKEN_QST |
> + CED_CTRLCHECKEN_QST |
> + CED_CHLOCKMAXER_QST_MASK,
> + CED_VIDDATACHECKEN_QST |
> + CED_GBCHECKEN_QST |
> + CED_CTRLCHECKEN_QST |
> + CED_CHLOCKMAXER_QST(0x10));
> + hdmirx_update_bits(hdmirx_dev, DEFRAMER_CONFIG0,
> + VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST_MASK,
> + VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
> +}
> +
> +static void hdmirx_set_negative_pol(struct snps_hdmirx_dev *hdmirx_dev, bool en)
> +{
> + if (en) {
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
> + VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN,
> + VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN);
> + hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
> + VPROC_VSYNC_POL_OVR_VALUE |
> + VPROC_VSYNC_POL_OVR_EN |
> + VPROC_HSYNC_POL_OVR_VALUE |
> + VPROC_HSYNC_POL_OVR_EN,
> + VPROC_VSYNC_POL_OVR_EN |
> + VPROC_HSYNC_POL_OVR_EN);
> + return;
> + }
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
> + VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN, 0);
> +
> + hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
> + VPROC_VSYNC_POL_OVR_VALUE |
> + VPROC_VSYNC_POL_OVR_EN |
> + VPROC_HSYNC_POL_OVR_VALUE |
> + VPROC_HSYNC_POL_OVR_EN, 0);
> +}
> +
> +static int hdmirx_try_to_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
> + struct v4l2_dv_timings *timings,
> + int try_cnt)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int i, cnt = 0, fail_cnt = 0, ret = 0;
> + bool from_dma = false;
> +
> + hdmirx_set_negative_pol(hdmirx_dev, false);
> + for (i = 0; i < try_cnt; i++) {
> + ret = hdmirx_get_detected_timings(hdmirx_dev, timings, from_dma);
> + if (ret) {
> + cnt = 0;
> + fail_cnt++;
> + if (fail_cnt > 3) {
> + hdmirx_set_negative_pol(hdmirx_dev, true);
> + from_dma = true;
> + }
> + } else {
> + cnt++;
> + }
> + if (cnt >= 5)
> + break;
> +
> + usleep_range(10 * 1000, 10 * 1100);
> + }
> +
> + if (try_cnt > 8 && cnt < 5)
> + v4l2_dbg(1, debug, v4l2_dev, "%s: res not stable\n", __func__);
> +
> + return ret;
> +}
> +
> +static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_dv_timings timings;
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + const struct v4l2_event ev_src_chg = {
This can be static.
> + .type = V4L2_EVENT_SOURCE_CHANGE,
> + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
> + };
> +
> + if (hdmirx_try_to_get_timings(hdmirx_dev, &timings, 20)) {
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(20));
This makes no sense. Detecting a format has nothing to do with hotplug.
I feel there is something wonky about how this is designed.
> + return;
> + }
> +
> + hdmirx_dev->got_timing = true;
> + v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
> + v4l2_event_queue(&stream->vdev, &ev_src_chg);
> +}
> +
> +static void hdmirx_set_ddr_store_fmt(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + enum ddr_store_fmt store_fmt;
> + u32 dma_cfg1;
> +
> + switch (hdmirx_dev->pix_fmt) {
> + case HDMIRX_RGB888:
> + store_fmt = STORE_RGB888;
> + break;
> + case HDMIRX_YUV444:
> + store_fmt = STORE_YUV444_8BIT;
> + break;
> + case HDMIRX_YUV422:
> + store_fmt = STORE_YUV422_8BIT;
> + break;
> + case HDMIRX_YUV420:
> + store_fmt = STORE_YUV420_8BIT;
> + break;
> + default:
> + store_fmt = STORE_RGB888;
> + break;
> + }
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
> + DDR_STORE_FORMAT_MASK, DDR_STORE_FORMAT(store_fmt));
> + dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
> + v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
> + __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
> +}
> +
> +static int hdmirx_wait_lock_and_get_timing(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 mu_status, scdc_status, dma_st10, cmu_st;
> + u32 i;
> +
> + for (i = 0; i < 300; i++) {
> + mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
> + scdc_status = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS3);
> + dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
> + cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
> +
> + if ((mu_status & TMDSVALID_STABLE_ST) &&
> + (dma_st10 & HDMIRX_LOCK) &&
> + (cmu_st & TMDSQPCLK_LOCKED_ST))
> + break;
> +
> + if (!tx_5v_power_present(hdmirx_dev)) {
> + v4l2_err(v4l2_dev, "%s: HDMI pull out, return\n", __func__);
> + return -1;
> + }
> +
> + hdmirx_tmds_clk_ratio_config(hdmirx_dev);
> + }
> +
> + if (i == 300) {
> + v4l2_err(v4l2_dev, "%s: signal not lock, tmds_clk_ratio:%d\n",
> + __func__, hdmirx_dev->tmds_clk_ratio);
> + v4l2_err(v4l2_dev, "%s: mu_st:%#x, scdc_st:%#x, dma_st10:%#x\n",
> + __func__, mu_status, scdc_status, dma_st10);
> + return -1;
> + }
> +
> + v4l2_info(v4l2_dev, "%s: signal lock ok, i:%d\n", __func__, i);
This should probably be v4l2_dbg.
> + hdmirx_writel(hdmirx_dev, GLOBAL_SWRESET_REQUEST, DATAPATH_SWRESETREQ);
> +
> + reinit_completion(&hdmirx_dev->avi_pkt_rcv);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
> + PKTDEC_AVIIF_RCV_IRQ, PKTDEC_AVIIF_RCV_IRQ);
> +
> + if (!wait_for_completion_timeout(&hdmirx_dev->avi_pkt_rcv,
> + msecs_to_jiffies(300))) {
> + v4l2_err(v4l2_dev, "%s wait avi_pkt_rcv failed\n", __func__);
> + hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
> + PKTDEC_AVIIF_RCV_IRQ, 0);
> + }
> +
> + usleep_range(50 * 1000, 50 * 1010);
> + hdmirx_format_change(hdmirx_dev);
It's all a bit weird.
Typical HDMI receivers will issue interrupts when it syncs to a video stream
(or loses sync, of course). Normally all you need to do is to raise the SOURCE_CHANGE
event, then userspace will call QUERY_DV_TIMINGS, which will then attempt to detect
if the timing is valid.
Are such interrupts missing in this hardware so you have to poll? If so, document
this. Admittedly, SCDC might complicate matters. I believe this is the first capture
driver with SCDC support.
> +
> + return 0;
> +}
> +
> +static void hdmirx_dma_config(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_set_ddr_store_fmt(hdmirx_dev);
> +
> + /* Note: uv_swap, rb can not swap, doc err*/
> + if (hdmirx_dev->cur_fmt_fourcc != V4L2_PIX_FMT_NV16)
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, RB_SWAP_EN);
> + else
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, 0);
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
> + LOCK_FRAME_NUM_MASK,
> + LOCK_FRAME_NUM(2));
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
> + UV_WID_MASK | Y_WID_MASK | ABANDON_EN,
> + UV_WID(1) | Y_WID(2) | ABANDON_EN);
> +}
> +
> +static void hdmirx_submodule_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + /* Note: if not config HDCP2_CONFIG, there will be some errors; */
> + hdmirx_update_bits(hdmirx_dev, HDCP2_CONFIG,
> + HDCP2_SWITCH_OVR_VALUE |
> + HDCP2_SWITCH_OVR_EN,
> + HDCP2_SWITCH_OVR_EN);
> + hdmirx_scdc_init(hdmirx_dev);
> + hdmirx_controller_init(hdmirx_dev);
> +}
> +
> +static int hdmirx_enum_input(struct file *file, void *priv,
> + struct v4l2_input *input)
> +{
> + if (input->index > 0)
> + return -EINVAL;
> +
> + input->type = V4L2_INPUT_TYPE_CAMERA;
> + input->std = 0;
> + strscpy(input->name, "HDMI IN", sizeof(input->name));
> + input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
> +
> + return 0;
> +}
> +
> +static int hdmirx_get_input(struct file *file, void *priv, unsigned int *i)
> +{
> + *i = 0;
> + return 0;
> +}
> +
> +static int hdmirx_set_input(struct file *file, void *priv, unsigned int i)
> +{
> + if (i)
> + return -EINVAL;
> + return 0;
> +}
> +
> +static void hdmirx_set_fmt(struct hdmirx_stream *stream,
> + struct v4l2_pix_format_mplane *pixm, bool try)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_bt_timings *bt = &hdmirx_dev->timings.bt;
> + const struct v4l2_format_info *finfo;
> + unsigned int imagesize = 0;
> + int i;
> +
> + memset(&pixm->plane_fmt[0], 0, sizeof(struct v4l2_plane_pix_format));
> + finfo = v4l2_format_info(pixm->pixelformat);
> + if (!finfo) {
> + finfo = v4l2_format_info(V4L2_PIX_FMT_BGR24);
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: set_fmt:%#x not supported, use def_fmt:%x\n",
> + __func__, pixm->pixelformat, finfo->format);
> + }
> +
> + if (!bt->width || !bt->height)
> + v4l2_dbg(1, debug, v4l2_dev, "%s: invalid resolution:%#xx%#x\n",
> + __func__, bt->width, bt->height);
> +
> + pixm->pixelformat = finfo->format;
> + pixm->width = bt->width;
> + pixm->height = bt->height;
> + pixm->num_planes = finfo->mem_planes;
> + pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
> + pixm->colorspace = V4L2_COLORSPACE_SRGB;
> + pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +
> + if (bt->interlaced == V4L2_DV_INTERLACED)
> + pixm->field = V4L2_FIELD_INTERLACED_TB;
> + else
> + pixm->field = V4L2_FIELD_NONE;
> +
> + memset(pixm->reserved, 0, sizeof(pixm->reserved));
> +
> + v4l2_fill_pixfmt_mp(pixm, finfo->format, pixm->width, pixm->height);
> +
> + for (i = 0; i < pixm->num_planes; i++) {
> + struct v4l2_plane_pix_format *plane_fmt;
> + int width, height, bpl, size, bpp = 0;
> +
> + if (!i) {
> + width = pixm->width;
> + height = pixm->height;
> + } else {
> + width = pixm->width / finfo->hdiv;
> + height = pixm->height / finfo->vdiv;
> + }
> +
> + switch (finfo->format) {
> + case V4L2_PIX_FMT_NV24:
> + case V4L2_PIX_FMT_NV16:
> + case V4L2_PIX_FMT_NV12:
> + case V4L2_PIX_FMT_BGR24:
> + bpp = finfo->bpp[i];
> + break;
> + default:
> + v4l2_dbg(1, debug, v4l2_dev,
> + "fourcc: %#x is not supported\n",
> + finfo->format);
> + break;
> + }
> +
> + bpl = ALIGN(width * bpp, MEMORY_ALIGN_ROUND_UP_BYTES);
> + size = bpl * height;
> + imagesize += size;
> +
> + if (finfo->mem_planes > i) {
> + /* Set bpl and size for each mplane */
> + plane_fmt = pixm->plane_fmt + i;
> + plane_fmt->bytesperline = bpl;
> + plane_fmt->sizeimage = size;
> + }
> +
> + v4l2_dbg(1, debug, v4l2_dev,
> + "C-Plane %i size: %d, Total imagesize: %d\n",
> + i, size, imagesize);
> + }
> +
> + /* Convert to non-MPLANE format as we want to unify non-MPLANE and MPLANE */
> + if (finfo->mem_planes == 1)
> + pixm->plane_fmt[0].sizeimage = imagesize;
> +
> + if (!try) {
> + stream->out_finfo = finfo;
> + stream->pixm = *pixm;
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: req(%d, %d), out(%d, %d), fmt:%#x\n", __func__,
> + pixm->width, pixm->height, stream->pixm.width,
> + stream->pixm.height, finfo->format);
> + }
> +}
> +
> +static int hdmirx_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
> + struct v4l2_fmtdesc *f)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> +
> + if (f->index >= 1)
> + return -EINVAL;
> +
> + f->pixelformat = hdmirx_dev->cur_fmt_fourcc;
> +
> + return 0;
> +}
> +
> +static int hdmirx_s_fmt_vid_cap_mplane(struct file *file,
> + void *priv, struct v4l2_format *f)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (vb2_is_busy(&stream->buf_queue)) {
> + v4l2_err(v4l2_dev, "%s: queue busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + hdmirx_set_fmt(stream, &f->fmt.pix_mp, false);
> +
> + return 0;
> +}
> +
> +static int hdmirx_g_fmt_vid_cap_mplane(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_pix_format_mplane pixm = {};
> +
> + pixm.pixelformat = hdmirx_dev->cur_fmt_fourcc;
> + hdmirx_set_fmt(stream, &pixm, true);
> + f->fmt.pix_mp = pixm;
> +
> + return 0;
> +}
> +
> +static int hdmirx_g_dv_timings(struct file *file, void *_fh,
> + struct v4l2_dv_timings *timings)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 dma_cfg1;
> +
> + *timings = hdmirx_dev->timings;
> + dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
> + v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
> + __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
> +
> + return 0;
> +}
> +
> +static int hdmirx_s_dv_timings(struct file *file, void *_fh,
> + struct v4l2_dv_timings *timings)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (!timings)
> + return -EINVAL;
> +
> + if (debug)
> + v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
> + "s_dv_timings: ", timings, false);
> +
> + if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: timings out of range\n", __func__);
> + return -ERANGE;
> + }
> +
> + /* Check if the timings are part of the CEA-861 timings. */
> + v4l2_find_dv_timings_cap(timings, &hdmirx_timings_cap, 0, NULL, NULL);
> +
> + if (v4l2_match_dv_timings(&hdmirx_dev->timings, timings, 0, false)) {
> + v4l2_dbg(1, debug, v4l2_dev, "%s: no change\n", __func__);
> + return 0;
> + }
> +
> + /*
> + * Changing the timings implies a format change, which is not allowed
> + * while buffers for use with streaming have already been allocated.
> + */
> + if (vb2_is_busy(&stream->buf_queue))
> + return -EBUSY;
> +
> + hdmirx_dev->timings = *timings;
> + /* Update the internal format */
> + hdmirx_set_fmt(stream, &stream->pixm, false);
> +
> + return 0;
> +}
> +
> +static int hdmirx_querycap(struct file *file, void *priv,
> + struct v4l2_capability *cap)
> +{
> + struct hdmirx_stream *stream = video_drvdata(file);
> + struct device *dev = stream->hdmirx_dev->dev;
> +
> + strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
> + strscpy(cap->card, dev->driver->name, sizeof(cap->card));
> +
> + return 0;
> +}
> +
> +static int hdmirx_queue_setup(struct vb2_queue *queue,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[],
> + struct device *alloc_ctxs[])
> +{
> + struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + const struct v4l2_pix_format_mplane *pixm = NULL;
> + const struct v4l2_format_info *out_finfo;
> + u32 i, height;
> +
> + pixm = &stream->pixm;
> + out_finfo = stream->out_finfo;
> +
> + if (!num_planes || !out_finfo) {
No need to check for !num_planes, that can never happen.
> + v4l2_err(v4l2_dev, "%s: out_fmt not set\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (*num_planes) {
> + if (*num_planes != pixm->num_planes)
> + return -EINVAL;
> +
> + for (i = 0; i < *num_planes; i++)
> + if (sizes[i] < pixm->plane_fmt[i].sizeimage)
> + return -EINVAL;
> + return 0;
> + }
> +
> + *num_planes = out_finfo->mem_planes;
> + height = pixm->height;
> +
> + for (i = 0; i < out_finfo->mem_planes; i++)
> + sizes[i] = pixm->plane_fmt[i].sizeimage;
> +
> + v4l2_dbg(1, debug, v4l2_dev, "%s: count %d, size %d\n",
> + v4l2_type_names[queue->type], *num_buffers, sizes[0]);
> +
> + return 0;
> +}
> +
> +/*
> + * The vb2_buffer are stored in hdmirx_buffer, in order to unify
> + * mplane buffer and none-mplane buffer.
> + */
> +static void hdmirx_buf_queue(struct vb2_buffer *vb)
> +{
> + const struct v4l2_format_info *out_finfo;
> + struct vb2_v4l2_buffer *vbuf;
> + struct hdmirx_buffer *hdmirx_buf;
> + struct vb2_queue *queue;
> + struct hdmirx_stream *stream;
> + const struct v4l2_pix_format_mplane *pixm;
> + unsigned long lock_flags = 0;
> + int i;
> +
> + vbuf = to_vb2_v4l2_buffer(vb);
> + hdmirx_buf = container_of(vbuf, struct hdmirx_buffer, vb);
> + queue = vb->vb2_queue;
> + stream = vb2_get_drv_priv(queue);
> + pixm = &stream->pixm;
> + out_finfo = stream->out_finfo;
> +
> + memset(hdmirx_buf->buff_addr, 0, sizeof(hdmirx_buf->buff_addr));
> +
> + /*
> + * If mplanes > 1, every c-plane has its own m-plane,
> + * otherwise, multiple c-planes are in the same m-plane
> + */
> + for (i = 0; i < out_finfo->mem_planes; i++)
> + hdmirx_buf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
> +
> + if (out_finfo->mem_planes == 1) {
> + if (out_finfo->comp_planes == 1) {
> + hdmirx_buf->buff_addr[HDMIRX_PLANE_CBCR] =
> + hdmirx_buf->buff_addr[HDMIRX_PLANE_Y];
> + } else {
> + for (i = 0; i < out_finfo->comp_planes - 1; i++)
> + hdmirx_buf->buff_addr[i + 1] =
> + hdmirx_buf->buff_addr[i] +
> + pixm->plane_fmt[i].bytesperline *
> + pixm->height;
> + }
> + }
> +
> + spin_lock_irqsave(&stream->vbq_lock, lock_flags);
> + list_add_tail(&hdmirx_buf->queue, &stream->buf_head);
> + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
> +}
> +
> +static void return_all_buffers(struct hdmirx_stream *stream,
> + enum vb2_buffer_state state)
> +{
> + struct hdmirx_buffer *buf;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&stream->vbq_lock, flags);
> + if (stream->curr_buf)
> + list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
> + if (stream->next_buf && stream->next_buf != stream->curr_buf)
> + list_add_tail(&stream->next_buf->queue, &stream->buf_head);
> + stream->curr_buf = NULL;
> + stream->next_buf = NULL;
> +
> + while (!list_empty(&stream->buf_head)) {
> + buf = list_first_entry(&stream->buf_head,
> + struct hdmirx_buffer, queue);
> + list_del(&buf->queue);
> + spin_unlock_irqrestore(&stream->vbq_lock, flags);
> + vb2_buffer_done(&buf->vb.vb2_buf, state);
> + spin_lock_irqsave(&stream->vbq_lock, flags);
> + }
> + spin_unlock_irqrestore(&stream->vbq_lock, flags);
> +}
> +
> +static void hdmirx_stop_streaming(struct vb2_queue *queue)
> +{
> + struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int ret;
> +
> + v4l2_info(v4l2_dev, "stream start stopping\n");
> + mutex_lock(&hdmirx_dev->stream_lock);
> + WRITE_ONCE(stream->stopping, true);
> +
> + /* wait last irq to return the buffer */
> + ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
> + msecs_to_jiffies(500));
> + if (!ret) {
> + v4l2_err(v4l2_dev, "%s: timeout waiting last irq\n",
> + __func__);
> + WRITE_ONCE(stream->stopping, false);
> + }
> +
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
> + return_all_buffers(stream, VB2_BUF_STATE_ERROR);
> + mutex_unlock(&hdmirx_dev->stream_lock);
> + v4l2_info(v4l2_dev, "stream stopping finished\n");
> +}
> +
> +static int hdmirx_start_streaming(struct vb2_queue *queue, unsigned int count)
> +{
> + struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_dv_timings timings = hdmirx_dev->timings;
> + struct v4l2_bt_timings *bt = &timings.bt;
> + unsigned long lock_flags = 0;
> + int line_flag;
> +
> + if (!hdmirx_dev->got_timing) {
> + v4l2_dbg(1, debug, v4l2_dev, "timing is invalid\n");
> + return 0;
> + }
> +
> + mutex_lock(&hdmirx_dev->stream_lock);
> + stream->frame_idx = 0;
> + stream->line_flag_int_cnt = 0;
> + stream->curr_buf = NULL;
> + stream->next_buf = NULL;
> + stream->irq_stat = 0;
> + queue->min_queued_buffers = 1;
> +
> + WRITE_ONCE(stream->stopping, false);
> +
> + spin_lock_irqsave(&stream->vbq_lock, lock_flags);
> + if (!stream->curr_buf) {
> + if (!list_empty(&stream->buf_head)) {
> + stream->curr_buf = list_first_entry(&stream->buf_head,
> + struct hdmirx_buffer,
> + queue);
> + list_del(&stream->curr_buf->queue);
> + } else {
> + stream->curr_buf = NULL;
> + }
> + }
> + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
> +
> + v4l2_dbg(2, debug, v4l2_dev,
> + "%s: start_stream cur_buf y_addr:%#x, uv_addr:%#x\n",
> + __func__, stream->curr_buf->buff_addr[HDMIRX_PLANE_Y],
> + stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
> + stream->curr_buf->buff_addr[HDMIRX_PLANE_Y]);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
> + stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
> +
> + if (bt->height) {
> + if (bt->interlaced == V4L2_DV_INTERLACED)
> + line_flag = bt->height / 4;
> + else
> + line_flag = bt->height / 2;
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
> + LINE_FLAG_NUM_MASK,
> + LINE_FLAG_NUM(line_flag));
> + } else {
> + v4l2_err(v4l2_dev, "height err: %d\n", bt->height);
> + }
> +
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, CED_DYN_CONTROL, 0x1);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, HDMIRX_DMA_EN);
> + v4l2_dbg(1, debug, v4l2_dev, "%s: enable dma", __func__);
> + mutex_unlock(&hdmirx_dev->stream_lock);
> +
> + return 0;
> +}
> +
> +/* vb2 queue */
> +static const struct vb2_ops hdmirx_vb2_ops = {
> + .queue_setup = hdmirx_queue_setup,
> + .buf_queue = hdmirx_buf_queue,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .stop_streaming = hdmirx_stop_streaming,
> + .start_streaming = hdmirx_start_streaming,
> +};
> +
> +static int hdmirx_init_vb2_queue(struct vb2_queue *q,
> + struct hdmirx_stream *stream,
> + enum v4l2_buf_type buf_type)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> +
> + q->type = buf_type;
> + q->io_modes = VB2_MMAP | VB2_DMABUF;
> + q->drv_priv = stream;
> + q->ops = &hdmirx_vb2_ops;
> + q->mem_ops = &vb2_dma_contig_memops;
> + q->buf_struct_size = sizeof(struct hdmirx_buffer);
> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> + q->lock = &stream->vlock;
> + q->dev = hdmirx_dev->dev;
> + /*
> + * rk3588 doesn't use iommu and works only with dma buffers
> + * that are physically contiguous in memory.
> + */
> + q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
Is it perhaps the case that the rk3588 does have an iommu, but that for
this hardware block that iommu is not actually used, so you need to force
it to contiguous? As I understand it, in the absence of an iommu the
vb2_dma_contig_memops should always produce physically contiguous memory.
Also, note that this is rk3588 specific: so another synopsys driver used
on another SoC might not have this problem.
This should perhaps depend on the 'rockchip,rk3588-hdmirx-ctrler' compatible.
> + return vb2_queue_init(q);
> +}
> +
> +/* video device */
> +static const struct v4l2_ioctl_ops hdmirx_v4l2_ioctl_ops = {
> + .vidioc_querycap = hdmirx_querycap,
> + .vidioc_try_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
> + .vidioc_s_fmt_vid_cap_mplane = hdmirx_s_fmt_vid_cap_mplane,
> + .vidioc_g_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
> + .vidioc_enum_fmt_vid_cap = hdmirx_enum_fmt_vid_cap_mplane,
> +
> + .vidioc_s_dv_timings = hdmirx_s_dv_timings,
> + .vidioc_g_dv_timings = hdmirx_g_dv_timings,
> + .vidioc_enum_dv_timings = hdmirx_enum_dv_timings,
> + .vidioc_query_dv_timings = hdmirx_query_dv_timings,
> + .vidioc_dv_timings_cap = hdmirx_dv_timings_cap,
> + .vidioc_enum_input = hdmirx_enum_input,
> + .vidioc_g_input = hdmirx_get_input,
> + .vidioc_s_input = hdmirx_set_input,
> + .vidioc_g_edid = hdmirx_get_edid,
> + .vidioc_s_edid = hdmirx_set_edid,
> + .vidioc_g_parm = hdmirx_g_parm,
> +
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .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 = hdmirx_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static const struct v4l2_file_operations hdmirx_fops = {
> + .owner = THIS_MODULE,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .unlocked_ioctl = video_ioctl2,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> +};
> +
> +static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct video_device *vdev = &stream->vdev;
> + int ret = 0;
> +
> + strscpy(vdev->name, "stream_hdmirx", sizeof(vdev->name));
> + INIT_LIST_HEAD(&stream->buf_head);
> + spin_lock_init(&stream->vbq_lock);
> + mutex_init(&stream->vlock);
> + init_waitqueue_head(&stream->wq_stopped);
> + stream->curr_buf = NULL;
> + stream->next_buf = NULL;
> +
> + vdev->ioctl_ops = &hdmirx_v4l2_ioctl_ops;
> + vdev->release = video_device_release_empty;
> + vdev->fops = &hdmirx_fops;
> + vdev->minor = -1;
> + vdev->v4l2_dev = v4l2_dev;
> + vdev->lock = &stream->vlock;
> + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> + V4L2_CAP_STREAMING;
> + video_set_drvdata(vdev, stream);
> + vdev->vfl_dir = VFL_DIR_RX;
> +
> + hdmirx_init_vb2_queue(&stream->buf_queue, stream,
> + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> + vdev->queue = &stream->buf_queue;
> +
> + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
> + if (ret < 0) {
> + v4l2_err(v4l2_dev, "video_register_device failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN, 0);
> + hdmirx_reset_dma(hdmirx_dev);
> + hdmirx_dev->got_timing = false;
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_res_change,
> + msecs_to_jiffies(50));
> +}
> +
> +static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (status & (CED_DYN_CNT_CH2_IRQ |
> + CED_DYN_CNT_CH1_IRQ |
> + CED_DYN_CNT_CH0_IRQ)) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: avp0_st:%#x\n",
> + __func__, status);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_FORCE, 0x0);
> +}
> +
> +static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + if (status & DEFRAMER_VSYNC_THR_REACHED_IRQ) {
> + v4l2_info(v4l2_dev, "Vertical Sync threshold reached interrupt %#x", status);
> + hdmirx_update_bits(hdmirx_dev, AVPUNIT_1_INT_MASK_N,
> + DEFRAMER_VSYNC_THR_REACHED_MASK_N, 0);
> + *handled = true;
> + }
> +}
> +
> +static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "mu0_st:%#x\n", status);
> + if (status & TIMER_BASE_LOCKED_IRQ) {
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
> + TIMER_BASE_LOCKED_IRQ, 0);
> + complete(&hdmirx_dev->timer_base_lock);
> + *handled = true;
> + }
> +
> + if (status & TMDSQPCLK_OFF_CHG) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_OFF_CHG\n", __func__);
> + *handled = true;
> + }
> +
> + if (status & TMDSQPCLK_LOCKED_CHG) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_LOCKED_CHG\n", __func__);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_FORCE, 0x0);
> +}
> +
> +static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "mu2_st:%#x\n", status);
> + if (status & PHYCREG_CR_WRITE_DONE) {
> + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
> + PHYCREG_CR_WRITE_DONE, 0);
> + complete(&hdmirx_dev->cr_write_done);
> + *handled = true;
> + }
> +
> + if (status & TMDSVALID_STABLE_CHG) {
> + process_signal_change(hdmirx_dev);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_FORCE, 0x0);
> +}
> +
> +static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: pk2_st:%#x\n", __func__, status);
> + if (status & PKTDEC_AVIIF_RCV_IRQ) {
> + hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
> + PKTDEC_AVIIF_RCV_IRQ, 0);
> + complete(&hdmirx_dev->avi_pkt_rcv);
> + v4l2_dbg(2, debug, v4l2_dev, "%s: AVIIF_RCV_IRQ\n", __func__);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
> +}
> +
> +static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + int status, bool *handled)
> +{
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: scdc_st:%#x\n", __func__, status);
> + if (status & SCDCTMDSCCFG_CHG) {
> + hdmirx_tmds_clk_ratio_config(hdmirx_dev);
> + *handled = true;
> + }
> +
> + hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
> +}
> +
> +static irqreturn_t hdmirx_hdmi_irq_handler(int irq, void *dev_id)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_id;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct arm_smccc_res res;
> + u32 mu0_st, mu2_st, pk2_st, scdc_st, avp1_st, avp0_st;
> + u32 mu0_mask, mu2_mask, pk2_mask, scdc_mask, avp1_msk, avp0_msk;
> + bool handled = false;
> +
> + mu0_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_MASK_N);
> + mu2_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_MASK_N);
> + pk2_mask = hdmirx_readl(hdmirx_dev, PKT_2_INT_MASK_N);
> + scdc_mask = hdmirx_readl(hdmirx_dev, SCDC_INT_MASK_N);
> + mu0_st = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_STATUS);
> + mu2_st = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_STATUS);
> + pk2_st = hdmirx_readl(hdmirx_dev, PKT_2_INT_STATUS);
> + scdc_st = hdmirx_readl(hdmirx_dev, SCDC_INT_STATUS);
> + avp0_st = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_STATUS);
> + avp1_st = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_STATUS);
> + avp0_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_MASK_N);
> + avp1_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_MASK_N);
> + mu0_st &= mu0_mask;
> + mu2_st &= mu2_mask;
> + pk2_st &= pk2_mask;
> + avp1_st &= avp1_msk;
> + avp0_st &= avp0_msk;
> + scdc_st &= scdc_mask;
> +
> + if (avp0_st)
> + avpunit_0_int_handler(hdmirx_dev, avp0_st, &handled);
> + if (avp1_st)
> + avpunit_1_int_handler(hdmirx_dev, avp1_st, &handled);
> + if (mu0_st)
> + mainunit_0_int_handler(hdmirx_dev, mu0_st, &handled);
> + if (mu2_st)
> + mainunit_2_int_handler(hdmirx_dev, mu2_st, &handled);
> + if (pk2_st)
> + pkt_2_int_handler(hdmirx_dev, pk2_st, &handled);
> + if (scdc_st)
> + scdc_int_handler(hdmirx_dev, scdc_st, &handled);
> +
> + if (!handled) {
> + v4l2_dbg(2, debug, v4l2_dev, "%s: hdmi irq not handled", __func__);
> + v4l2_dbg(2, debug, v4l2_dev,
> + "avp0:%#x, avp1:%#x, mu0:%#x, mu2:%#x, pk2:%#x, scdc:%#x\n",
> + avp0_st, avp1_st, mu0_st, mu2_st, pk2_st, scdc_st);
> + }
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: en_fiq", __func__);
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + return handled ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static void hdmirx_vb_done(struct hdmirx_stream *stream,
> + struct vb2_v4l2_buffer *vb_done)
> +{
> + const struct v4l2_format_info *finfo = stream->out_finfo;
> + u32 i;
> +
> + /* Dequeue a filled buffer */
> + for (i = 0; i < finfo->mem_planes; i++) {
> + vb2_set_plane_payload(&vb_done->vb2_buf, i,
> + stream->pixm.plane_fmt[i].sizeimage);
> + }
> +
> + vb_done->vb2_buf.timestamp = ktime_get_ns();
> + vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + bool *handled)
> +{
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_dv_timings timings = hdmirx_dev->timings;
> + struct v4l2_bt_timings *bt = &timings.bt;
> + struct vb2_v4l2_buffer *vb_done = NULL;
> +
> + if (!(stream->irq_stat) && !(stream->irq_stat & LINE_FLAG_INT_EN))
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: last time have no line_flag_irq\n", __func__);
> +
> + if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
> + goto DMA_IDLE_OUT;
> +
> + if (bt->interlaced != V4L2_DV_INTERLACED ||
> + !(stream->line_flag_int_cnt % 2)) {
> + if (stream->next_buf) {
> + if (stream->curr_buf)
> + vb_done = &stream->curr_buf->vb;
> +
> + if (vb_done) {
> + vb_done->vb2_buf.timestamp = ktime_get_ns();
> + vb_done->sequence = stream->frame_idx;
> + hdmirx_vb_done(stream, vb_done);
> + stream->frame_idx++;
> + if (stream->frame_idx == 30)
> + v4l2_info(v4l2_dev, "rcv frames\n");
> + }
> +
> + stream->curr_buf = NULL;
> + if (stream->next_buf) {
> + stream->curr_buf = stream->next_buf;
> + stream->next_buf = NULL;
> + }
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev,
> + "%s: next_buf NULL, skip vb_done\n", __func__);
> + }
> + }
> +
> +DMA_IDLE_OUT:
> + *handled = true;
> +}
> +
> +static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
> + bool *handled)
> +{
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + struct v4l2_dv_timings timings = hdmirx_dev->timings;
> + struct v4l2_bt_timings *bt = &timings.bt;
> + u32 dma_cfg6;
> +
> + stream->line_flag_int_cnt++;
> + if (!(stream->irq_stat) && !(stream->irq_stat & HDMIRX_DMA_IDLE_INT))
> + v4l2_dbg(1, debug, v4l2_dev,
> + "%s: last have no dma_idle_irq\n", __func__);
> + dma_cfg6 = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
> + if (!(dma_cfg6 & HDMIRX_DMA_EN)) {
> + v4l2_dbg(2, debug, v4l2_dev, "%s: dma not on\n", __func__);
> + goto LINE_FLAG_OUT;
> + }
> +
> + if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
> + goto LINE_FLAG_OUT;
> +
> + if (bt->interlaced != V4L2_DV_INTERLACED ||
> + !(stream->line_flag_int_cnt % 2)) {
> + if (!stream->next_buf) {
> + spin_lock(&stream->vbq_lock);
> + if (!list_empty(&stream->buf_head)) {
> + stream->next_buf = list_first_entry(&stream->buf_head,
> + struct hdmirx_buffer,
> + queue);
> + list_del(&stream->next_buf->queue);
> + } else {
> + stream->next_buf = NULL;
> + }
> + spin_unlock(&stream->vbq_lock);
> +
> + if (stream->next_buf) {
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
> + stream->next_buf->buff_addr[HDMIRX_PLANE_Y]);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
> + stream->next_buf->buff_addr[HDMIRX_PLANE_CBCR]);
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev,
> + "%s: no buffer is available\n", __func__);
> + }
> + }
> + } else {
> + v4l2_dbg(3, debug, v4l2_dev, "%s: interlace:%d, line_flag_int_cnt:%d\n",
> + __func__, bt->interlaced, stream->line_flag_int_cnt);
> + }
> +
> +LINE_FLAG_OUT:
> + *handled = true;
> +}
> +
> +static irqreturn_t hdmirx_dma_irq_handler(int irq, void *dev_id)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_id;
> + struct hdmirx_stream *stream = &hdmirx_dev->stream;
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + u32 dma_stat1, dma_stat13;
> + bool handled = false;
> +
> + dma_stat1 = hdmirx_readl(hdmirx_dev, DMA_STATUS1);
> + dma_stat13 = hdmirx_readl(hdmirx_dev, DMA_STATUS13);
> + v4l2_dbg(3, debug, v4l2_dev, "dma_irq st1:%#x, st13:%d\n",
> + dma_stat1, dma_stat13);
> +
> + if (READ_ONCE(stream->stopping)) {
> + v4l2_dbg(1, debug, v4l2_dev, "%s: stop stream\n", __func__);
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
> + hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
> + LINE_FLAG_INT_EN |
> + HDMIRX_DMA_IDLE_INT |
> + HDMIRX_LOCK_DISABLE_INT |
> + LAST_FRAME_AXI_UNFINISH_INT_EN |
> + FIFO_OVERFLOW_INT_EN |
> + FIFO_UNDERFLOW_INT_EN |
> + HDMIRX_AXI_ERROR_INT_EN, 0);
> + WRITE_ONCE(stream->stopping, false);
> + wake_up(&stream->wq_stopped);
> + return IRQ_HANDLED;
> + }
> +
> + if (dma_stat1 & HDMIRX_DMA_IDLE_INT)
> + dma_idle_int_handler(hdmirx_dev, &handled);
> +
> + if (dma_stat1 & LINE_FLAG_INT_EN)
> + line_flag_int_handler(hdmirx_dev, &handled);
> +
> + if (!handled)
> + v4l2_dbg(3, debug, v4l2_dev,
> + "%s: dma irq not handled, dma_stat1:%#x\n",
> + __func__, dma_stat1);
> +
> + stream->irq_stat = dma_stat1;
> + hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void hdmirx_plugin(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct arm_smccc_res res;
> + int ret;
> +
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_heartbeat,
> + msecs_to_jiffies(10));
> + arm_smccc_smc(SIP_WDT_CFG, WDT_START, 0, 0, 0, 0, 0, 0, &res);
> + hdmirx_submodule_init(hdmirx_dev);
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
> + POWERPROVIDED);
> + hdmirx_hpd_ctrl(hdmirx_dev, true);
> + hdmirx_phy_config(hdmirx_dev);
> + ret = hdmirx_wait_lock_and_get_timing(hdmirx_dev);
> + if (ret) {
> + hdmirx_plugout(hdmirx_dev);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(200));
> + return;
> + }
> + hdmirx_dma_config(hdmirx_dev);
> + hdmirx_interrupts_setup(hdmirx_dev, true);
> +}
> +
> +static void hdmirx_delayed_work_hotplug(struct work_struct *work)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + bool plugin;
> +
> + hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
> + delayed_work_hotplug.work);
> +
> + mutex_lock(&hdmirx_dev->work_lock);
> + hdmirx_dev->got_timing = false;
> + plugin = tx_5v_power_present(hdmirx_dev);
> + v4l2_ctrl_s_ctrl(hdmirx_dev->detect_tx_5v_ctrl, plugin);
> + v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
> + __func__, plugin);
> +
> + if (plugin)
> + hdmirx_plugin(hdmirx_dev);
> + else
> + hdmirx_plugout(hdmirx_dev);
> +
> + mutex_unlock(&hdmirx_dev->work_lock);
> +}
> +
> +static void hdmirx_delayed_work_res_change(struct work_struct *work)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + bool plugin;
> +
> + hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
> + delayed_work_res_change.work);
> +
> + mutex_lock(&hdmirx_dev->work_lock);
> + plugin = tx_5v_power_present(hdmirx_dev);
> + v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
> + __func__, plugin);
> + if (plugin) {
> + hdmirx_interrupts_setup(hdmirx_dev, false);
> + hdmirx_submodule_init(hdmirx_dev);
> + hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
> + POWERPROVIDED);
> + hdmirx_hpd_ctrl(hdmirx_dev, true);
> + hdmirx_phy_config(hdmirx_dev);
> +
> + if (hdmirx_wait_lock_and_get_timing(hdmirx_dev)) {
> + hdmirx_plugout(hdmirx_dev);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(200));
> + } else {
> + hdmirx_dma_config(hdmirx_dev);
> + hdmirx_interrupts_setup(hdmirx_dev, true);
> + }
> + }
> + mutex_unlock(&hdmirx_dev->work_lock);
> +}
> +
> +static void hdmirx_delayed_work_heartbeat(struct work_struct *work)
> +{
> + struct delayed_work *dwork = to_delayed_work(work);
> + struct snps_hdmirx_dev *hdmirx_dev = container_of(dwork,
> + struct snps_hdmirx_dev,
> + delayed_work_heartbeat);
> +
> + queue_work(system_highpri_wq, &hdmirx_dev->work_wdt_config);
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_heartbeat, HZ);
> +}
> +
> +static void hdmirx_work_wdt_config(struct work_struct *work)
> +{
> + struct arm_smccc_res res;
> + struct snps_hdmirx_dev *hdmirx_dev = container_of(work,
> + struct snps_hdmirx_dev,
> + work_wdt_config);
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + arm_smccc_smc(SIP_WDT_CFG, WDT_PING, 0, 0, 0, 0, 0, 0, &res);
> + v4l2_dbg(3, debug, v4l2_dev, "hb\n");
> +}
> +
> +static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_id;
> + u32 val;
> +
> + val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
> + v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val);
> +
> + queue_delayed_work(system_unbound_wq,
> + &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(10));
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct hdmirx_cec_ops hdmirx_cec_ops = {
> + .write = hdmirx_writel,
> + .read = hdmirx_readl,
> +};
> +
> +static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + int ret;
> +
> + hdmirx_dev->num_clks = devm_clk_bulk_get_all(dev, &hdmirx_dev->clks);
> + if (hdmirx_dev->num_clks < 1)
> + return -ENODEV;
> +
> + hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
> + hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
> + hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
> + hdmirx_dev->resets[HDMIRX_RST_BIU].id = "biu";
> +
> + ret = devm_reset_control_bulk_get_exclusive(dev, HDMIRX_NUM_RST,
> + hdmirx_dev->resets);
> + if (ret < 0) {
> + dev_err(dev, "failed to get reset controls\n");
> + return ret;
> + }
> +
> + hdmirx_dev->detect_5v_gpio =
> + devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
> +
> + if (IS_ERR(hdmirx_dev->detect_5v_gpio)) {
> + dev_err(dev, "failed to get hdmirx hot plug detection gpio\n");
> + return PTR_ERR(hdmirx_dev->detect_5v_gpio);
> + }
> +
> + hdmirx_dev->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(hdmirx_dev->grf)) {
> + dev_err(dev, "failed to get rockchip,grf\n");
> + return PTR_ERR(hdmirx_dev->grf);
> + }
> +
> + hdmirx_dev->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,vo1-grf");
> + if (IS_ERR(hdmirx_dev->vo1_grf)) {
> + dev_err(dev, "failed to get rockchip,vo1-grf\n");
> + return PTR_ERR(hdmirx_dev->vo1_grf);
> + }
> +
> + hdmirx_dev->hpd_trigger_level = !device_property_read_bool(dev, "hpd-is-active-low");
> +
> + ret = of_reserved_mem_device_init(dev);
> + if (ret)
> + dev_warn(dev, "No reserved memory for HDMIRX, use default CMA\n");
> +
> + return 0;
> +}
> +
> +static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_1_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, AVPUNIT_1_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, PKT_0_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, PKT_1_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, PKT_2_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, SCDC_INT_MASK_N, 0);
> + hdmirx_writel(hdmirx_dev, CEC_INT_MASK_N, 0);
> +
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_0_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, HDCP_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, HDCP_1_INT_CLEAR, 0xffffffff);
> + hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff);
> +}
> +
> +static int hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0);
> +
> + regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2,
> + (HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) |
> + ((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16));
> + /*
> + * Some interrupts are enabled by default, so we disable
> + * all interrupts and clear interrupts status first.
> + */
> + hdmirx_disable_all_interrupts(hdmirx_dev);
> +
> + return 0;
> +}
> +
> +static void hdmirx_load_default_edid(struct snps_hdmirx_dev *hdmirx_dev)
> +{
> + int ret;
> + struct v4l2_edid def_edid;
> +
> + hdmirx_hpd_ctrl(hdmirx_dev, false);
> +
> + /* disable hpd and write edid */
> + def_edid.pad = 0;
> + def_edid.start_block = 0;
> + def_edid.blocks = EDID_NUM_BLOCKS_MAX;
> +
> + if (IS_ENABLED(CONFIG_HDMIRX_LOAD_DEFAULT_EDID))
> + def_edid.edid = edid_init_data_340M;
> + else
> + def_edid.edid = hdmirx_dev->edid;
> +
> + ret = hdmirx_write_edid(hdmirx_dev, &def_edid, true);
> + if (ret)
> + dev_err(hdmirx_dev->dev, "%s: write edid failed\n", __func__);
> +}
> +
> +static void hdmirx_disable_irq(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct arm_smccc_res res;
> +
> + disable_irq(hdmirx_dev->hdmi_irq);
> + disable_irq(hdmirx_dev->dma_irq);
> + disable_irq(hdmirx_dev->det_irq);
> +
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_DIS,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_hotplug);
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_res_change);
> + cancel_delayed_work_sync(&hdmirx_dev->delayed_work_heartbeat);
> + flush_work(&hdmirx_dev->work_wdt_config);
> +
> + arm_smccc_smc(SIP_WDT_CFG, WDT_STOP, 0, 0, 0, 0, 0, 0, &res);
> +}
> +
> +static int hdmirx_disable(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> +
> + clk_bulk_disable_unprepare(hdmirx_dev->num_clks, hdmirx_dev->clks);
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: suspend\n", __func__);
> +
> + return pinctrl_pm_select_sleep_state(dev);
> +}
> +
> +static void hdmirx_enable_irq(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct arm_smccc_res res;
> +
> + enable_irq(hdmirx_dev->hdmi_irq);
> + enable_irq(hdmirx_dev->dma_irq);
> + enable_irq(hdmirx_dev->det_irq);
> +
> + arm_smccc_smc(RK_SIP_FIQ_CTRL, RK_SIP_FIQ_CTRL_FIQ_EN,
> + RK_IRQ_HDMIRX_HDMI, 0, 0, 0, 0, 0, &res);
> +
> + queue_delayed_work(system_unbound_wq, &hdmirx_dev->delayed_work_hotplug,
> + msecs_to_jiffies(20));
> +}
> +
> +static int hdmirx_enable(struct device *dev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> + struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
> + int ret;
> +
> + v4l2_dbg(2, debug, v4l2_dev, "%s: resume\n", __func__);
> + ret = pinctrl_pm_select_default_state(dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(hdmirx_dev->num_clks, hdmirx_dev->clks);
> + if (ret) {
> + dev_err(dev, "failed to enable hdmirx bulk clks: %d\n", ret);
> + return ret;
> + }
> +
> + reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> + usleep_range(150, 160);
> + reset_control_bulk_deassert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> + usleep_range(150, 160);
> +
> + return 0;
> +}
> +
> +static int hdmirx_suspend(struct device *dev)
> +{
> + hdmirx_disable_irq(dev);
> +
> + return hdmirx_disable(dev);
> +}
> +
> +static int hdmirx_resume(struct device *dev)
> +{
> + int ret = hdmirx_enable(dev);
> +
> + if (ret)
> + return ret;
> +
> + hdmirx_enable_irq(dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops snps_hdmirx_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(hdmirx_suspend, hdmirx_resume)
> +};
> +
> +static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + int ret, irq;
> +
> + irq = platform_get_irq_byname(pdev, "hdmi");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get hdmi irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->hdmi_irq = irq;
> + ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
> + "rk_hdmirx-hdmi", hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request hdmi irq\n");
> + return ret;
> + }
> +
> + irq = platform_get_irq_byname(pdev, "dma");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get dma irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->dma_irq = irq;
> + ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
> + IRQF_ONESHOT, "rk_hdmirx-dma",
> + hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request dma irq\n");
> + return ret;
> + }
> +
> + irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
> + return irq;
> + }
> +
> + irq_set_status_flags(irq, IRQ_NOAUTOEN);
> +
> + hdmirx_dev->det_irq = irq;
> + ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
> + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
> + "rk_hdmirx-5v", hdmirx_dev);
> + if (ret) {
> + dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = hdmirx_dev->dev;
> + struct hdmirx_cec_data cec_data;
> + int irq;
> +
> + irq = platform_get_irq_byname(pdev, "cec");
> + if (irq < 0) {
> + dev_err_probe(dev, irq, "failed to get cec irq\n");
> + return irq;
> + }
> +
> + hdmirx_dev->cec_notifier = cec_notifier_conn_register(dev, NULL, NULL);
> + if (!hdmirx_dev->cec_notifier)
> + return -EINVAL;
> +
> + cec_data.hdmirx = hdmirx_dev;
> + cec_data.dev = hdmirx_dev->dev;
> + cec_data.ops = &hdmirx_cec_ops;
> + cec_data.irq = irq;
> +
> + hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
> + if (!hdmirx_dev->cec) {
> + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int hdmirx_probe(struct platform_device *pdev)
> +{
> + struct snps_hdmirx_dev *hdmirx_dev;
> + struct device *dev = &pdev->dev;
> + struct v4l2_ctrl_handler *hdl;
> + struct hdmirx_stream *stream;
> + struct v4l2_device *v4l2_dev;
> + int ret;
> +
> + hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
> + if (!hdmirx_dev)
> + return -ENOMEM;
> +
> + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->dev = dev;
> + dev_set_drvdata(dev, hdmirx_dev);
> +
> + ret = hdmirx_parse_dt(hdmirx_dev);
> + if (ret)
> + return ret;
> +
> + ret = hdmirx_setup_irq(hdmirx_dev, pdev);
> + if (ret)
> + return ret;
> +
> + hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(hdmirx_dev->regs))
> + return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
> + "failed to remap regs resource\n");
> +
> + mutex_init(&hdmirx_dev->stream_lock);
> + mutex_init(&hdmirx_dev->work_lock);
> + spin_lock_init(&hdmirx_dev->rst_lock);
> +
> + init_completion(&hdmirx_dev->cr_write_done);
> + init_completion(&hdmirx_dev->timer_base_lock);
> + init_completion(&hdmirx_dev->avi_pkt_rcv);
> +
> + INIT_WORK(&hdmirx_dev->work_wdt_config, hdmirx_work_wdt_config);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
> + hdmirx_delayed_work_hotplug);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
> + hdmirx_delayed_work_res_change);
> + INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_heartbeat,
> + hdmirx_delayed_work_heartbeat);
> +
> + hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
> + hdmirx_dev->timings = cea640x480;
> +
> + hdmirx_enable(dev);
> + hdmirx_init(hdmirx_dev);
> +
> + v4l2_dev = &hdmirx_dev->v4l2_dev;
> + strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
> +
> + hdl = &hdmirx_dev->hdl;
> + v4l2_ctrl_handler_init(hdl, 1);
> +
> + hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
> + V4L2_CID_DV_RX_POWER_PRESENT,
> + 0, 1, 0, 0);
> +
> + hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, 0,
> + V4L2_CID_DV_RX_RGB_RANGE,
> + V4L2_DV_RGB_RANGE_FULL, 0,
> + V4L2_DV_RGB_RANGE_AUTO);
> +
> + hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> + if (hdl->error) {
> + dev_err(dev, "v4l2 ctrl handler init failed\n");
> + ret = hdl->error;
> + goto err_pm;
> + }
> + hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
> +
> + ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
> + if (ret < 0) {
> + dev_err(dev, "register v4l2 device failed\n");
> + goto err_hdl;
> + }
> +
> + stream = &hdmirx_dev->stream;
> + stream->hdmirx_dev = hdmirx_dev;
> + ret = hdmirx_register_stream_vdev(stream);
> + if (ret < 0) {
> + dev_err(dev, "register video device failed\n");
> + goto err_unreg_v4l2_dev;
> + }
> +
> + ret = hdmirx_register_cec(hdmirx_dev, pdev);
> + if (ret)
> + goto err_unreg_video_dev;
> +
> + hdmirx_load_default_edid(hdmirx_dev);
> +
> + hdmirx_enable_irq(dev);
> +
> + return 0;
> +
> +err_unreg_video_dev:
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> +err_unreg_v4l2_dev:
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +err_hdl:
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> +err_pm:
> + hdmirx_disable(dev);
> +
> + return ret;
> +}
> +
> +static void hdmirx_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
> +
> + snps_hdmirx_cec_unregister(hdmirx_dev->cec);
> + cec_notifier_conn_unregister(hdmirx_dev->cec_notifier);
> +
> + hdmirx_disable_irq(dev);
> +
> + video_unregister_device(&hdmirx_dev->stream.vdev);
> + v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
> + v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
> +
> + hdmirx_disable(dev);
> +
> + reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
> +
> + of_reserved_mem_device_release(dev);
> +}
> +
> +static const struct of_device_id hdmirx_id[] = {
> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, hdmirx_id);
> +
> +static struct platform_driver hdmirx_driver = {
> + .probe = hdmirx_probe,
> + .remove = hdmirx_remove,
> + .driver = {
> + .name = "snps_hdmirx",
> + .of_match_table = hdmirx_id,
> + .pm = &snps_hdmirx_pm_ops,
> + }
> +};
> +module_platform_driver(hdmirx_driver);
> +
> +MODULE_DESCRIPTION("Rockchip HDMI Receiver Driver");
> +MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
> +MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> new file mode 100644
> index 000000000000..220ab99ca611
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> @@ -0,0 +1,394 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + *
> + * Author: Dingxian Wen <shawn.wen@rock-chips.com>
> + */
> +
> +#ifndef DW_HDMIRX_H
> +#define DW_HDMIRX_H
> +
> +#include <linux/bitops.h>
> +
> +#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
> +#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
> +
> +/* SYS_GRF */
> +#define SYS_GRF_SOC_CON1 0x0304
> +#define HDMIRXPHY_SRAM_EXT_LD_DONE BIT(1)
> +#define HDMIRXPHY_SRAM_BYPASS BIT(0)
> +#define SYS_GRF_SOC_STATUS1 0x0384
> +#define HDMIRXPHY_SRAM_INIT_DONE BIT(10)
> +#define SYS_GRF_CHIP_ID 0x0600
> +
> +/* VO1_GRF */
> +#define VO1_GRF_VO1_CON2 0x0008
> +#define HDMIRX_SDAIN_MSK BIT(2)
> +#define HDMIRX_SCLIN_MSK BIT(1)
> +
> +/* HDMIRX PHY */
> +#define SUP_DIG_ANA_CREGS_SUP_ANA_NC 0x004f
> +
> +#define LANE0_DIG_ASIC_RX_OVRD_OUT_0 0x100f
> +#define LANE1_DIG_ASIC_RX_OVRD_OUT_0 0x110f
> +#define LANE2_DIG_ASIC_RX_OVRD_OUT_0 0x120f
> +#define LANE3_DIG_ASIC_RX_OVRD_OUT_0 0x130f
> +#define ASIC_ACK_OVRD_EN BIT(1)
> +#define ASIC_ACK BIT(0)
> +
> +#define LANE0_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x104a
> +#define LANE1_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x114a
> +#define LANE2_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x124a
> +#define LANE3_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x134a
> +#define FREQ_TUNE_START_VAL_MASK GENMASK(9, 0)
> +#define FREQ_TUNE_START_VAL(x) UPDATE(x, 9, 0)
> +
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG 0x20c4
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_ADAPT_REF_FOM 0x20c7
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG 0x20e9
> +#define CDR_SETTING_BOUNDARY_3_DEFAULT 0x52da
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG 0x20ea
> +#define CDR_SETTING_BOUNDARY_4_DEFAULT 0x43cd
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG 0x20eb
> +#define CDR_SETTING_BOUNDARY_5_DEFAULT 0x35b3
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG 0x20fb
> +#define CDR_SETTING_BOUNDARY_6_DEFAULT 0x2799
> +#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG 0x20fc
> +#define CDR_SETTING_BOUNDARY_7_DEFAULT 0x1b65
> +
> +#define RAWLANE0_DIG_PCS_XF_RX_OVRD_OUT 0x300e
> +#define RAWLANE1_DIG_PCS_XF_RX_OVRD_OUT 0x310e
> +#define RAWLANE2_DIG_PCS_XF_RX_OVRD_OUT 0x320e
> +#define RAWLANE3_DIG_PCS_XF_RX_OVRD_OUT 0x330e
> +#define PCS_ACK_WRITE_SELECT BIT(14)
> +#define PCS_EN_CTL BIT(1)
> +#define PCS_ACK BIT(0)
> +
> +#define RAWLANE0_DIG_AON_FAST_FLAGS 0x305c
> +#define RAWLANE1_DIG_AON_FAST_FLAGS 0x315c
> +#define RAWLANE2_DIG_AON_FAST_FLAGS 0x325c
> +#define RAWLANE3_DIG_AON_FAST_FLAGS 0x335c
> +
> +/* HDMIRX Ctrler */
> +#define GLOBAL_SWRESET_REQUEST 0x0020
> +#define DATAPATH_SWRESETREQ BIT(12)
> +#define GLOBAL_SWENABLE 0x0024
> +#define PHYCTRL_ENABLE BIT(21)
> +#define CEC_ENABLE BIT(16)
> +#define TMDS_ENABLE BIT(13)
> +#define DATAPATH_ENABLE BIT(12)
> +#define PKTFIFO_ENABLE BIT(11)
> +#define AVPUNIT_ENABLE BIT(8)
> +#define MAIN_ENABLE BIT(0)
> +#define GLOBAL_TIMER_REF_BASE 0x0028
> +#define CORE_CONFIG 0x0050
> +#define CMU_CONFIG0 0x0060
> +#define TMDSQPCLK_STABLE_FREQ_MARGIN_MASK GENMASK(30, 16)
> +#define TMDSQPCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 30, 16)
> +#define AUDCLK_STABLE_FREQ_MARGIN_MASK GENMASK(11, 9)
> +#define AUDCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 11, 9)
> +#define CMU_STATUS 0x007c
> +#define TMDSQPCLK_LOCKED_ST BIT(4)
> +#define CMU_TMDSQPCLK_FREQ 0x0084
> +#define PHY_CONFIG 0x00c0
> +#define LDO_AFE_PROG_MASK GENMASK(24, 23)
> +#define LDO_AFE_PROG(x) UPDATE(x, 24, 23)
> +#define LDO_PWRDN BIT(21)
> +#define TMDS_CLOCK_RATIO BIT(16)
> +#define RXDATA_WIDTH BIT(15)
> +#define REFFREQ_SEL_MASK GENMASK(11, 9)
> +#define REFFREQ_SEL(x) UPDATE(x, 11, 9)
> +#define HDMI_DISABLE BIT(8)
> +#define PHY_PDDQ BIT(1)
> +#define PHY_RESET BIT(0)
> +#define PHY_STATUS 0x00c8
> +#define HDMI_DISABLE_ACK BIT(1)
> +#define PDDQ_ACK BIT(0)
> +#define PHYCREG_CONFIG0 0x00e0
> +#define PHYCREG_CR_PARA_SELECTION_MODE_MASK GENMASK(1, 0)
> +#define PHYCREG_CR_PARA_SELECTION_MODE(x) UPDATE(x, 1, 0)
> +#define PHYCREG_CONFIG1 0x00e4
> +#define PHYCREG_CONFIG2 0x00e8
> +#define PHYCREG_CONFIG3 0x00ec
> +#define PHYCREG_CONTROL 0x00f0
> +#define PHYCREG_CR_PARA_WRITE_P BIT(1)
> +#define PHYCREG_CR_PARA_READ_P BIT(0)
> +#define PHYCREG_STATUS 0x00f4
> +
> +#define MAINUNIT_STATUS 0x0150
> +#define TMDSVALID_STABLE_ST BIT(1)
> +#define DESCRAND_EN_CONTROL 0x0210
> +#define SCRAMB_EN_SEL_QST_MASK GENMASK(1, 0)
> +#define SCRAMB_EN_SEL_QST(x) UPDATE(x, 1, 0)
> +#define DESCRAND_SYNC_CONTROL 0x0214
> +#define RECOVER_UNSYNC_STREAM_QST BIT(0)
> +#define DESCRAND_SYNC_SEQ_CONFIG 0x022c
> +#define DESCRAND_SYNC_SEQ_ERR_CNT_EN BIT(0)
> +#define DESCRAND_SYNC_SEQ_STATUS 0x0234
> +#define DEFRAMER_CONFIG0 0x0270
> +#define VS_CNT_THR_QST_MASK GENMASK(27, 20)
> +#define VS_CNT_THR_QST(x) UPDATE(x, 27, 20)
> +#define HS_POL_QST_MASK GENMASK(19, 18)
> +#define HS_POL_QST(x) UPDATE(x, 19, 18)
> +#define VS_POL_QST_MASK GENMASK(17, 16)
> +#define VS_POL_QST(x) UPDATE(x, 17, 16)
> +#define VS_REMAPFILTER_EN_QST BIT(8)
> +#define VS_FILTER_ORDER_QST_MASK GENMASK(1, 0)
> +#define VS_FILTER_ORDER_QST(x) UPDATE(x, 1, 0)
> +#define DEFRAMER_VSYNC_CNT_CLEAR 0x0278
> +#define VSYNC_CNT_CLR_P BIT(0)
> +#define DEFRAMER_STATUS 0x027c
> +#define OPMODE_STS_MASK GENMASK(6, 4)
> +#define I2C_SLAVE_CONFIG1 0x0164
> +#define I2C_SDA_OUT_HOLD_VALUE_QST_MASK GENMASK(15, 8)
> +#define I2C_SDA_OUT_HOLD_VALUE_QST(x) UPDATE(x, 15, 8)
> +#define I2C_SDA_IN_HOLD_VALUE_QST_MASK GENMASK(7, 0)
> +#define I2C_SDA_IN_HOLD_VALUE_QST(x) UPDATE(x, 7, 0)
> +#define OPMODE_STS_MASK GENMASK(6, 4)
> +#define REPEATER_QST BIT(28)
> +#define FASTREAUTH_QST BIT(27)
> +#define FEATURES_1DOT1_QST BIT(26)
> +#define FASTI2C_QST BIT(25)
> +#define EESS_CTL_THR_QST_MASK GENMASK(19, 16)
> +#define EESS_CTL_THR_QST(x) UPDATE(x, 19, 16)
> +#define OESS_CTL3_THR_QST_MASK GENMASK(11, 8)
> +#define OESS_CTL3_THR_QST(x) UPDATE(x, 11, 8)
> +#define EESS_OESS_SEL_QST_MASK GENMASK(5, 4)
> +#define EESS_OESS_SEL_QST(x) UPDATE(x, 5, 4)
> +#define KEY_DECRYPT_EN_QST BIT(0)
> +#define KEY_DECRYPT_SEED_QST_MASK GENMASK(15, 0)
> +#define KEY_DECRYPT_SEED_QST(x) UPDATE(x, 15, 0)
> +#define HDCP_INT_CLEAR 0x50d8
> +#define HDCP_1_INT_CLEAR 0x50e8
> +#define HDCP2_CONFIG 0x02f0
> +#define HDCP2_SWITCH_OVR_VALUE BIT(2)
> +#define HDCP2_SWITCH_OVR_EN BIT(1)
> +
> +#define VIDEO_CONFIG2 0x042c
> +#define VPROC_VSYNC_POL_OVR_VALUE BIT(19)
> +#define VPROC_VSYNC_POL_OVR_EN BIT(18)
> +#define VPROC_HSYNC_POL_OVR_VALUE BIT(17)
> +#define VPROC_HSYNC_POL_OVR_EN BIT(16)
> +#define VPROC_FMT_OVR_VALUE_MASK GENMASK(6, 4)
> +#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
> +#define VPROC_FMT_OVR_EN BIT(0)
> +
> +#define AFIFO_FILL_RESTART BIT(0)
> +#define AFIFO_INIT_P BIT(0)
> +#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
> +#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
> +#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
> +#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
> +#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
> +#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
> +#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
> +#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
> +
> +#define AFIFO_UNDERFLOW_ST BIT(25)
> +#define AFIFO_OVERFLOW_ST BIT(24)
> +
> +#define SPEAKER_ALLOC_OVR_EN BIT(16)
> +#define I2S_BPCUV_EN BIT(4)
> +#define SPDIF_EN BIT(2)
> +#define I2S_EN BIT(1)
> +#define AFIFO_THR_PASS_DEMUTEMASK_N BIT(24)
> +#define AVMUTE_DEMUTEMASK_N BIT(16)
> +#define AFIFO_THR_MUTE_LOW_MUTEMASK_N BIT(9)
> +#define AFIFO_THR_MUTE_HIGH_MUTEMASK_N BIT(8)
> +#define AVMUTE_MUTEMASK_N BIT(0)
> +#define SCDC_CONFIG 0x0580
> +#define HPDLOW BIT(1)
> +#define POWERPROVIDED BIT(0)
> +#define SCDC_REGBANK_STATUS1 0x058c
> +#define SCDC_TMDSBITCLKRATIO BIT(1)
> +#define SCDC_REGBANK_STATUS3 0x0594
> +#define SCDC_REGBANK_CONFIG0 0x05c0
> +#define SCDC_SINKVERSION_QST_MASK GENMASK(7, 0)
> +#define SCDC_SINKVERSION_QST(x) UPDATE(x, 7, 0)
> +#define AGEN_LAYOUT BIT(4)
> +#define AGEN_SPEAKER_ALLOC GENMASK(15, 8)
> +
> +#define CED_CONFIG 0x0760
> +#define CED_VIDDATACHECKEN_QST BIT(27)
> +#define CED_DATAISCHECKEN_QST BIT(26)
> +#define CED_GBCHECKEN_QST BIT(25)
> +#define CED_CTRLCHECKEN_QST BIT(24)
> +#define CED_CHLOCKMAXER_QST_MASK GENMASK(14, 0)
> +#define CED_CHLOCKMAXER_QST(x) UPDATE(x, 14, 0)
> +#define CED_DYN_CONFIG 0x0768
> +#define CED_DYN_CONTROL 0x076c
> +#define PKTEX_BCH_ERRFILT_CONFIG 0x07c4
> +#define PKTEX_CHKSUM_ERRFILT_CONFIG 0x07c8
> +
> +#define PKTDEC_ACR_PH2_1 0x1100
> +#define PKTDEC_ACR_PB3_0 0x1104
> +#define PKTDEC_ACR_PB7_4 0x1108
> +#define PKTDEC_AVIIF_PH2_1 0x1200
> +#define PKTDEC_AVIIF_PB3_0 0x1204
> +#define PKTDEC_AVIIF_PB7_4 0x1208
> +#define VIC_VAL_MASK GENMASK(6, 0)
> +#define PKTDEC_AVIIF_PB11_8 0x120c
> +#define PKTDEC_AVIIF_PB15_12 0x1210
> +#define PKTDEC_AVIIF_PB19_16 0x1214
> +#define PKTDEC_AVIIF_PB23_20 0x1218
> +#define PKTDEC_AVIIF_PB27_24 0x121c
> +
> +#define PKTFIFO_CONFIG 0x1500
> +#define PKTFIFO_STORE_FILT_CONFIG 0x1504
> +#define PKTFIFO_THR_CONFIG0 0x1508
> +#define PKTFIFO_THR_CONFIG1 0x150c
> +#define PKTFIFO_CONTROL 0x1510
> +
> +#define VMON_STATUS1 0x1580
> +#define VMON_STATUS2 0x1584
> +#define VMON_STATUS3 0x1588
> +#define VMON_STATUS4 0x158c
> +#define VMON_STATUS5 0x1590
> +#define VMON_STATUS6 0x1594
> +#define VMON_STATUS7 0x1598
> +#define VMON_ILACE_DETECT BIT(4)
> +
> +#define CEC_TX_CONTROL 0x2000
> +#define CEC_STATUS 0x2004
> +#define CEC_CONFIG 0x2008
> +#define RX_AUTO_DRIVE_ACKNOWLEDGE BIT(9)
> +#define CEC_ADDR 0x200c
> +#define CEC_TX_COUNT 0x2020
> +#define CEC_TX_DATA3_0 0x2024
> +#define CEC_RX_COUNT_STATUS 0x2040
> +#define CEC_RX_DATA3_0 0x2044
> +#define CEC_LOCK_CONTROL 0x2054
> +#define CEC_RXQUAL_BITTIME_CONFIG 0x2060
> +#define CEC_RX_BITTIME_CONFIG 0x2064
> +#define CEC_TX_BITTIME_CONFIG 0x2068
> +
> +#define DMA_CONFIG1 0x4400
> +#define UV_WID_MASK GENMASK(31, 28)
> +#define UV_WID(x) UPDATE(x, 31, 28)
> +#define Y_WID_MASK GENMASK(27, 24)
> +#define Y_WID(x) UPDATE(x, 27, 24)
> +#define DDR_STORE_FORMAT_MASK GENMASK(15, 12)
> +#define DDR_STORE_FORMAT(x) UPDATE(x, 15, 12)
> +#define ABANDON_EN BIT(0)
> +#define DMA_CONFIG2 0x4404
> +#define DMA_CONFIG3 0x4408
> +#define DMA_CONFIG4 0x440c // dma irq en
> +#define DMA_CONFIG5 0x4410 // dma irq clear status
> +#define LINE_FLAG_INT_EN BIT(8)
> +#define HDMIRX_DMA_IDLE_INT BIT(7)
> +#define HDMIRX_LOCK_DISABLE_INT BIT(6)
> +#define LAST_FRAME_AXI_UNFINISH_INT_EN BIT(5)
> +#define FIFO_OVERFLOW_INT_EN BIT(2)
> +#define FIFO_UNDERFLOW_INT_EN BIT(1)
> +#define HDMIRX_AXI_ERROR_INT_EN BIT(0)
> +#define DMA_CONFIG6 0x4414
> +#define RB_SWAP_EN BIT(9)
> +#define HSYNC_TOGGLE_EN BIT(5)
> +#define VSYNC_TOGGLE_EN BIT(4)
> +#define HDMIRX_DMA_EN BIT(1)
> +#define DMA_CONFIG7 0x4418
> +#define LINE_FLAG_NUM_MASK GENMASK(31, 16)
> +#define LINE_FLAG_NUM(x) UPDATE(x, 31, 16)
> +#define LOCK_FRAME_NUM_MASK GENMASK(11, 0)
> +#define LOCK_FRAME_NUM(x) UPDATE(x, 11, 0)
> +#define DMA_CONFIG8 0x441c
> +#define REG_MIRROR_EN BIT(0)
> +#define DMA_CONFIG9 0x4420
> +#define DMA_CONFIG10 0x4424
> +#define DMA_CONFIG11 0x4428
> +#define EDID_READ_EN_MASK BIT(8)
> +#define EDID_READ_EN(x) UPDATE(x, 8, 8)
> +#define EDID_WRITE_EN_MASK BIT(7)
> +#define EDID_WRITE_EN(x) UPDATE(x, 7, 7)
> +#define EDID_SLAVE_ADDR_MASK GENMASK(6, 0)
> +#define EDID_SLAVE_ADDR(x) UPDATE(x, 6, 0)
> +#define DMA_STATUS1 0x4430 // dma irq status
> +#define DMA_STATUS2 0x4434
> +#define DMA_STATUS3 0x4438
> +#define DMA_STATUS4 0x443c
> +#define DMA_STATUS5 0x4440
> +#define DMA_STATUS6 0x4444
> +#define DMA_STATUS7 0x4448
> +#define DMA_STATUS8 0x444c
> +#define DMA_STATUS9 0x4450
> +#define DMA_STATUS10 0x4454
> +#define HDMIRX_LOCK BIT(3)
> +#define DMA_STATUS11 0x4458
> +#define HDMIRX_TYPE_MASK GENMASK(8, 7)
> +#define HDMIRX_COLOR_DEPTH_MASK GENMASK(6, 3)
> +#define HDMIRX_FORMAT_MASK GENMASK(2, 0)
> +#define DMA_STATUS12 0x445c
> +#define DMA_STATUS13 0x4460
> +#define DMA_STATUS14 0x4464
> +
> +#define MAINUNIT_INTVEC_INDEX 0x5000
> +#define MAINUNIT_0_INT_STATUS 0x5010
> +#define CECRX_NOTIFY_ERR BIT(12)
> +#define CECRX_EOM BIT(11)
> +#define CECTX_DRIVE_ERR BIT(10)
> +#define CECRX_BUSY BIT(9)
> +#define CECTX_BUSY BIT(8)
> +#define CECTX_FRAME_DISCARDED BIT(5)
> +#define CECTX_NRETRANSMIT_FAIL BIT(4)
> +#define CECTX_LINE_ERR BIT(3)
> +#define CECTX_ARBLOST BIT(2)
> +#define CECTX_NACK BIT(1)
> +#define CECTX_DONE BIT(0)
> +#define MAINUNIT_0_INT_MASK_N 0x5014
> +#define MAINUNIT_0_INT_CLEAR 0x5018
> +#define MAINUNIT_0_INT_FORCE 0x501c
> +#define TIMER_BASE_LOCKED_IRQ BIT(26)
> +#define TMDSQPCLK_OFF_CHG BIT(5)
> +#define TMDSQPCLK_LOCKED_CHG BIT(4)
> +#define MAINUNIT_1_INT_STATUS 0x5020
> +#define MAINUNIT_1_INT_MASK_N 0x5024
> +#define MAINUNIT_1_INT_CLEAR 0x5028
> +#define MAINUNIT_1_INT_FORCE 0x502c
> +#define MAINUNIT_2_INT_STATUS 0x5030
> +#define MAINUNIT_2_INT_MASK_N 0x5034
> +#define MAINUNIT_2_INT_CLEAR 0x5038
> +#define MAINUNIT_2_INT_FORCE 0x503c
> +#define PHYCREG_CR_READ_DONE BIT(11)
> +#define PHYCREG_CR_WRITE_DONE BIT(10)
> +#define TMDSVALID_STABLE_CHG BIT(1)
> +
> +#define AVPUNIT_0_INT_STATUS 0x5040
> +#define AVPUNIT_0_INT_MASK_N 0x5044
> +#define AVPUNIT_0_INT_CLEAR 0x5048
> +#define AVPUNIT_0_INT_FORCE 0x504c
> +#define CED_DYN_CNT_CH2_IRQ BIT(22)
> +#define CED_DYN_CNT_CH1_IRQ BIT(21)
> +#define CED_DYN_CNT_CH0_IRQ BIT(20)
> +#define AVPUNIT_1_INT_STATUS 0x5050
> +#define DEFRAMER_VSYNC_THR_REACHED_IRQ BIT(1)
> +#define AVPUNIT_1_INT_MASK_N 0x5054
> +#define DEFRAMER_VSYNC_THR_REACHED_MASK_N BIT(1)
> +#define DEFRAMER_VSYNC_MASK_N BIT(0)
> +#define AVPUNIT_1_INT_CLEAR 0x5058
> +#define DEFRAMER_VSYNC_THR_REACHED_CLEAR BIT(1)
> +#define PKT_0_INT_STATUS 0x5080
> +#define PKTDEC_ACR_CHG_IRQ BIT(3)
> +#define PKT_0_INT_MASK_N 0x5084
> +#define PKTDEC_ACR_CHG_MASK_N BIT(3)
> +#define PKT_0_INT_CLEAR 0x5088
> +#define PKT_1_INT_STATUS 0x5090
> +#define PKT_1_INT_MASK_N 0x5094
> +#define PKT_1_INT_CLEAR 0x5098
> +#define PKT_2_INT_STATUS 0x50a0
> +#define PKTDEC_ACR_RCV_IRQ BIT(3)
> +#define PKT_2_INT_MASK_N 0x50a4
> +#define PKTDEC_AVIIF_RCV_IRQ BIT(11)
> +#define PKTDEC_ACR_RCV_MASK_N BIT(3)
> +#define PKT_2_INT_CLEAR 0x50a8
> +#define PKTDEC_AVIIF_RCV_CLEAR BIT(11)
> +#define PKTDEC_ACR_RCV_CLEAR BIT(3)
> +#define SCDC_INT_STATUS 0x50c0
> +#define SCDC_INT_MASK_N 0x50c4
> +#define SCDC_INT_CLEAR 0x50c8
> +#define SCDCTMDSCCFG_CHG BIT(2)
> +
> +#define CEC_INT_STATUS 0x5100
> +#define CEC_INT_MASK_N 0x5104
> +#define CEC_INT_CLEAR 0x5108
> +
> +#endif
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> new file mode 100644
> index 000000000000..9f67e2080bb6
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> @@ -0,0 +1,285 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + *
> + * Author: Shunqing Chen <csq@rock-chips.com>
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <media/cec.h>
> +#include <media/cec-notifier.h>
> +
> +#include "snps_hdmirx.h"
> +#include "snps_hdmirx_cec.h"
> +
> +static void hdmirx_cec_write(struct hdmirx_cec *cec, int reg, u32 val)
> +{
> + cec->ops->write(cec->hdmirx, reg, val);
> +}
> +
> +static u32 hdmirx_cec_read(struct hdmirx_cec *cec, int reg)
> +{
> + return cec->ops->read(cec->hdmirx, reg);
> +}
> +
> +static void hdmirx_cec_update_bits(struct hdmirx_cec *cec, int reg, u32 mask,
> + u32 data)
> +{
> + u32 val = hdmirx_cec_read(cec, reg) & ~mask;
> +
> + val |= (data & mask);
> + hdmirx_cec_write(cec, reg, val);
> +}
> +
> +static int hdmirx_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
> +{
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> +
> + if (logical_addr == CEC_LOG_ADDR_INVALID)
> + cec->addresses = 0;
> + else
> + cec->addresses |= BIT(logical_addr) | BIT(15);
> +
> + hdmirx_cec_write(cec, CEC_ADDR, cec->addresses);
> +
> + return 0;
> +}
> +
> +/* signal_free_time is handled by the Synopsys Designware
> + * HDMIRX Controller hardware.
> + */
> +static int hdmirx_cec_transmit(struct cec_adapter *adap, u8 attempts,
> + u32 signal_free_time, struct cec_msg *msg)
> +{
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> + u32 data[4] = {0};
> + int i, data_len, msg_len;
> +
> + msg_len = msg->len;
> +
> + hdmirx_cec_write(cec, CEC_TX_COUNT, msg_len - 1);
> + for (i = 0; i < msg_len; i++)
> + data[i / 4] |= msg->msg[i] << (i % 4) * 8;
> +
> + data_len = DIV_ROUND_UP(msg_len, 4);
> +
> + for (i = 0; i < data_len; i++)
> + hdmirx_cec_write(cec, CEC_TX_DATA3_0 + i * 4, data[i]);
> +
> + hdmirx_cec_write(cec, CEC_TX_CONTROL, 0x1);
> +
> + return 0;
> +}
> +
> +static irqreturn_t hdmirx_cec_hardirq(int irq, void *data)
> +{
> + struct cec_adapter *adap = data;
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> + u32 stat = hdmirx_cec_read(cec, CEC_INT_STATUS);
> + irqreturn_t ret = IRQ_HANDLED;
> + u32 val;
> +
> + if (!stat)
> + return IRQ_NONE;
> +
> + hdmirx_cec_write(cec, CEC_INT_CLEAR, stat);
> +
> + if (stat & CECTX_LINE_ERR) {
> + cec->tx_status = CEC_TX_STATUS_ERROR;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + } else if (stat & CECTX_DONE) {
> + cec->tx_status = CEC_TX_STATUS_OK;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + } else if (stat & CECTX_NACK) {
> + cec->tx_status = CEC_TX_STATUS_NACK;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + } else if (stat & CECTX_ARBLOST) {
> + cec->tx_status = CEC_TX_STATUS_ARB_LOST;
> + cec->tx_done = true;
> + ret = IRQ_WAKE_THREAD;
> + }
> +
> + if (stat & CECRX_EOM) {
> + unsigned int len, i;
> +
> + val = hdmirx_cec_read(cec, CEC_RX_COUNT_STATUS);
> + /* rxbuffer locked status */
> + if ((val & 0x80))
> + return ret;
> +
> + len = (val & 0xf) + 1;
> + if (len > sizeof(cec->rx_msg.msg))
> + len = sizeof(cec->rx_msg.msg);
> +
> + for (i = 0; i < len; i++) {
> + if (!(i % 4))
> + val = hdmirx_cec_read(cec, CEC_RX_DATA3_0 + i / 4 * 4);
> + cec->rx_msg.msg[i] = (val >> ((i % 4) * 8)) & 0xff;
> + }
> +
> + cec->rx_msg.len = len;
> + smp_wmb(); /* receive RX msg */
> + cec->rx_done = true;
> + hdmirx_cec_write(cec, CEC_LOCK_CONTROL, 0x1);
> +
> + ret = IRQ_WAKE_THREAD;
> + }
> +
> + return ret;
> +}
> +
> +static irqreturn_t hdmirx_cec_thread(int irq, void *data)
> +{
> + struct cec_adapter *adap = data;
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> +
> + if (cec->tx_done) {
> + cec->tx_done = false;
> + cec_transmit_attempt_done(adap, cec->tx_status);
> + }
> + if (cec->rx_done) {
> + cec->rx_done = false;
> + smp_rmb(); /* RX msg has been received */
> + cec_received_msg(adap, &cec->rx_msg);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int hdmirx_cec_enable(struct cec_adapter *adap, bool enable)
> +{
> + struct hdmirx_cec *cec = cec_get_drvdata(adap);
> +
> + if (!enable) {
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
> + hdmirx_cec_write(cec, CEC_INT_CLEAR, 0);
> + if (cec->ops->disable)
> + cec->ops->disable(cec->hdmirx);
> + } else {
> + unsigned int irqs;
> +
> + hdmirx_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
> + if (cec->ops->enable)
> + cec->ops->enable(cec->hdmirx);
> + hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
> +
> + irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
> + }
> +
> + return 0;
> +}
> +
> +static const struct cec_adap_ops hdmirx_cec_ops = {
> + .adap_enable = hdmirx_cec_enable,
> + .adap_log_addr = hdmirx_cec_log_addr,
> + .adap_transmit = hdmirx_cec_transmit,
> +};
> +
> +static void hdmirx_cec_del(void *data)
> +{
> + struct hdmirx_cec *cec = data;
> +
> + cec_delete_adapter(cec->adap);
> +}
> +
> +struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data)
> +{
> + struct hdmirx_cec *cec;
> + unsigned int irqs;
> + int ret;
> +
> + /*
> + * Our device is just a convenience - we want to link to the real
> + * hardware device here, so that userspace can see the association
> + * between the HDMI hardware and its associated CEC chardev.
> + */
> + cec = devm_kzalloc(data->dev, sizeof(*cec), GFP_KERNEL);
> + if (!cec)
> + return NULL;
> +
> + cec->dev = data->dev;
> + cec->irq = data->irq;
> + cec->ops = data->ops;
> + cec->hdmirx = data->hdmirx;
> +
> + hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
> + hdmirx_cec_update_bits(cec, CEC_CONFIG, RX_AUTO_DRIVE_ACKNOWLEDGE,
> + RX_AUTO_DRIVE_ACKNOWLEDGE);
> +
> + hdmirx_cec_write(cec, CEC_TX_COUNT, 0);
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
> + hdmirx_cec_write(cec, CEC_INT_CLEAR, ~0);
> +
> + cec->adap = cec_allocate_adapter(&hdmirx_cec_ops, cec, "snps-hdmirx",
> + CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
> + CEC_CAP_RC | CEC_CAP_PASSTHROUGH |
Use CEC_CAP_DEFAULTS, which is the four CAPs above ORed together.
> + CEC_CAP_MONITOR_ALL,
> + CEC_MAX_LOG_ADDRS);
> + if (IS_ERR(cec->adap)) {
> + dev_err(cec->dev, "cec adap allocate failed\n");
> + return NULL;
> + }
> +
> + /* override the module pointer */
> + cec->adap->owner = THIS_MODULE;
> +
> + ret = devm_add_action(cec->dev, hdmirx_cec_del, cec);
> + if (ret) {
> + cec_delete_adapter(cec->adap);
> + return NULL;
> + }
> +
> + irq_set_status_flags(cec->irq, IRQ_NOAUTOEN);
> +
> + ret = devm_request_threaded_irq(cec->dev, cec->irq,
> + hdmirx_cec_hardirq,
> + hdmirx_cec_thread, IRQF_ONESHOT,
> + "rk_hdmirx_cec", cec->adap);
> + if (ret) {
> + dev_err(cec->dev, "cec irq request failed\n");
> + return NULL;
> + }
> +
> + cec->notify = cec_notifier_cec_adap_register(cec->dev,
> + NULL, cec->adap);
> + if (!cec->notify) {
> + dev_err(cec->dev, "cec notify register failed\n");
> + return NULL;
> + }
> +
> + ret = cec_register_adapter(cec->adap, cec->dev);
> + if (ret < 0) {
> + dev_err(cec->dev, "cec register adapter failed\n");
> + cec_unregister_adapter(cec->adap);
> + return NULL;
> + }
> +
> + irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
> + hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
> +
> + /*
> + * CEC documentation says we must not call cec_delete_adapter
> + * after a successful call to cec_register_adapter().
> + */
> + devm_remove_action(cec->dev, hdmirx_cec_del, cec);
> +
> + enable_irq(cec->irq);
> +
> + return cec;
> +}
> +
> +void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec)
> +{
> + disable_irq(cec->irq);
> +
> + cec_unregister_adapter(cec->adap);
> +}
> diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> new file mode 100644
> index 000000000000..c55c403cdb9f
> --- /dev/null
> +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
> + *
> + * Author: Shunqing Chen <csq@rock-chips.com>
> + */
> +
> +#ifndef DW_HDMI_RX_CEC_H
> +#define DW_HDMI_RX_CEC_H
> +
> +struct snps_hdmirx_dev;
> +
> +struct hdmirx_cec_ops {
> + void (*write)(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val);
> + u32 (*read)(struct snps_hdmirx_dev *hdmirx_dev, int reg);
> + void (*enable)(struct snps_hdmirx_dev *hdmirx);
> + void (*disable)(struct snps_hdmirx_dev *hdmirx);
> +};
> +
> +struct hdmirx_cec_data {
> + struct snps_hdmirx_dev *hdmirx;
> + const struct hdmirx_cec_ops *ops;
> + struct device *dev;
> + int irq;
> +};
> +
> +struct hdmirx_cec {
> + struct snps_hdmirx_dev *hdmirx;
> + struct device *dev;
> + const struct hdmirx_cec_ops *ops;
> + u32 addresses;
> + struct cec_adapter *adap;
> + struct cec_msg rx_msg;
> + unsigned int tx_status;
> + bool tx_done;
> + bool rx_done;
> + struct cec_notifier *notify;
> + int irq;
> +};
> +
> +struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data);
> +void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec);
> +
> +#endif /* DW_HDMI_RX_CEC_H */
Regards,
Hans
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-22 13:53 ` Shreeya Patel
@ 2024-07-23 11:16 ` Johan Jonker
2024-07-23 17:28 ` Sebastian Reichel
0 siblings, 1 reply; 40+ messages in thread
From: Johan Jonker @ 2024-07-23 11:16 UTC (permalink / raw)
To: Shreeya Patel
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip, Dmitry Osipenko
On 7/22/24 15:53, Shreeya Patel wrote:
> On Saturday, July 20, 2024 16:14 IST, Johan Jonker <jbx6244@yandex.com> wrote:
>
> Hi Johan,
>
>>
>>
>> On 7/19/24 14:40, Shreeya Patel wrote:
>>> Document bindings for the Synopsys DesignWare HDMI RX Controller.
>>>
>>> Reviewed-by: Rob Herring <robh@kernel.org>
>>> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Remove to trigger a new review.
>>> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
>>> ---
>>>
>>> Changes in v4 :-
>>> - No change
>>>
>>> Changes in v3 :-
>>> - Rename hdmirx_cma to hdmi_receiver_cma
>>> - Add a Reviewed-by tag
>>>
>>> Changes in v2 :-
>>> - Add a description for the hardware
>>> - Rename resets, vo1 grf and HPD properties
>>> - Add a proper description for grf and vo1-grf phandles
>>> - Rename the HDMI Input node name to hdmi-receiver
>>> - Improve the subject line
>>> - Include gpio header file in example to fix dt_binding_check failure
>>>
>>> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
>>> 1 file changed, 132 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>> new file mode 100644
>>> index 000000000000..96ae1e2d2816
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>> @@ -0,0 +1,132 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
>>> +
>>> +---
>>> +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: Synopsys DesignWare HDMI RX Controller
>>> +
>>> +maintainers:
>>> + - Shreeya Patel <shreeya.patel@collabora.com>
>>> +
>>> +description:
>>> + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
>>> + allowing devices to receive and decode high-resolution video streams
>>> + from external sources like media players, cameras, laptops, etc.
>>> +
>>> +properties:
>>> + compatible:
>>> + items:
>>> + - const: rockchip,rk3588-hdmirx-ctrler
>>
>>> + - const: snps,dw-hdmi-rx
remove
>>
>> 1: Compatible strings must be SoC orientated.
>> 2: In Linux there's no priority in which string will probed first.
>> What's the point of having a fallback string when there's no common code, but instead only the first string is used?
>>
>> +static const struct of_device_id hdmirx_id[] = {
>> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
>> + { },
>> +};
>>
>
> We believe the HDMIRX driver can be used for the Synopsys IP on other SoCs
> in the future, which is why we have added snps,dw-hdmi-rx as the fallback compatible.
> Currently, we have tested the driver only on the RK3588 Rock5B, so we are using the
> rockchip,rk3588-hdmirx-ctrler compatible in the driver instead of the fallback one.
The rule that compatible strings (for internal SoC components) must be SoC orientated also applies to the fallback string. "snps,xxxx" does not refer to an independent SoC.
Don't invent strings for devices that we don't know yet if it might or might not be compatible in the future.
Johan
>
>
> Thanks,
> Shreeya Patel
>
>>> +
>>> + reg:
>>> + maxItems: 1
>>> +
>>> + interrupts:
>>> + maxItems: 3
>>> +
>>> + interrupt-names:
>>> + items:
>>> + - const: cec
>>> + - const: hdmi
>>> + - const: dma
>>> +
>>> + clocks:
>>> + maxItems: 7
>>> +
>>> + clock-names:
>>> + items:
>>> + - const: aclk
>>> + - const: audio
>>> + - const: cr_para
>>> + - const: pclk
>>> + - const: ref
>>> + - const: hclk_s_hdmirx
>>> + - const: hclk_vo1
>>> +
>>> + power-domains:
>>> + maxItems: 1
>>> +
>>> + resets:
>>> + maxItems: 4
>>> +
>>> + reset-names:
>>> + items:
>>> + - const: axi
>>> + - const: apb
>>> + - const: ref
>>> + - const: biu
>>> +
>>> + memory-region:
>>> + maxItems: 1
>>> +
>>> + hpd-gpios:
>>> + description: GPIO specifier for HPD.
>>> + maxItems: 1
>>> +
>>> + rockchip,grf:
>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>> + description:
>>> + The phandle of the syscon node for the general register file
>>> + containing HDMIRX PHY status bits.
>>> +
>>> + rockchip,vo1-grf:
>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>> + description:
>>> + The phandle of the syscon node for the Video Output GRF register
>>> + to enable EDID transfer through SDAIN and SCLIN.
>>> +
>>> +required:
>>> + - compatible
>>> + - reg
>>> + - interrupts
>>> + - interrupt-names
>>> + - clocks
>>> + - clock-names
>>> + - power-domains
>>> + - resets
>>> + - pinctrl-0
>>> + - hpd-gpios
>>> +
>>> +additionalProperties: false
>>> +
>>> +examples:
>>> + - |
>>> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
>>> + #include <dt-bindings/gpio/gpio.h>
>>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>>> + #include <dt-bindings/interrupt-controller/irq.h>
>>> + #include <dt-bindings/power/rk3588-power.h>
>>> + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
>>> + hdmi_receiver: hdmi-receiver@fdee0000 {
>>> + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
compatible = "rockchip,rk3588-hdmirx-ctrler";
>>> + reg = <0xfdee0000 0x6000>;
>>> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
>>> + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
>>> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
>>> + interrupt-names = "cec", "hdmi", "dma";
>>> + clocks = <&cru ACLK_HDMIRX>,
>>> + <&cru CLK_HDMIRX_AUD>,
>>> + <&cru CLK_CR_PARA>,
>>> + <&cru PCLK_HDMIRX>,
>>> + <&cru CLK_HDMIRX_REF>,
>>> + <&cru PCLK_S_HDMIRX>,
>>> + <&cru HCLK_VO1>;
>>> + clock-names = "aclk",
>>> + "audio",
>>> + "cr_para",
>>> + "pclk",
>>> + "ref",
>>> + "hclk_s_hdmirx",
>>> + "hclk_vo1";
>>> + power-domains = <&power RK3588_PD_VO1>;
>>> + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
>>> + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
>>> + reset-names = "axi", "apb", "ref", "biu";
>>> + memory-region = <&hdmi_receiver_cma>;
>>> + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
>>> + pinctrl-names = "default";
>>> + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
>>> + };
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-23 11:16 ` Johan Jonker
@ 2024-07-23 17:28 ` Sebastian Reichel
2024-07-24 13:20 ` Johan Jonker
0 siblings, 1 reply; 40+ messages in thread
From: Sebastian Reichel @ 2024-07-23 17:28 UTC (permalink / raw)
To: Johan Jonker
Cc: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco, kernel, linux-kernel,
linux-media, devicetree, linux-arm-kernel, linux-rockchip,
Dmitry Osipenko
[-- Attachment #1: Type: text/plain, Size: 10090 bytes --]
Hi,
On Tue, Jul 23, 2024 at 01:16:00PM GMT, Johan Jonker wrote:
> On 7/22/24 15:53, Shreeya Patel wrote:
> > On Saturday, July 20, 2024 16:14 IST, Johan Jonker <jbx6244@yandex.com> wrote:
> >> On 7/19/24 14:40, Shreeya Patel wrote:
> >>> Document bindings for the Synopsys DesignWare HDMI RX Controller.
> >>>
>
> >>> Reviewed-by: Rob Herring <robh@kernel.org>
> >>> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
>
> Remove to trigger a new review.
Rob and Dmitry both already reviewed the version with the fallback
compatible. I don't think the rename of hdmirx_cma to hdmi_receiver_cma
warrant a new review. Also FWIW:
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
> >>> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> >>> ---
> >>>
> >>> Changes in v4 :-
> >>> - No change
> >>>
> >>> Changes in v3 :-
> >>> - Rename hdmirx_cma to hdmi_receiver_cma
> >>> - Add a Reviewed-by tag
> >>>
> >>> Changes in v2 :-
> >>> - Add a description for the hardware
> >>> - Rename resets, vo1 grf and HPD properties
> >>> - Add a proper description for grf and vo1-grf phandles
> >>> - Rename the HDMI Input node name to hdmi-receiver
> >>> - Improve the subject line
> >>> - Include gpio header file in example to fix dt_binding_check failure
> >>>
> >>> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
> >>> 1 file changed, 132 insertions(+)
> >>> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >>> new file mode 100644
> >>> index 000000000000..96ae1e2d2816
> >>> --- /dev/null
> >>> +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >>> @@ -0,0 +1,132 @@
> >>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> >>> +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
> >>> +
> >>> +---
> >>> +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
> >>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> >>> +
> >>> +title: Synopsys DesignWare HDMI RX Controller
> >>> +
> >>> +maintainers:
> >>> + - Shreeya Patel <shreeya.patel@collabora.com>
> >>> +
> >>> +description:
> >>> + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
> >>> + allowing devices to receive and decode high-resolution video streams
> >>> + from external sources like media players, cameras, laptops, etc.
> >>> +
> >>> +properties:
> >>> + compatible:
> >>> + items:
> >>> + - const: rockchip,rk3588-hdmirx-ctrler
> >>
>
> >>> + - const: snps,dw-hdmi-rx
>
> remove
>
> >>
> >> 1: Compatible strings must be SoC orientated.
> >> 2: In Linux there's no priority in which string will probed first.
> >> What's the point of having a fallback string when there's no common code, but instead only the first string is used?
> >>
> >> +static const struct of_device_id hdmirx_id[] = {
> >> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> >> + { },
> >> +};
> >>
> >
>
> > We believe the HDMIRX driver can be used for the Synopsys IP on other SoCs
> > in the future, which is why we have added snps,dw-hdmi-rx as the fallback compatible.
> > Currently, we have tested the driver only on the RK3588 Rock5B, so we are using the
> > rockchip,rk3588-hdmirx-ctrler compatible in the driver instead of the fallback one.
>
> The rule that compatible strings (for internal SoC components)
> must be SoC orientated also applies to the fallback string.
> "snps,xxxx" does not refer to an independent SoC.
Where did you learn that? Having non-SoC specific generic fallback
compatibles is pretty much standard throughout the kernel. See for
example these RK3588 DesignWare compatibles:
Synopsys Serial Controller:
Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
Synopsys USB3 Controller:
Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
Synopsys Ethernet Controller:
Documentation/devicetree/bindings/net/snps,dwmac.yaml
compatible = "rockchip,rk3588-gmac", "snps,dwmac-4.20a";
Synsopsys SATA Controller:
Documentation/devicetree/bindings/ata/rockchip,dwc-ahci.yaml
compatible = "rockchip,rk3588-dwc-ahci", "snps,dwc-ahci"
It's also not specific to Synopsys (but RK3588 has a lot of Synopsys
design incl. the HDMI-RX IP currently worked on by Shreeya). Here
are some other examples:
ARM Mali GPU:
Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
compatible = "rockchip,rk3588-mali", "arm,mali-valhall-csf";
Generic EHCI:
Documentation/devicetree/bindings/usb/generic-ehci.yaml
compatible = "rockchip,rk3588-ehci", "generic-ehci";
As you can see almost everything in RK3588 has a non SoC specific
fallback :) It's also not a Rockchip/RK3588 specific thing, but
I think you should be able to find enough references yourself by
looking into the kernel's DTS files.
> Don't invent strings for devices that we don't know yet if it
> might or might not be compatible in the future.
Right now it's a sensible assumption, that an operating system driver
for this hardware (i.e. not necessarily the one submitted by Shreeya
right now) can handle the Synopsys HDMI receiver hardware from different
SoCs just like it is the case for other Synopsys IP.
Whatever is being done now is set in stone, since DT is considered
ABI. So without the fallback compatible being available in DT from
the beginning we need to carry the RK3588 specific compatible in the
kernel driver forever. OTOH if we add the generic one now, the kernel
can switch to use the generic one at any point in time and ignore the
RK3588 specific one.
Greetings,
-- Sebastian
> Johan
>
> >
> >
> > Thanks,
> > Shreeya Patel
> >
> >>> +
> >>> + reg:
> >>> + maxItems: 1
> >>> +
> >>> + interrupts:
> >>> + maxItems: 3
> >>> +
> >>> + interrupt-names:
> >>> + items:
> >>> + - const: cec
> >>> + - const: hdmi
> >>> + - const: dma
> >>> +
> >>> + clocks:
> >>> + maxItems: 7
> >>> +
> >>> + clock-names:
> >>> + items:
> >>> + - const: aclk
> >>> + - const: audio
> >>> + - const: cr_para
> >>> + - const: pclk
> >>> + - const: ref
> >>> + - const: hclk_s_hdmirx
> >>> + - const: hclk_vo1
> >>> +
> >>> + power-domains:
> >>> + maxItems: 1
> >>> +
> >>> + resets:
> >>> + maxItems: 4
> >>> +
> >>> + reset-names:
> >>> + items:
> >>> + - const: axi
> >>> + - const: apb
> >>> + - const: ref
> >>> + - const: biu
> >>> +
> >>> + memory-region:
> >>> + maxItems: 1
> >>> +
> >>> + hpd-gpios:
> >>> + description: GPIO specifier for HPD.
> >>> + maxItems: 1
> >>> +
> >>> + rockchip,grf:
> >>> + $ref: /schemas/types.yaml#/definitions/phandle
> >>> + description:
> >>> + The phandle of the syscon node for the general register file
> >>> + containing HDMIRX PHY status bits.
> >>> +
> >>> + rockchip,vo1-grf:
> >>> + $ref: /schemas/types.yaml#/definitions/phandle
> >>> + description:
> >>> + The phandle of the syscon node for the Video Output GRF register
> >>> + to enable EDID transfer through SDAIN and SCLIN.
> >>> +
> >>> +required:
> >>> + - compatible
> >>> + - reg
> >>> + - interrupts
> >>> + - interrupt-names
> >>> + - clocks
> >>> + - clock-names
> >>> + - power-domains
> >>> + - resets
> >>> + - pinctrl-0
> >>> + - hpd-gpios
> >>> +
> >>> +additionalProperties: false
> >>> +
> >>> +examples:
> >>> + - |
> >>> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
> >>> + #include <dt-bindings/gpio/gpio.h>
> >>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> >>> + #include <dt-bindings/interrupt-controller/irq.h>
> >>> + #include <dt-bindings/power/rk3588-power.h>
> >>> + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
> >>> + hdmi_receiver: hdmi-receiver@fdee0000 {
>
> >>> + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
>
> compatible = "rockchip,rk3588-hdmirx-ctrler";
>
> >>> + reg = <0xfdee0000 0x6000>;
> >>> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
> >>> + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
> >>> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
> >>> + interrupt-names = "cec", "hdmi", "dma";
> >>> + clocks = <&cru ACLK_HDMIRX>,
> >>> + <&cru CLK_HDMIRX_AUD>,
> >>> + <&cru CLK_CR_PARA>,
> >>> + <&cru PCLK_HDMIRX>,
> >>> + <&cru CLK_HDMIRX_REF>,
> >>> + <&cru PCLK_S_HDMIRX>,
> >>> + <&cru HCLK_VO1>;
> >>> + clock-names = "aclk",
> >>> + "audio",
> >>> + "cr_para",
> >>> + "pclk",
> >>> + "ref",
> >>> + "hclk_s_hdmirx",
> >>> + "hclk_vo1";
> >>> + power-domains = <&power RK3588_PD_VO1>;
> >>> + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
> >>> + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
> >>> + reset-names = "axi", "apb", "ref", "biu";
> >>> + memory-region = <&hdmi_receiver_cma>;
> >>> + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
> >>> + pinctrl-names = "default";
> >>> + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
> >>> + };
> >
> _______________________________________________
> Kernel mailing list -- kernel@mailman.collabora.com
> To unsubscribe send an email to kernel-leave@mailman.collabora.com
> This list is managed by https://mailman.collabora.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-23 17:28 ` Sebastian Reichel
@ 2024-07-24 13:20 ` Johan Jonker
2024-07-25 6:35 ` Krzysztof Kozlowski
` (2 more replies)
0 siblings, 3 replies; 40+ messages in thread
From: Johan Jonker @ 2024-07-24 13:20 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco, kernel, linux-kernel,
linux-media, devicetree, linux-arm-kernel, linux-rockchip,
Dmitry Osipenko
On 7/23/24 19:28, Sebastian Reichel wrote:
> Hi,
>
> On Tue, Jul 23, 2024 at 01:16:00PM GMT, Johan Jonker wrote:
>> On 7/22/24 15:53, Shreeya Patel wrote:
>>> On Saturday, July 20, 2024 16:14 IST, Johan Jonker <jbx6244@yandex.com> wrote:
>>>> On 7/19/24 14:40, Shreeya Patel wrote:
>>>>> Document bindings for the Synopsys DesignWare HDMI RX Controller.
>>>>>
>>
>>>>> Reviewed-by: Rob Herring <robh@kernel.org>
>>>>> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
>>
>> Remove to trigger a new review.
>
> Rob and Dmitry both already reviewed the version with the fallback
> compatible. I don't think the rename of hdmirx_cma to hdmi_receiver_cma
> warrant a new review. Also FWIW:
>
> Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Please have a look at the comments below before you tag.
>
>>>>> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
>>>>> ---
>>>>>
>>>>> Changes in v4 :-
>>>>> - No change
>>>>>
>>>>> Changes in v3 :-
>>>>> - Rename hdmirx_cma to hdmi_receiver_cma
>>>>> - Add a Reviewed-by tag
>>>>>
>>>>> Changes in v2 :-
>>>>> - Add a description for the hardware
>>>>> - Rename resets, vo1 grf and HPD properties
>>>>> - Add a proper description for grf and vo1-grf phandles
>>>>> - Rename the HDMI Input node name to hdmi-receiver
>>>>> - Improve the subject line
>>>>> - Include gpio header file in example to fix dt_binding_check failure
>>>>>
>>>>> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
>>>>> 1 file changed, 132 insertions(+)
>>>>> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>>> new file mode 100644
>>>>> index 000000000000..96ae1e2d2816
>>>>> --- /dev/null
>>>>> +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>>> @@ -0,0 +1,132 @@
>>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>>> +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
>>>>> +
>>>>> +---
>>>>> +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
>>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>>> +
>>>>> +title: Synopsys DesignWare HDMI RX Controller
>>>>> +
>>>>> +maintainers:
>>>>> + - Shreeya Patel <shreeya.patel@collabora.com>
>>>>> +
>>>>> +description:
>>>>> + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
>>>>> + allowing devices to receive and decode high-resolution video streams
>>>>> + from external sources like media players, cameras, laptops, etc.
>>>>> +
>>>>> +properties:
>>>>> + compatible:
>>>>> + items:
>>>>> + - const: rockchip,rk3588-hdmirx-ctrler
>>>>
>>
>>>>> + - const: snps,dw-hdmi-rx
>>
>> remove
>>
>>>>
Relevant compatible methods in use for Rockchip drivers:
===================================================================================================
Compatible method #1:
Probe is triggered by a SoC orientated string.
compatible = "rockchip,rk3588-hdmirx-ctrler";
If for example a new SoC rk3599 is released that has the same device properties
then the old string can be used as fallback string.
compatible = ""rockchip,rk3599-hdmirx-ctrler" , "rockchip,rk3588-hdmirx-ctrler";
The driver structure:
{ .compatible = "rockchip,rk3588-hdmirx-ctrler" },
===================================================================================================
Compatible method #2:
Probe is triggered by a IP orientated fallback string.
compatible = "rockchip,rk3588-hdmirx-ctrler" , "snps,dw-hdmi-rx";
If for example a new SoC rk3599 is released that has the same device properties
then add the same fallback string.
compatible = ""rockchip,rk3599-hdmirx-ctrler" , "snps,dw-hdmi-rx";
The driver structure:
{ .compatible = "snps,dw-hdmi-rx" },
If for example a new SoC rk3599 is released that has NOT the same device properties
then use method #1.
The driver structure:
{ .compatible = "rockchip,rk3599-hdmirx-ctrler" .data = &rk3599_ops },
{ .compatible = "snps,dw-hdmi-rx" },
===================================================================================================
Compatible method #3:
Probe is triggered by a vendor orientated fallback string.
Special case only useful if the driver is written long after all SoCs are released.
The standalone IP has a version register and the driver can handle all the feature difference
inside the IP depending on the version register.
compatible = "rockchip,sfc";
The driver structure:
{ .compatible = "rockchip,sfc"},
===================================================================================================
The rules:
1: Compatible strings must be SoC orientated.
2: In Linux there's no priority in which string will probed first.
3: There is a commitment that old DT's should still work with newer kernels.
>>>> What's the point of having a fallback string when there's no common code, but instead only the first string is used?
>>>>
>>>> +static const struct of_device_id hdmirx_id[] = {
>>>> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
>>>> + { },
>>>> +};
>>>>
The consequence of the third rule is that drivers must continue to support this string once added and
can not be removed as suggested below.
If for example the fallback is added later it will trigger 2 probes and it breaks rule #2.
Only one of string is allowed to trigger a probe in the driver.
This is wrong:
compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
{ .compatible = "rockchip,rk3588-hdmirx-ctrler" },
{ .compatible = "snps,dw-hdmi-rx" },
Ones a compatible method is chosen the driver must stick to it.
===================================================================================================
>>>
>>
>>> We believe the HDMIRX driver can be used for the Synopsys IP on other SoCs
>>> in the future, which is why we have added snps,dw-hdmi-rx as the fallback compatible.
>>> Currently, we have tested the driver only on the RK3588 Rock5B, so we are using the
>>> rockchip,rk3588-hdmirx-ctrler compatible in the driver instead of the fallback one.
>>
>> The rule that compatible strings (for internal SoC components)
>> must be SoC orientated also applies to the fallback string.
>> "snps,xxxx" does not refer to an independent SoC.
This refers to compatible method #1.
>
> Where did you learn that? Having non-SoC specific generic fallback
> compatibles is pretty much standard throughout the kernel. See for
> example these RK3588 DesignWare compatibles:
>
> Synopsys Serial Controller:
> Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
> compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
Compatible method #2:
{ .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
>
> Synopsys USB3 Controller:
> Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
> compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
Compatible method #2:
{
.compatible = "snps,dwc3"
},
>
> Synopsys Ethernet Controller:
> Documentation/devicetree/bindings/net/snps,dwmac.yaml
> compatible = "rockchip,rk3588-gmac", "snps,dwmac-4.20a";
Compatible method #1:
{ .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops },
of_device_is_compatible(np, "snps,dwmac-4.20a") ||
>
> Synsopsys SATA Controller:
> Documentation/devicetree/bindings/ata/rockchip,dwc-ahci.yaml
> compatible = "rockchip,rk3588-dwc-ahci", "snps,dwc-ahci"
Compatible method #2:
{ .compatible = "snps,dwc-ahci", &ahci_dwc_plat },
>
> It's also not specific to Synopsys (but RK3588 has a lot of Synopsys
> design incl. the HDMI-RX IP currently worked on by Shreeya). Here
> are some other examples:
>
> ARM Mali GPU:
> Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
> compatible = "rockchip,rk3588-mali", "arm,mali-valhall-csf";
Should be compatible method #2:
{ .compatible = "rockchip,rk3588-mali" },
{ .compatible = "arm,mali-valhall-csf" },
This is wrong!
Each strings will trigger a probe.
The string "rockchip,rk3588-mali" should be removed.
Review was done by Collabora people and without including the Rockchip mail list.
https://patchwork.freedesktop.org/patch/msgid/20240229162230.2634044-12-boris.brezillon@collabora.com
Could someone look at this and test.
>
> Generic EHCI:
> Documentation/devicetree/bindings/usb/generic-ehci.yaml
> compatible = "rockchip,rk3588-ehci", "generic-ehci";
compatible method #2:
{ .compatible = "generic-ehci", },
>
> As you can see almost everything in RK3588 has a non SoC specific
> fallback :) It's also not a Rockchip/RK3588 specific thing, but
> I think you should be able to find enough references yourself by
> looking into the kernel's DTS files.
You are mixing up 2 compatible methods.
The driver has compatible method #1 and the DT has method #2.
>
>> Don't invent strings for devices that we don't know yet if it
>> might or might not be compatible in the future.
>
> Right now it's a sensible assumption, that an operating system driver
> for this hardware (i.e. not necessarily the one submitted by Shreeya
> right now) can handle the Synopsys HDMI receiver hardware from different
> SoCs just like it is the case for other Synopsys IP.
>
> Whatever is being done now is set in stone, since DT is considered
> ABI. So without the fallback compatible being available in DT from
> the beginning we need to carry the RK3588 specific compatible in the
> kernel driver forever. OTOH if we add the generic one now, the kernel
> can switch to use the generic one at any point in time and ignore the
> RK3588 specific one.
Ignoring breaks rule #3 as explained above.
For you the task to select a compatible method:
If the IP device registers are guaranteed remain the same then choose compatible method #2 and fix the driver.
If in doubt choose compatible method #1 and fix the binding.
Johan
>
> Greetings,
>
> -- Sebastian
>
>> Johan
>>
>>>
>>>
>>> Thanks,
>>> Shreeya Patel
>>>
>>>>> +
>>>>> + reg:
>>>>> + maxItems: 1
>>>>> +
>>>>> + interrupts:
>>>>> + maxItems: 3
>>>>> +
>>>>> + interrupt-names:
>>>>> + items:
>>>>> + - const: cec
>>>>> + - const: hdmi
>>>>> + - const: dma
>>>>> +
>>>>> + clocks:
>>>>> + maxItems: 7
>>>>> +
>>>>> + clock-names:
>>>>> + items:
>>>>> + - const: aclk
>>>>> + - const: audio
>>>>> + - const: cr_para
>>>>> + - const: pclk
>>>>> + - const: ref
>>>>> + - const: hclk_s_hdmirx
>>>>> + - const: hclk_vo1
>>>>> +
>>>>> + power-domains:
>>>>> + maxItems: 1
>>>>> +
>>>>> + resets:
>>>>> + maxItems: 4
>>>>> +
>>>>> + reset-names:
>>>>> + items:
>>>>> + - const: axi
>>>>> + - const: apb
>>>>> + - const: ref
>>>>> + - const: biu
>>>>> +
>>>>> + memory-region:
>>>>> + maxItems: 1
>>>>> +
>>>>> + hpd-gpios:
>>>>> + description: GPIO specifier for HPD.
>>>>> + maxItems: 1
>>>>> +
>>>>> + rockchip,grf:
>>>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>>>> + description:
>>>>> + The phandle of the syscon node for the general register file
>>>>> + containing HDMIRX PHY status bits.
>>>>> +
>>>>> + rockchip,vo1-grf:
>>>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>>>> + description:
>>>>> + The phandle of the syscon node for the Video Output GRF register
>>>>> + to enable EDID transfer through SDAIN and SCLIN.
>>>>> +
>>>>> +required:
>>>>> + - compatible
>>>>> + - reg
>>>>> + - interrupts
>>>>> + - interrupt-names
>>>>> + - clocks
>>>>> + - clock-names
>>>>> + - power-domains
>>>>> + - resets
>>>>> + - pinctrl-0
>>>>> + - hpd-gpios
>>>>> +
>>>>> +additionalProperties: false
>>>>> +
>>>>> +examples:
>>>>> + - |
>>>>> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
>>>>> + #include <dt-bindings/gpio/gpio.h>
>>>>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>>>>> + #include <dt-bindings/interrupt-controller/irq.h>
>>>>> + #include <dt-bindings/power/rk3588-power.h>
>>>>> + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
>>>>> + hdmi_receiver: hdmi-receiver@fdee0000 {
>>
>>>>> + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
>>
>> compatible = "rockchip,rk3588-hdmirx-ctrler";
>>
>>>>> + reg = <0xfdee0000 0x6000>;
>>>>> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
>>>>> + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
>>>>> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
>>>>> + interrupt-names = "cec", "hdmi", "dma";
>>>>> + clocks = <&cru ACLK_HDMIRX>,
>>>>> + <&cru CLK_HDMIRX_AUD>,
>>>>> + <&cru CLK_CR_PARA>,
>>>>> + <&cru PCLK_HDMIRX>,
>>>>> + <&cru CLK_HDMIRX_REF>,
>>>>> + <&cru PCLK_S_HDMIRX>,
>>>>> + <&cru HCLK_VO1>;
>>>>> + clock-names = "aclk",
>>>>> + "audio",
>>>>> + "cr_para",
>>>>> + "pclk",
>>>>> + "ref",
>>>>> + "hclk_s_hdmirx",
>>>>> + "hclk_vo1";
>>>>> + power-domains = <&power RK3588_PD_VO1>;
>>>>> + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
>>>>> + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
>>>>> + reset-names = "axi", "apb", "ref", "biu";
>>>>> + memory-region = <&hdmi_receiver_cma>;
>>>>> + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
>>>>> + pinctrl-names = "default";
>>>>> + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
>>>>> + };
>>>
>> _______________________________________________
>> Kernel mailing list -- kernel@mailman.collabora.com
>> To unsubscribe send an email to kernel-leave@mailman.collabora.com
>> This list is managed by https://mailman.collabora.com
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-24 13:20 ` Johan Jonker
@ 2024-07-25 6:35 ` Krzysztof Kozlowski
2024-07-25 6:38 ` Krzysztof Kozlowski
2024-07-25 7:58 ` AngeloGioacchino Del Regno
2024-07-25 14:10 ` Sebastian Reichel
2 siblings, 1 reply; 40+ messages in thread
From: Krzysztof Kozlowski @ 2024-07-25 6:35 UTC (permalink / raw)
To: Johan Jonker, Sebastian Reichel
Cc: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco, kernel, linux-kernel,
linux-media, devicetree, linux-arm-kernel, linux-rockchip,
Dmitry Osipenko
On 24/07/2024 15:20, Johan Jonker wrote:
>
>>
>> Where did you learn that? Having non-SoC specific generic fallback
>> compatibles is pretty much standard throughout the kernel. See for
>> example these RK3588 DesignWare compatibles:
>>
>> Synopsys Serial Controller:
>> Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
>> compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
>
> Compatible method #2:
> { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
>
>>
>> Synopsys USB3 Controller:
>> Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
>> compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
>
> Compatible method #2:
> {
> .compatible = "snps,dwc3"
> },
>
>>
>> Synopsys Ethernet Controller:
>> Documentation/devicetree/bindings/net/snps,dwmac.yaml
>> compatible = "rockchip,rk3588-gmac", "snps,dwmac-4.20a";
>
> Compatible method #1:
> { .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops },
>
> of_device_is_compatible(np, "snps,dwmac-4.20a") ||
>
>>
>> Synsopsys SATA Controller:
>> Documentation/devicetree/bindings/ata/rockchip,dwc-ahci.yaml
>> compatible = "rockchip,rk3588-dwc-ahci", "snps,dwc-ahci"
>
> Compatible method #2:
> { .compatible = "snps,dwc-ahci", &ahci_dwc_plat },
>
>>
>> It's also not specific to Synopsys (but RK3588 has a lot of Synopsys
>> design incl. the HDMI-RX IP currently worked on by Shreeya). Here
>> are some other examples:
>>
>> ARM Mali GPU:
>> Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
>> compatible = "rockchip,rk3588-mali", "arm,mali-valhall-csf";
>
> Should be compatible method #2:
> { .compatible = "rockchip,rk3588-mali" },
> { .compatible = "arm,mali-valhall-csf" },
>
> This is wrong!
Except that it is pointless and redundant, why is it wrong? You did not
bring any argument, except "will trigger 2 probes" which is clearly false.
> Each strings will trigger a probe.
What? That's not true.
> The string "rockchip,rk3588-mali" should be removed.
>
> Review was done by Collabora people and without including the Rockchip mail list.
> https://patchwork.freedesktop.org/patch/msgid/20240229162230.2634044-12-boris.brezillon@collabora.com
>
> Could someone look at this and test.
No need, just read how device matching and probing works...
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-25 6:35 ` Krzysztof Kozlowski
@ 2024-07-25 6:38 ` Krzysztof Kozlowski
0 siblings, 0 replies; 40+ messages in thread
From: Krzysztof Kozlowski @ 2024-07-25 6:38 UTC (permalink / raw)
To: Johan Jonker, Sebastian Reichel
Cc: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco, kernel, linux-kernel,
linux-media, devicetree, linux-arm-kernel, linux-rockchip,
Dmitry Osipenko
On 25/07/2024 08:35, Krzysztof Kozlowski wrote:
> On 24/07/2024 15:20, Johan Jonker wrote:
>>
>>>
>>> Where did you learn that? Having non-SoC specific generic fallback
>>> compatibles is pretty much standard throughout the kernel. See for
>>> example these RK3588 DesignWare compatibles:
>>>
>>> Synopsys Serial Controller:
>>> Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
>>> compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
>>
>> Compatible method #2:
>> { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
>>
>>>
>>> Synopsys USB3 Controller:
>>> Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
>>> compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
>>
>> Compatible method #2:
>> {
>> .compatible = "snps,dwc3"
>> },
>>
>>>
>>> Synopsys Ethernet Controller:
>>> Documentation/devicetree/bindings/net/snps,dwmac.yaml
>>> compatible = "rockchip,rk3588-gmac", "snps,dwmac-4.20a";
>>
>> Compatible method #1:
>> { .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops },
>>
>> of_device_is_compatible(np, "snps,dwmac-4.20a") ||
>>
>>>
>>> Synsopsys SATA Controller:
>>> Documentation/devicetree/bindings/ata/rockchip,dwc-ahci.yaml
>>> compatible = "rockchip,rk3588-dwc-ahci", "snps,dwc-ahci"
>>
>> Compatible method #2:
>> { .compatible = "snps,dwc-ahci", &ahci_dwc_plat },
>>
>>>
>>> It's also not specific to Synopsys (but RK3588 has a lot of Synopsys
>>> design incl. the HDMI-RX IP currently worked on by Shreeya). Here
>>> are some other examples:
>>>
>>> ARM Mali GPU:
>>> Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
>>> compatible = "rockchip,rk3588-mali", "arm,mali-valhall-csf";
>>
>> Should be compatible method #2:
>> { .compatible = "rockchip,rk3588-mali" },
>> { .compatible = "arm,mali-valhall-csf" },
>>
>> This is wrong!
>
> Except that it is pointless and redundant, why is it wrong? You did not
> bring any argument, except "will trigger 2 probes" which is clearly false.
>
>> Each strings will trigger a probe.
>
> What? That's not true.
Although if you meant "any string will trigger one probe in total", then
it would be true, so maybe that's what you meant.
But then - what's wrong with this (except needless redundancy)? You did
not bring any argument but keep calling more than once "wrong". So what
is wrong?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-24 13:20 ` Johan Jonker
2024-07-25 6:35 ` Krzysztof Kozlowski
@ 2024-07-25 7:58 ` AngeloGioacchino Del Regno
2024-07-25 14:10 ` Sebastian Reichel
2 siblings, 0 replies; 40+ messages in thread
From: AngeloGioacchino Del Regno @ 2024-07-25 7:58 UTC (permalink / raw)
To: Johan Jonker, Sebastian Reichel
Cc: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco, kernel, linux-kernel,
linux-media, devicetree, linux-arm-kernel, linux-rockchip,
Dmitry Osipenko
Il 24/07/24 15:20, Johan Jonker ha scritto:
>
>
> On 7/23/24 19:28, Sebastian Reichel wrote:
>> Hi,
>>
>> On Tue, Jul 23, 2024 at 01:16:00PM GMT, Johan Jonker wrote:
>>> On 7/22/24 15:53, Shreeya Patel wrote:
>>>> On Saturday, July 20, 2024 16:14 IST, Johan Jonker <jbx6244@yandex.com> wrote:
>>>>> On 7/19/24 14:40, Shreeya Patel wrote:
>>>>>> Document bindings for the Synopsys DesignWare HDMI RX Controller.
>>>>>>
>>>
>>>>>> Reviewed-by: Rob Herring <robh@kernel.org>
>>>>>> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
>>>
>>> Remove to trigger a new review.
>>
>> Rob and Dmitry both already reviewed the version with the fallback
>> compatible. I don't think the rename of hdmirx_cma to hdmi_receiver_cma
>> warrant a new review. Also FWIW:
>>
>
>> Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
>
> Please have a look at the comments below before you tag.
>
I have checked the (mostly wrong) comments before tagging.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Regards,
Angelo
>>
>>>>>> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
>>>>>> ---
>>>>>>
>>>>>> Changes in v4 :-
>>>>>> - No change
>>>>>>
>>>>>> Changes in v3 :-
>>>>>> - Rename hdmirx_cma to hdmi_receiver_cma
>>>>>> - Add a Reviewed-by tag
>>>>>>
>>>>>> Changes in v2 :-
>>>>>> - Add a description for the hardware
>>>>>> - Rename resets, vo1 grf and HPD properties
>>>>>> - Add a proper description for grf and vo1-grf phandles
>>>>>> - Rename the HDMI Input node name to hdmi-receiver
>>>>>> - Improve the subject line
>>>>>> - Include gpio header file in example to fix dt_binding_check failure
>>>>>>
>>>>>> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
>>>>>> 1 file changed, 132 insertions(+)
>>>>>> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>>>>
>>>>>> diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>>>> new file mode 100644
>>>>>> index 000000000000..96ae1e2d2816
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
>>>>>> @@ -0,0 +1,132 @@
>>>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>>>> +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
>>>>>> +
>>>>>> +---
>>>>>> +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
>>>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>>>> +
>>>>>> +title: Synopsys DesignWare HDMI RX Controller
>>>>>> +
>>>>>> +maintainers:
>>>>>> + - Shreeya Patel <shreeya.patel@collabora.com>
>>>>>> +
>>>>>> +description:
>>>>>> + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
>>>>>> + allowing devices to receive and decode high-resolution video streams
>>>>>> + from external sources like media players, cameras, laptops, etc.
>>>>>> +
>>>>>> +properties:
>>>>>> + compatible:
>>>>>> + items:
>>>>>> + - const: rockchip,rk3588-hdmirx-ctrler
>>>>>
>>>
>>>>>> + - const: snps,dw-hdmi-rx
>>>
>>> remove
>>>
>>>>>
>
> Relevant compatible methods in use for Rockchip drivers:
>
> ===================================================================================================
>
> Compatible method #1:
> Probe is triggered by a SoC orientated string.
>
> compatible = "rockchip,rk3588-hdmirx-ctrler";
>
> If for example a new SoC rk3599 is released that has the same device properties
> then the old string can be used as fallback string.
>
> compatible = ""rockchip,rk3599-hdmirx-ctrler" , "rockchip,rk3588-hdmirx-ctrler";
>
> The driver structure:
> { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
>
> ===================================================================================================
> Compatible method #2:
> Probe is triggered by a IP orientated fallback string.
>
> compatible = "rockchip,rk3588-hdmirx-ctrler" , "snps,dw-hdmi-rx";
>
> If for example a new SoC rk3599 is released that has the same device properties
> then add the same fallback string.
>
> compatible = ""rockchip,rk3599-hdmirx-ctrler" , "snps,dw-hdmi-rx";
>
> The driver structure:
> { .compatible = "snps,dw-hdmi-rx" },
>
> If for example a new SoC rk3599 is released that has NOT the same device properties
> then use method #1.
>
> The driver structure:
> { .compatible = "rockchip,rk3599-hdmirx-ctrler" .data = &rk3599_ops },
> { .compatible = "snps,dw-hdmi-rx" },
>
> ===================================================================================================
>
> Compatible method #3:
> Probe is triggered by a vendor orientated fallback string.
>
> Special case only useful if the driver is written long after all SoCs are released.
> The standalone IP has a version register and the driver can handle all the feature difference
> inside the IP depending on the version register.
>
> compatible = "rockchip,sfc";
>
> The driver structure:
> { .compatible = "rockchip,sfc"},
>
> ===================================================================================================
>
> The rules:
>
> 1: Compatible strings must be SoC orientated.
> 2: In Linux there's no priority in which string will probed first.
> 3: There is a commitment that old DT's should still work with newer kernels.
>
>>>>> What's the point of having a fallback string when there's no common code, but instead only the first string is used?
>>>>>
>>>>> +static const struct of_device_id hdmirx_id[] = {
>>>>> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
>>>>> + { },
>>>>> +};
>>>>>
>
> The consequence of the third rule is that drivers must continue to support this string once added and
> can not be removed as suggested below.
>
> If for example the fallback is added later it will trigger 2 probes and it breaks rule #2.
> Only one of string is allowed to trigger a probe in the driver.
>
> This is wrong:
> compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
>
> { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> { .compatible = "snps,dw-hdmi-rx" },
>
> Ones a compatible method is chosen the driver must stick to it.
>
> ===================================================================================================
>
>>>>
>>>
>>>> We believe the HDMIRX driver can be used for the Synopsys IP on other SoCs
>>>> in the future, which is why we have added snps,dw-hdmi-rx as the fallback compatible.
>>>> Currently, we have tested the driver only on the RK3588 Rock5B, so we are using the
>>>> rockchip,rk3588-hdmirx-ctrler compatible in the driver instead of the fallback one.
>>>
>>> The rule that compatible strings (for internal SoC components)
>>> must be SoC orientated also applies to the fallback string.
>>> "snps,xxxx" does not refer to an independent SoC.
>
> This refers to compatible method #1.
>
>>
>> Where did you learn that? Having non-SoC specific generic fallback
>> compatibles is pretty much standard throughout the kernel. See for
>> example these RK3588 DesignWare compatibles:
>>
>> Synopsys Serial Controller:
>> Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
>> compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";
>
> Compatible method #2:
> { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
>
>>
>> Synopsys USB3 Controller:
>> Documentation/devicetree/bindings/usb/rockchip,dwc3.yaml
>> compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
>
> Compatible method #2:
> {
> .compatible = "snps,dwc3"
> },
>
>>
>> Synopsys Ethernet Controller:
>> Documentation/devicetree/bindings/net/snps,dwmac.yaml
>> compatible = "rockchip,rk3588-gmac", "snps,dwmac-4.20a";
>
> Compatible method #1:
> { .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops },
>
> of_device_is_compatible(np, "snps,dwmac-4.20a") ||
>
>>
>> Synsopsys SATA Controller:
>> Documentation/devicetree/bindings/ata/rockchip,dwc-ahci.yaml
>> compatible = "rockchip,rk3588-dwc-ahci", "snps,dwc-ahci"
>
> Compatible method #2:
> { .compatible = "snps,dwc-ahci", &ahci_dwc_plat },
>
>>
>> It's also not specific to Synopsys (but RK3588 has a lot of Synopsys
>> design incl. the HDMI-RX IP currently worked on by Shreeya). Here
>> are some other examples:
>>
>> ARM Mali GPU:
>> Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
>> compatible = "rockchip,rk3588-mali", "arm,mali-valhall-csf";
>
> Should be compatible method #2:
> { .compatible = "rockchip,rk3588-mali" },
> { .compatible = "arm,mali-valhall-csf" },
>
> This is wrong!
> Each strings will trigger a probe.
> The string "rockchip,rk3588-mali" should be removed.
>
> Review was done by Collabora people and without including the Rockchip mail list.
> https://patchwork.freedesktop.org/patch/msgid/20240229162230.2634044-12-boris.brezillon@collabora.com
>
> Could someone look at this and test.
>
>>
>> Generic EHCI:
>> Documentation/devicetree/bindings/usb/generic-ehci.yaml
>> compatible = "rockchip,rk3588-ehci", "generic-ehci";
>
> compatible method #2:
> { .compatible = "generic-ehci", },
>
>>
>> As you can see almost everything in RK3588 has a non SoC specific
>> fallback :) It's also not a Rockchip/RK3588 specific thing, but
>> I think you should be able to find enough references yourself by
>> looking into the kernel's DTS files.
>
> You are mixing up 2 compatible methods.
> The driver has compatible method #1 and the DT has method #2.
>
>>
>>> Don't invent strings for devices that we don't know yet if it
>>> might or might not be compatible in the future.
>>
>> Right now it's a sensible assumption, that an operating system driver
>> for this hardware (i.e. not necessarily the one submitted by Shreeya
>> right now) can handle the Synopsys HDMI receiver hardware from different
>> SoCs just like it is the case for other Synopsys IP.
>>
>> Whatever is being done now is set in stone, since DT is considered
>> ABI. So without the fallback compatible being available in DT from
>> the beginning we need to carry the RK3588 specific compatible in the
>
>> kernel driver forever. OTOH if we add the generic one now, the kernel
>> can switch to use the generic one at any point in time and ignore the
>> RK3588 specific one.
>
> Ignoring breaks rule #3 as explained above.
>
> For you the task to select a compatible method:
>
> If the IP device registers are guaranteed remain the same then choose compatible method #2 and fix the driver.
> If in doubt choose compatible method #1 and fix the binding.
>
> Johan
>
>>
>> Greetings,
>>
>> -- Sebastian
>>
>>> Johan
>>>
>>>>
>>>>
>>>> Thanks,
>>>> Shreeya Patel
>>>>
>>>>>> +
>>>>>> + reg:
>>>>>> + maxItems: 1
>>>>>> +
>>>>>> + interrupts:
>>>>>> + maxItems: 3
>>>>>> +
>>>>>> + interrupt-names:
>>>>>> + items:
>>>>>> + - const: cec
>>>>>> + - const: hdmi
>>>>>> + - const: dma
>>>>>> +
>>>>>> + clocks:
>>>>>> + maxItems: 7
>>>>>> +
>>>>>> + clock-names:
>>>>>> + items:
>>>>>> + - const: aclk
>>>>>> + - const: audio
>>>>>> + - const: cr_para
>>>>>> + - const: pclk
>>>>>> + - const: ref
>>>>>> + - const: hclk_s_hdmirx
>>>>>> + - const: hclk_vo1
>>>>>> +
>>>>>> + power-domains:
>>>>>> + maxItems: 1
>>>>>> +
>>>>>> + resets:
>>>>>> + maxItems: 4
>>>>>> +
>>>>>> + reset-names:
>>>>>> + items:
>>>>>> + - const: axi
>>>>>> + - const: apb
>>>>>> + - const: ref
>>>>>> + - const: biu
>>>>>> +
>>>>>> + memory-region:
>>>>>> + maxItems: 1
>>>>>> +
>>>>>> + hpd-gpios:
>>>>>> + description: GPIO specifier for HPD.
>>>>>> + maxItems: 1
>>>>>> +
>>>>>> + rockchip,grf:
>>>>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>>>>> + description:
>>>>>> + The phandle of the syscon node for the general register file
>>>>>> + containing HDMIRX PHY status bits.
>>>>>> +
>>>>>> + rockchip,vo1-grf:
>>>>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>>>>> + description:
>>>>>> + The phandle of the syscon node for the Video Output GRF register
>>>>>> + to enable EDID transfer through SDAIN and SCLIN.
>>>>>> +
>>>>>> +required:
>>>>>> + - compatible
>>>>>> + - reg
>>>>>> + - interrupts
>>>>>> + - interrupt-names
>>>>>> + - clocks
>>>>>> + - clock-names
>>>>>> + - power-domains
>>>>>> + - resets
>>>>>> + - pinctrl-0
>>>>>> + - hpd-gpios
>>>>>> +
>>>>>> +additionalProperties: false
>>>>>> +
>>>>>> +examples:
>>>>>> + - |
>>>>>> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
>>>>>> + #include <dt-bindings/gpio/gpio.h>
>>>>>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>>>>>> + #include <dt-bindings/interrupt-controller/irq.h>
>>>>>> + #include <dt-bindings/power/rk3588-power.h>
>>>>>> + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
>>>>>> + hdmi_receiver: hdmi-receiver@fdee0000 {
>>>
>>>>>> + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
>>>
>>> compatible = "rockchip,rk3588-hdmirx-ctrler";
>>>
>>>>>> + reg = <0xfdee0000 0x6000>;
>>>>>> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
>>>>>> + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
>>>>>> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
>>>>>> + interrupt-names = "cec", "hdmi", "dma";
>>>>>> + clocks = <&cru ACLK_HDMIRX>,
>>>>>> + <&cru CLK_HDMIRX_AUD>,
>>>>>> + <&cru CLK_CR_PARA>,
>>>>>> + <&cru PCLK_HDMIRX>,
>>>>>> + <&cru CLK_HDMIRX_REF>,
>>>>>> + <&cru PCLK_S_HDMIRX>,
>>>>>> + <&cru HCLK_VO1>;
>>>>>> + clock-names = "aclk",
>>>>>> + "audio",
>>>>>> + "cr_para",
>>>>>> + "pclk",
>>>>>> + "ref",
>>>>>> + "hclk_s_hdmirx",
>>>>>> + "hclk_vo1";
>>>>>> + power-domains = <&power RK3588_PD_VO1>;
>>>>>> + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
>>>>>> + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
>>>>>> + reset-names = "axi", "apb", "ref", "biu";
>>>>>> + memory-region = <&hdmi_receiver_cma>;
>>>>>> + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
>>>>>> + pinctrl-names = "default";
>>>>>> + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
>>>>>> + };
>>>>
>>> _______________________________________________
>>> Kernel mailing list -- kernel@mailman.collabora.com
>>> To unsubscribe send an email to kernel-leave@mailman.collabora.com
>>> This list is managed by https://mailman.collabora.com
> _______________________________________________
> Kernel mailing list -- kernel@mailman.collabora.com
> To unsubscribe send an email to kernel-leave@mailman.collabora.com
> This list is managed by https://mailman.collabora.com
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-19 22:10 ` Rob Herring (Arm)
@ 2024-07-25 9:46 ` Shreeya Patel
0 siblings, 0 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-25 9:46 UTC (permalink / raw)
To: Rob Herring (Arm)
Cc: kernel, mchehab, conor+dt, linux-media, nelson.costa,
linux-arm-kernel, heiko, mturquette, hverkuil, hverkuil-cisco,
linux-rockchip, shawn.wen, sboyd, Dmitry Osipenko, p.zabel,
jose.abreu, linux-kernel, krzk+dt, devicetree, nicolas.dufresne
On Saturday, July 20, 2024 03:40 IST, "Rob Herring (Arm)" <robh@kernel.org> wrote:
>
> On Fri, 19 Jul 2024 18:10:30 +0530, Shreeya Patel wrote:
> > Document bindings for the Synopsys DesignWare HDMI RX Controller.
> >
> > Reviewed-by: Rob Herring <robh@kernel.org>
> > Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> > Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> > ---
> >
> > Changes in v4 :-
> > - No change
> >
> > Changes in v3 :-
> > - Rename hdmirx_cma to hdmi_receiver_cma
> > - Add a Reviewed-by tag
> >
> > Changes in v2 :-
> > - Add a description for the hardware
> > - Rename resets, vo1 grf and HPD properties
> > - Add a proper description for grf and vo1-grf phandles
> > - Rename the HDMI Input node name to hdmi-receiver
> > - Improve the subject line
> > - Include gpio header file in example to fix dt_binding_check failure
> >
> > .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
> > 1 file changed, 132 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >
>
> My bot found errors running 'make dt_binding_check' on your patch:
>
> yamllint warnings/errors:
>
> dtschema/dtc warnings/errors:
> Error: Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.example.dts:53.38-39 syntax error
> FATAL ERROR: Unable to parse input tree
> make[2]: *** [scripts/Makefile.lib:427: Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.example.dtb] Error 1
> make[2]: *** Waiting for unfinished jobs....
> make[1]: *** [/builds/robherring/dt-review-ci/linux/Makefile:1430: dt_binding_check] Error 2
> make: *** [Makefile:240: __sub-make] Error 2
>
> doc reference errors (make refcheckdocs):
>
> See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240719124032.26852-3-shreeya.patel@collabora.com
>
> The base for the series is generally the latest rc1. A different dependency
> should be noted in *this* patch.
>
My HDMI RX patches are based on the linux-next/master branch.
Since the bot tested the patches on top of rc1, it resulted in some errors
due to missing reset ID patches.
I think the above statement means I should explicitly mention in this
patch that it is based on linux-next/master (something to keep in mind
for future :)
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
>
> pip3 install dtschema --upgrade
>
> Please check and re-submit after running the above command yourself. Note
> that DT_SCHEMA_FILES can be set to your schema file to speed up checking
> your schema. However, it must be unset to test all examples with your schema.
>
> _______________________________________________
> Kernel mailing list -- kernel@mailman.collabora.com
> To unsubscribe send an email to kernel-leave@mailman.collabora.com
> This list is managed by https://mailman.collabora.com
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-21 8:51 ` Hans Verkuil
@ 2024-07-25 9:56 ` Shreeya Patel
0 siblings, 0 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-07-25 9:56 UTC (permalink / raw)
To: Hans Verkuil
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil-cisco, kernel, linux-kernel, linux-media, devicetree,
linux-arm-kernel, linux-rockchip, Dmitry Osipenko
On Sunday, July 21, 2024 14:21 IST, Hans Verkuil <hverkuil@xs4all.nl> wrote:
Hi Hans,
> On 19/07/2024 14:40, Shreeya Patel wrote:
> > Add initial support for the Synopsys DesignWare HDMI RX
> > Controller Driver used by Rockchip RK3588. The driver
> > supports:
> > - HDMI 1.4b and 2.0 modes (HDMI 4k@60Hz)
> > - RGB888, YUV422, YUV444 and YCC420 pixel formats
> > - CEC
> > - EDID configuration
> >
> > The hardware also has Audio and HDCP capabilities, but these are
> > not yet supported by the driver.
>
> FYI: if you want to add HDCP support, then please contact me. I have code
> available for that (i.e. the public API part), although it is out of date.
>
> But if you want to enable HDCP in this driver, then I would be very happy
> to clean it up and post patches for that. It is something we (i.e. Cisco)
> have been using for several years now in out-of-tree drivers.
>
Currently we do not have any plans to work on the HDCP support.
But in case the requirement arises in future, we will definitely reach
out to you.
Thanks,
Shreeya Patel
> I will try to review this patch in the next few days.
>
> Regards,
>
> Hans
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-24 13:20 ` Johan Jonker
2024-07-25 6:35 ` Krzysztof Kozlowski
2024-07-25 7:58 ` AngeloGioacchino Del Regno
@ 2024-07-25 14:10 ` Sebastian Reichel
2 siblings, 0 replies; 40+ messages in thread
From: Sebastian Reichel @ 2024-07-25 14:10 UTC (permalink / raw)
To: Johan Jonker
Cc: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco, kernel, linux-kernel,
linux-media, devicetree, linux-arm-kernel, linux-rockchip,
Dmitry Osipenko
[-- Attachment #1: Type: text/plain, Size: 10759 bytes --]
Hi,
On Wed, Jul 24, 2024 at 03:20:28PM GMT, Johan Jonker wrote:
>
>
> On 7/23/24 19:28, Sebastian Reichel wrote:
> > Hi,
> >
> > On Tue, Jul 23, 2024 at 01:16:00PM GMT, Johan Jonker wrote:
> >> On 7/22/24 15:53, Shreeya Patel wrote:
> >>> On Saturday, July 20, 2024 16:14 IST, Johan Jonker <jbx6244@yandex.com> wrote:
> >>>> On 7/19/24 14:40, Shreeya Patel wrote:
> >>>>> Document bindings for the Synopsys DesignWare HDMI RX Controller.
> >>>>>
> >>
> >>>>> Reviewed-by: Rob Herring <robh@kernel.org>
> >>>>> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> >>
> >> Remove to trigger a new review.
> >
> > Rob and Dmitry both already reviewed the version with the fallback
> > compatible. I don't think the rename of hdmirx_cma to hdmi_receiver_cma
> > warrant a new review. Also FWIW:
> >
>
> > Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
>
> Please have a look at the comments below before you tag.
>
> >
> >>>>> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
> >>>>> ---
> >>>>>
> >>>>> Changes in v4 :-
> >>>>> - No change
> >>>>>
> >>>>> Changes in v3 :-
> >>>>> - Rename hdmirx_cma to hdmi_receiver_cma
> >>>>> - Add a Reviewed-by tag
> >>>>>
> >>>>> Changes in v2 :-
> >>>>> - Add a description for the hardware
> >>>>> - Rename resets, vo1 grf and HPD properties
> >>>>> - Add a proper description for grf and vo1-grf phandles
> >>>>> - Rename the HDMI Input node name to hdmi-receiver
> >>>>> - Improve the subject line
> >>>>> - Include gpio header file in example to fix dt_binding_check failure
> >>>>>
> >>>>> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 ++++++++++++++++++
> >>>>> 1 file changed, 132 insertions(+)
> >>>>> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >>>>>
> >>>>> diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >>>>> new file mode 100644
> >>>>> index 000000000000..96ae1e2d2816
> >>>>> --- /dev/null
> >>>>> +++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> >>>>> @@ -0,0 +1,132 @@
> >>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> >>>>> +# Device Tree bindings for Synopsys DesignWare HDMI RX Controller
> >>>>> +
> >>>>> +---
> >>>>> +$id: http://devicetree.org/schemas/media/snps,dw-hdmi-rx.yaml#
> >>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> >>>>> +
> >>>>> +title: Synopsys DesignWare HDMI RX Controller
> >>>>> +
> >>>>> +maintainers:
> >>>>> + - Shreeya Patel <shreeya.patel@collabora.com>
> >>>>> +
> >>>>> +description:
> >>>>> + Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
> >>>>> + allowing devices to receive and decode high-resolution video streams
> >>>>> + from external sources like media players, cameras, laptops, etc.
> >>>>> +
> >>>>> +properties:
> >>>>> + compatible:
> >>>>> + items:
> >>>>> + - const: rockchip,rk3588-hdmirx-ctrler
> >>>>
> >>
> >>>>> + - const: snps,dw-hdmi-rx
> >>
> >> remove
> >>
> >>>>
>
> Relevant compatible methods in use for Rockchip drivers:
You are arguing with kernel drivers. Drivers can be changed at any
point in time, but DT bindings cannot, because they define an ABI.
> ===================================================================================================
>
> Compatible method #1:
> Probe is triggered by a SoC orientated string.
>
> compatible = "rockchip,rk3588-hdmirx-ctrler";
>
> If for example a new SoC rk3599 is released that has the same device properties
> then the old string can be used as fallback string.
>
> compatible = ""rockchip,rk3599-hdmirx-ctrler" , "rockchip,rk3588-hdmirx-ctrler";
>
> The driver structure:
> { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
>
> ===================================================================================================
> Compatible method #2:
> Probe is triggered by a IP orientated fallback string.
>
> compatible = "rockchip,rk3588-hdmirx-ctrler" , "snps,dw-hdmi-rx";
>
> If for example a new SoC rk3599 is released that has the same device properties
> then add the same fallback string.
>
> compatible = ""rockchip,rk3599-hdmirx-ctrler" , "snps,dw-hdmi-rx";
>
> The driver structure:
> { .compatible = "snps,dw-hdmi-rx" },
>
> If for example a new SoC rk3599 is released that has NOT the same device properties
> then use method #1.
>
> The driver structure:
> { .compatible = "rockchip,rk3599-hdmirx-ctrler" .data = &rk3599_ops },
> { .compatible = "snps,dw-hdmi-rx" },
This is what is being used here. The only diference is, that the
driver currently uses the RK3588 specific compatible string instead
of the fallback string right now.
If another SoC vendor adds the same IP into their latest chip and
the driver has been proven to work with their hardware the driver
can be changed to bind against "snps,dw-hdmi-rx" instead of the
RK3588 specific compatible. Doing this change will keep
compatibility with existing DTs, if we add the fallback string now.
Until then we just carry it as an unused fallback.
> ===================================================================================================
>
> Compatible method #3:
> Probe is triggered by a vendor orientated fallback string.
>
> Special case only useful if the driver is written long after all SoCs are released.
> The standalone IP has a version register and the driver can handle all the feature difference
> inside the IP depending on the version register.
>
> compatible = "rockchip,sfc";
>
> The driver structure:
> { .compatible = "rockchip,sfc"},
FWIW I think _this_ is a bad example. It is missing the SoC specific
compatible making applying of quirks harder than necessary. Just
because no quirks are needed now, does not mean it will stay that
way. E.g. if an Errata gets released that SFC on RK3588 must not be
run at 1 MHz it would be super useful to have an "rockchip,rk3588-sfc"
to match against. That's the reason for the rule #1 in the following
list
> ===================================================================================================
>
> The rules:
>
> 1: Compatible strings must be SoC orientated.
> 2: In Linux there's no priority in which string will probed first.
I initially thought you mean the list in the driver, but after
reading your remaining mail - this is just wrong. There is a
priority. If DT specifies
compatible = "main", "fallback";
Linux will first try to bind against "main". If that does not
work it will try to bind against "fallback". To give you a
simple example from the subsystem I maintain:
DT binding: Documentation/devicetree/bindings/power/supply/sbs,sbs-battery.yaml
Linux Kernel Driver: drivers/power/supply/sbs-battery.c
Valid compatibles according to the DT binding:
compatible = "ti,bq20z45", "sbs,sbs-battery";
compatible = "ti,bq20z65", "sbs,sbs-battery";
compatible = "ti,bq20z75", "sbs,sbs-battery";
compatible = "sbs,sbs-battery";
The driver has these of_device_id entries:
{ .compatible = "sbs,sbs-battery" },
{ .compatible = "ti,bq20z65", QUIRKS },
{ .compatible = "ti,bq20z75", QUIRKS },
The driver will probe ONCE in all cases.
compatible = "ti,bq20z45", "sbs,sbs-battery"; => probe happens with fallback string
compatible = "ti,bq20z65", "sbs,sbs-battery"; => probe happens with main string (QUIRKS apply)
compatible = "ti,bq20z75", "sbs,sbs-battery"; => probe happens with main string (QUIRKS apply)
compatible = "sbs,sbs-battery"; => probe happens with main string
> 3: There is a commitment that old DT's should still work with newer kernels.
> >>>> What's the point of having a fallback string when there's no common code, but instead only the first string is used?
> >>>>
> >>>> +static const struct of_device_id hdmirx_id[] = {
> >>>> + { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> >>>> + { },
> >>>> +};
> >>>>
>
> The consequence of the third rule is that drivers must continue to
> support this string once added and can not be removed as suggested
> below.
That's wrong. We can remove the "rockchip,rk3588-hdmirx-ctrler" from
the kernel driver and use the fallback string at any point in time
__IF__ we make it mandatory that rockchip,rk3588-hdmirx-ctrler must
always be followed by the fallback compatible in DT. Because then we
will keep working with old DTs, since the old DT also has the
fallback compatible.
> If for example the fallback is added later it will trigger 2 probes and it breaks rule #2.
> Only one of string is allowed to trigger a probe in the driver.
>
> This is wrong:
> compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
>
> { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
> { .compatible = "snps,dw-hdmi-rx" },
>
> Ones a compatible method is chosen the driver must stick to it.
I don't know how you came to that conclusion, but it's simply wrong.
The above example will probe once using the "rockchip,rk3588-hdmirx-ctrler"
compatible. At that point the DT node is marked as processed, so
no other probe happens.
> ===================================================================================================
>
> >>>
> >>
> >>> We believe the HDMIRX driver can be used for the Synopsys IP on other SoCs
> >>> in the future, which is why we have added snps,dw-hdmi-rx as the fallback compatible.
> >>> Currently, we have tested the driver only on the RK3588 Rock5B, so we are using the
> >>> rockchip,rk3588-hdmirx-ctrler compatible in the driver instead of the fallback one.
> >>
> >> The rule that compatible strings (for internal SoC components)
> >> must be SoC orientated also applies to the fallback string.
> >> "snps,xxxx" does not refer to an independent SoC.
>
> This refers to compatible method #1.
Yeah, which is used when the IP is from the SoC vendor itself (or
unknown) or if its not possible to use the generic IP compatible
anyways.
> [...]
> If the IP device registers are guaranteed remain the same then
> choose compatible method #2 and fix the driver.
Adapting the driver is the plan, but it cannot be done right now
because lack of information. This requires either another SoC with
this IP or at least the Synopsys documentation. We only have the
Rockchip documentation.
But the driver can be fixed in the future and the DT binding is
ABI. Thus the DT binding is prepared now to allow the driver looking
like your method #2 in the future. Until then the extra compatible
will just be ignored.
-- Sebastian
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
` (4 preceding siblings ...)
2024-07-22 19:39 ` hoff.benjamin.k
@ 2024-08-03 23:57 ` Tim Surber
2024-08-05 17:02 ` Nicolas Dufresne
` (2 more replies)
5 siblings, 3 replies; 40+ messages in thread
From: Tim Surber @ 2024-08-03 23:57 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
Hi Shreeya,
I tested your patch and noticed problems when using 3840x2160 resolution
at 60fps.
For my testing I connected an HDMI source and set it to 4k60fps. I
verified that this source and the cables work on a screen at this
resolution.
Using
'v4l2-ctl --verbose -d /dev/video1
--set-fmt-video=width=3840,height=2160,pixelformat='NV12'
--stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
I get the video format output, but not the periodic output which shows
the fps.
Using
'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
I get the following error message:
(gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
gst_memory_resize: assertion 'size + mem->offset + offset <=
mem->maxsize' failed
0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:<v4l2src0:pool0:src>
Dropping truncated buffer, this is likely a driver bug.
0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
gstbufferpool.c:1252:default_reset_buffer:<v4l2src0:pool0:src> Buffer
0xffff98008e80 without the memory tag has maxsize (8294400) that is
smaller than the configured buffer pool size (12441600). The buffer will
be not be reused. This is most likely a bug in this GstBufferPool subclass
Everything works with 4k30fps or 1080p 60fps. The hardware should
support 4k60fps.
Best regards,
Tim
On 19.07.24 14:40, Shreeya Patel wrote:
> This series implements support for the Synopsys DesignWare
> HDMI RX Controller, being compliant with standard HDMI 1.4b
> and HDMI 2.0.
>
> Features that are currently supported by the HDMI RX driver
> have been tested on rock5b board using a HDMI to micro-HDMI cable.
> It is recommended to use a good quality cable as there were
> multiple issues seen during testing the driver.
>
> Please note the below information :-
> * While testing the driver on rock5b we noticed that the binary BL31
> from Rockchip contains some unknown code to get the HDMI-RX PHY
> access working without any errors.
> With TF-A BL31, the HDMI-RX PHY also works fine but there were no
> interrupts seen for rk_hdmirx-hdmi leading to some errors when
> loading the driver [0]. It doesn't affect the functionality of the
> driver though.
> * We have tested the working of OBS studio with HDMIRX driver and
> there were no issues seen.
> * We also tested and verified the support for interlaced video.
>
> [0] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/trusted-firmware-a/-/issues/1
>
> To test the HDMI RX Controller driver, following example commands can be used :-
>
> root@debian-rockchip-rock5b-rk3588:~# v4l2-ctl --verbose -d /dev/video0 \
> --set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 \
> --stream-skip=3 --stream-count=100 --stream-to=/home/hdmiin4k.raw --stream-poll
>
> root@debian-rockchip-rock5b-rk3588:~# ffmpeg -f rawvideo -vcodec rawvideo \
> -s 1920x1080 -r 60 -pix_fmt bgr24 -i /home/hdmiin4k.raw output.mkv
>
> CEC compliance test results :-
>
> * https://gitlab.collabora.com/-/snippets/381
> * https://gitlab.collabora.com/-/snippets/380
>
> Following is the v4l2-compliance test result :-
>
> root@debian-rockchip-rock5b-rk3588:~# v4l2-compliance -d /dev/video0
> v4l2-compliance 1.27.0-5220, 64 bits, 64-bit time_t
> v4l2-compliance SHA: 8387e3673837 2024-07-01 11:09:32
>
> Compliance test for snps_hdmirx device /dev/video0:
>
> Driver Info:
> Driver name : snps_hdmirx
> Card type : snps_hdmirx
> Bus info : platform:fdee0000.hdmi-receiver
> Driver version : 6.10.0
> Capabilities : 0x84201000
> Video Capture Multiplanar
> Streaming
> Extended Pix Format
> Device Capabilities
> Device Caps : 0x04201000
> Video Capture Multiplanar
> Streaming
> Extended Pix Format
>
> Required ioctls:
> test VIDIOC_QUERYCAP: OK
> test invalid ioctls: OK
>
> Allow for multiple opens:
> test second /dev/video0 open: OK
> test VIDIOC_QUERYCAP: OK
> test VIDIOC_G/S_PRIORITY: OK
> test for unlimited opens: OK
>
> Debug ioctls:
> test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> test VIDIOC_LOG_STATUS: OK
>
> Input ioctls:
> test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> test VIDIOC_ENUMAUDIO: OK (Not Supported)
> test VIDIOC_G/S/ENUMINPUT: OK
> test VIDIOC_G/S_AUDIO: OK (Not Supported)
> Inputs: 1 Audio Inputs: 0 Tuners: 0
>
> Output ioctls:
> test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> Outputs: 0 Audio Outputs: 0 Modulators: 0
>
> Input/Output configuration ioctls:
> test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
> test VIDIOC_DV_TIMINGS_CAP: OK
> test VIDIOC_G/S_EDID: OK
>
> Control ioctls (Input 0):
> test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> test VIDIOC_QUERYCTRL: OK
> test VIDIOC_G/S_CTRL: OK
> test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> Standard Controls: 3 Private Controls: 0
>
> Format ioctls (Input 0):
> test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> test VIDIOC_G/S_PARM: OK
> test VIDIOC_G_FBUF: OK (Not Supported)
> test VIDIOC_G_FMT: OK
> test VIDIOC_TRY_FMT: OK
> test VIDIOC_S_FMT: OK
> test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> test Cropping: OK (Not Supported)
> test Composing: OK (Not Supported)
> test Scaling: OK (Not Supported)
>
> Codec ioctls (Input 0):
> test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
>
> Buffer ioctls (Input 0):
> test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> test CREATE_BUFS maximum buffers: OK
> test VIDIOC_REMOVE_BUFS: OK
> test VIDIOC_EXPBUF: OK
> test Requests: OK (Not Supported)
>
> Total for snps_hdmirx device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
>
> Changes in v4 :-
> - Remove DTS changes included in the device tree patch
> - Remove the hdmi rx pin info as it's already present
> in the rk3588-base-pinctrl.dtsi
> - Create a separate config option for selecting the EDID
> and enable it by default
> - Improve the comment related to DV timings and move it
> to the side of hdmirx_get_detected_timings
> - Add 100ms delay before pulling the HPD high
> - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> - Drop the bus info from hdmirx_querycap
> - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> - Set queue->min_queued_buffers to 1
> - Drop q->allow_cache_hints = 0; as it's always 0 by default
> - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> - Drop .read = vb2_fop_read as it's not supported by driver
> - Remove redundant edid_init_data_600M
> - Make HPD low when driver is loaded
> - Add support for reading AVI Infoframe
> - Remove msg_len checks from hdmirx_cec_transmit
> - Add info about the CEC compliance test in the cover letter
> - Add arbitration lost status
> - Validate the physical address inside the EDID
>
> Changes in v3 :-
> - Use v4l2-common helpers in the HDMIRX driver
> - Rename cma node and phandle names
> - Elaborate the comment to explain 160MiB calculation
> - Move &hdmi_receiver_cma to the rock5b dts file
> - Add information about interlaced video testing in the
> cover-letter
>
> Changes in v2 :-
> - Fix checkpatch --strict warnings
> - Move the dt-binding include file changes in a separate patch
> - Add a description for the hardware in the dt-bindings file
> - Rename resets, vo1 grf and HPD properties
> - Add a proper description for grf and vo1-grf phandles in the
> bindings
> - Rename the HDMI RX node name to hdmi-receiver
> - Include gpio header file in binding example to fix the
> dt_binding_check failure
> - Move hdmirx_cma node to the rk3588.dtsi file
> - Add an entry to MAINTAINERS file for the HDMIRX driver
>
> Shreeya Patel (4):
> MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
> dt-bindings: media: Document bindings for HDMI RX Controller
> arm64: dts: rockchip: Add device tree support for HDMI RX Controller
> media: platform: synopsys: Add support for hdmi input driver
>
> .../bindings/media/snps,dw-hdmi-rx.yaml | 132 +
> MAINTAINERS | 8 +
> .../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +
> .../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 56 +
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 1 +
> drivers/media/platform/synopsys/Kconfig | 3 +
> drivers/media/platform/synopsys/Makefile | 2 +
> .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> .../media/platform/synopsys/hdmirx/Makefile | 4 +
> .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> 14 files changed, 3734 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> create mode 100644 drivers/media/platform/synopsys/Kconfig
> create mode 100644 drivers/media/platform/synopsys/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-03 23:57 ` [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Tim Surber
@ 2024-08-05 17:02 ` Nicolas Dufresne
2024-08-06 11:58 ` Dmitry Osipenko
2024-08-14 10:22 ` Shreeya Patel
2 siblings, 0 replies; 40+ messages in thread
From: Nicolas Dufresne @ 2024-08-05 17:02 UTC (permalink / raw)
To: Tim Surber, Shreeya Patel, heiko, mchehab, robh, krzk+dt,
conor+dt, mturquette, sboyd, p.zabel, jose.abreu, nelson.costa,
shawn.wen, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
Hi Tim,
Le dimanche 04 août 2024 à 01:57 +0200, Tim Surber a écrit :
> Hi Shreeya,
>
> I tested your patch and noticed problems when using 3840x2160 resolution
> at 60fps.
>
> For my testing I connected an HDMI source and set it to 4k60fps. I
> verified that this source and the cables work on a screen at this
> resolution.
>
> Using
> 'v4l2-ctl --verbose -d /dev/video1
> --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
> --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
> I get the video format output, but not the periodic output which shows
> the fps.
>
> Using
> 'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
> fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
> I get the following error message:
>
> (gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
> gst_memory_resize: assertion 'size + mem->offset + offset <=
> mem->maxsize' failed
> 0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
> gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:<v4l2src0:pool0:src>
> Dropping truncated buffer, this is likely a driver bug.
> 0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
> gstbufferpool.c:1252:default_reset_buffer:<v4l2src0:pool0:src> Buffer
> 0xffff98008e80 without the memory tag has maxsize (8294400) that is
> smaller than the configured buffer pool size (12441600). The buffer will
> be not be reused. This is most likely a bug in this GstBufferPool subclass
We'll need to check if its a userspace issue rather then kernel one. You can
test with v4l2-ctl (change 0 to the index of your video node):
v4l2-ctl -d 0 --stream-mmap --set-fmt-video=width=3840,height=2160 --set-parm 60
The option --stream-to let you write to a file (will be slower then real-time
though).
Nicolas
>
>
> Everything works with 4k30fps or 1080p 60fps. The hardware should
> support 4k60fps.
>
> Best regards,
> Tim
>
> On 19.07.24 14:40, Shreeya Patel wrote:
> > This series implements support for the Synopsys DesignWare
> > HDMI RX Controller, being compliant with standard HDMI 1.4b
> > and HDMI 2.0.
> >
> > Features that are currently supported by the HDMI RX driver
> > have been tested on rock5b board using a HDMI to micro-HDMI cable.
> > It is recommended to use a good quality cable as there were
> > multiple issues seen during testing the driver.
> >
> > Please note the below information :-
> > * While testing the driver on rock5b we noticed that the binary BL31
> > from Rockchip contains some unknown code to get the HDMI-RX PHY
> > access working without any errors.
> > With TF-A BL31, the HDMI-RX PHY also works fine but there were no
> > interrupts seen for rk_hdmirx-hdmi leading to some errors when
> > loading the driver [0]. It doesn't affect the functionality of the
> > driver though.
> > * We have tested the working of OBS studio with HDMIRX driver and
> > there were no issues seen.
> > * We also tested and verified the support for interlaced video.
> >
> > [0] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/trusted-firmware-a/-/issues/1
> >
> > To test the HDMI RX Controller driver, following example commands can be used :-
> >
> > root@debian-rockchip-rock5b-rk3588:~# v4l2-ctl --verbose -d /dev/video0 \
> > --set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 \
> > --stream-skip=3 --stream-count=100 --stream-to=/home/hdmiin4k.raw --stream-poll
> >
> > root@debian-rockchip-rock5b-rk3588:~# ffmpeg -f rawvideo -vcodec rawvideo \
> > -s 1920x1080 -r 60 -pix_fmt bgr24 -i /home/hdmiin4k.raw output.mkv
> >
> > CEC compliance test results :-
> >
> > * https://gitlab.collabora.com/-/snippets/381
> > * https://gitlab.collabora.com/-/snippets/380
> >
> > Following is the v4l2-compliance test result :-
> >
> > root@debian-rockchip-rock5b-rk3588:~# v4l2-compliance -d /dev/video0
> > v4l2-compliance 1.27.0-5220, 64 bits, 64-bit time_t
> > v4l2-compliance SHA: 8387e3673837 2024-07-01 11:09:32
> >
> > Compliance test for snps_hdmirx device /dev/video0:
> >
> > Driver Info:
> > Driver name : snps_hdmirx
> > Card type : snps_hdmirx
> > Bus info : platform:fdee0000.hdmi-receiver
> > Driver version : 6.10.0
> > Capabilities : 0x84201000
> > Video Capture Multiplanar
> > Streaming
> > Extended Pix Format
> > Device Capabilities
> > Device Caps : 0x04201000
> > Video Capture Multiplanar
> > Streaming
> > Extended Pix Format
> >
> > Required ioctls:
> > test VIDIOC_QUERYCAP: OK
> > test invalid ioctls: OK
> >
> > Allow for multiple opens:
> > test second /dev/video0 open: OK
> > test VIDIOC_QUERYCAP: OK
> > test VIDIOC_G/S_PRIORITY: OK
> > test for unlimited opens: OK
> >
> > Debug ioctls:
> > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> > test VIDIOC_LOG_STATUS: OK
> >
> > Input ioctls:
> > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > test VIDIOC_G/S/ENUMINPUT: OK
> > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > Inputs: 1 Audio Inputs: 0 Tuners: 0
> >
> > Output ioctls:
> > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > Outputs: 0 Audio Outputs: 0 Modulators: 0
> >
> > Input/Output configuration ioctls:
> > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
> > test VIDIOC_DV_TIMINGS_CAP: OK
> > test VIDIOC_G/S_EDID: OK
> >
> > Control ioctls (Input 0):
> > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > test VIDIOC_QUERYCTRL: OK
> > test VIDIOC_G/S_CTRL: OK
> > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > Standard Controls: 3 Private Controls: 0
> >
> > Format ioctls (Input 0):
> > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> > test VIDIOC_G/S_PARM: OK
> > test VIDIOC_G_FBUF: OK (Not Supported)
> > test VIDIOC_G_FMT: OK
> > test VIDIOC_TRY_FMT: OK
> > test VIDIOC_S_FMT: OK
> > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > test Cropping: OK (Not Supported)
> > test Composing: OK (Not Supported)
> > test Scaling: OK (Not Supported)
> >
> > Codec ioctls (Input 0):
> > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> >
> > Buffer ioctls (Input 0):
> > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> > test CREATE_BUFS maximum buffers: OK
> > test VIDIOC_REMOVE_BUFS: OK
> > test VIDIOC_EXPBUF: OK
> > test Requests: OK (Not Supported)
> >
> > Total for snps_hdmirx device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
> >
> > Changes in v4 :-
> > - Remove DTS changes included in the device tree patch
> > - Remove the hdmi rx pin info as it's already present
> > in the rk3588-base-pinctrl.dtsi
> > - Create a separate config option for selecting the EDID
> > and enable it by default
> > - Improve the comment related to DV timings and move it
> > to the side of hdmirx_get_detected_timings
> > - Add 100ms delay before pulling the HPD high
> > - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> > - Drop the bus info from hdmirx_querycap
> > - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> > - Set queue->min_queued_buffers to 1
> > - Drop q->allow_cache_hints = 0; as it's always 0 by default
> > - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> > - Drop .read = vb2_fop_read as it's not supported by driver
> > - Remove redundant edid_init_data_600M
> > - Make HPD low when driver is loaded
> > - Add support for reading AVI Infoframe
> > - Remove msg_len checks from hdmirx_cec_transmit
> > - Add info about the CEC compliance test in the cover letter
> > - Add arbitration lost status
> > - Validate the physical address inside the EDID
> >
> > Changes in v3 :-
> > - Use v4l2-common helpers in the HDMIRX driver
> > - Rename cma node and phandle names
> > - Elaborate the comment to explain 160MiB calculation
> > - Move &hdmi_receiver_cma to the rock5b dts file
> > - Add information about interlaced video testing in the
> > cover-letter
> >
> > Changes in v2 :-
> > - Fix checkpatch --strict warnings
> > - Move the dt-binding include file changes in a separate patch
> > - Add a description for the hardware in the dt-bindings file
> > - Rename resets, vo1 grf and HPD properties
> > - Add a proper description for grf and vo1-grf phandles in the
> > bindings
> > - Rename the HDMI RX node name to hdmi-receiver
> > - Include gpio header file in binding example to fix the
> > dt_binding_check failure
> > - Move hdmirx_cma node to the rk3588.dtsi file
> > - Add an entry to MAINTAINERS file for the HDMIRX driver
> >
> > Shreeya Patel (4):
> > MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
> > dt-bindings: media: Document bindings for HDMI RX Controller
> > arm64: dts: rockchip: Add device tree support for HDMI RX Controller
> > media: platform: synopsys: Add support for hdmi input driver
> >
> > .../bindings/media/snps,dw-hdmi-rx.yaml | 132 +
> > MAINTAINERS | 8 +
> > .../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +
> > .../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 56 +
> > drivers/media/platform/Kconfig | 1 +
> > drivers/media/platform/Makefile | 1 +
> > drivers/media/platform/synopsys/Kconfig | 3 +
> > drivers/media/platform/synopsys/Makefile | 2 +
> > .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> > .../media/platform/synopsys/hdmirx/Makefile | 4 +
> > .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> > .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> > .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> > .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> > 14 files changed, 3734 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> > create mode 100644 drivers/media/platform/synopsys/Kconfig
> > create mode 100644 drivers/media/platform/synopsys/Makefile
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> >
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-03 23:57 ` [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Tim Surber
2024-08-05 17:02 ` Nicolas Dufresne
@ 2024-08-06 11:58 ` Dmitry Osipenko
2024-08-06 20:37 ` Tim Surber
2024-08-14 10:22 ` Shreeya Patel
2 siblings, 1 reply; 40+ messages in thread
From: Dmitry Osipenko @ 2024-08-06 11:58 UTC (permalink / raw)
To: Tim Surber, Shreeya Patel, heiko, mchehab, robh, krzk+dt,
conor+dt, mturquette, sboyd, p.zabel, jose.abreu, nelson.costa,
shawn.wen, nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
On 8/4/24 02:57, Tim Surber wrote:
> Hi Shreeya,
>
> I tested your patch and noticed problems when using 3840x2160 resolution
> at 60fps.
>
> For my testing I connected an HDMI source and set it to 4k60fps. I
> verified that this source and the cables work on a screen at this
> resolution.
>
> Using
> 'v4l2-ctl --verbose -d /dev/video1
> --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
> --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
> I get the video format output, but not the periodic output which shows
> the fps.
>
> Using
> 'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
> fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
> I get the following error message:
>
> (gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
> gst_memory_resize: assertion 'size + mem->offset + offset <=
> mem->maxsize' failed
> 0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
> gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:<v4l2src0:pool0:src> Dropping truncated buffer, this is likely a driver bug.
> 0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
> gstbufferpool.c:1252:default_reset_buffer:<v4l2src0:pool0:src> Buffer
> 0xffff98008e80 without the memory tag has maxsize (8294400) that is
> smaller than the configured buffer pool size (12441600). The buffer will
> be not be reused. This is most likely a bug in this GstBufferPool subclass
>
>
> Everything works with 4k30fps or 1080p 60fps. The hardware should
> support 4k60fps.
Please do `echo 3 > /sys/module/synopsys_hdmirx/parameters/debug` and
show the kernel log of capturing 4k@60 with v4l2-ctl.
--
Best regards,
Dmitry
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-06 11:58 ` Dmitry Osipenko
@ 2024-08-06 20:37 ` Tim Surber
2024-08-06 21:17 ` Nicolas Dufresne
0 siblings, 1 reply; 40+ messages in thread
From: Tim Surber @ 2024-08-06 20:37 UTC (permalink / raw)
To: Dmitry Osipenko, Shreeya Patel, heiko, mchehab, robh, krzk+dt,
conor+dt, mturquette, sboyd, p.zabel, jose.abreu, nelson.costa,
shawn.wen, nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
Here are the results of some more debugging.
I see that in the first example the pixel format is set to RGB888 and in
the second to NV12, I was not able to successfully change this on my
source, nevertheless the HDMI RX should work I think.
###source set to 4k30fps####
---------------------------
v4l2-ctl --verbose -d /dev/video1
--set-fmt-video=width=3840,height=2160,pixelformat='NV12'
--stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll
---------------------------
VIDIOC_QUERYCAP: ok
VIDIOC_G_FMT: ok
The pixelformat 'NV12' is invalid
New timings found
VIDIOC_REQBUFS returned 0 (Success)
VIDIOC_CREATE_BUFS returned 0 (Success)
VIDIOC_QUERYBUF returned 0 (Success)
VIDIOC_QUERYBUF returned 0 (Success)
VIDIOC_QUERYBUF returned 0 (Success)
VIDIOC_QUERYBUF returned 0 (Success)
VIDIOC_G_FMT returned 0 (Success)
VIDIOC_QBUF returned 0 (Success)
VIDIOC_QBUF returned 0 (Success)
VIDIOC_QBUF returned 0 (Success)
VIDIOC_QBUF returned 0 (Success)
VIDIOC_STREAMON returned 0 (Success)
cap dqbuf: 0 seq: 0 bytesused: 8294400 ts: 536.006261 field: Any
(ts-monotonic, ts-src-eof)
cap dqbuf: 1 seq: 1 bytesused: 8294400 ts: 536.039574 delta: 33.313
ms field: Any (ts-monotonic, ts-src-eof)
cap dqbuf: 2 seq: 2 bytesused: 8294400 ts: 536.072906 delta: 33.332
ms field: Any (ts-monotonic, ts-src-eof)
cap dqbuf: 3 seq: 3 bytesused: 8294400 ts: 536.106239 delta: 33.333
ms field: Any (ts-monotonic, ts-src-eof)
cap dqbuf: 0 seq: 4 bytesused: 8294400 ts: 536.139571 delta: 33.332
ms fps: 30.01 field: Any (ts-monotonic, ts-src-eof)
cap dqbuf: 1 seq: 5 bytesused: 8294400 ts: 536.172903 delta: 33.332
ms fps: 30.00 field: Any (ts-monotonic, ts-src-eof)
[...]
---------------------------
dmesg
---------------------------
[ 529.266804] fdee0000.hdmi_receiver: hb
[ 530.280073] fdee0000.hdmi_receiver: hb
[ 531.293412] fdee0000.hdmi_receiver: hb
[ 532.306716] fdee0000.hdmi_receiver: hb
[ 533.319906] fdee0000.hdmi_receiver: hb
[ 534.333263] fdee0000.hdmi_receiver: hb
[ 535.346616] fdee0000.hdmi_receiver: hb
[ 535.739795] fdee0000.hdmi_receiver: C-Plane 0 size: 24883200, Total
imagesize: 24883200
[ 535.747733] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 535.747750] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: RGB888
[ 535.747760] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 535.747780] fdee0000.hdmi_receiver: get timings from dma
[ 535.747787] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:30, pixclk:297016000
[ 535.747799] fdee0000.hdmi_receiver: hfp:172, hs:92, hbp:296, vfp:8,
vs:10, vbp:72
[ 535.747809] fdee0000.hdmi_receiver: tmds_clk:297016000
[ 535.747816] fdee0000.hdmi_receiver: interlace:0, fmt:0, vic:127,
color:24, mode:hdmi
[ 535.747825] fdee0000.hdmi_receiver: deframer_st:0x11
[ 535.747833] fdee0000.hdmi_receiver: query_dv_timings: 3840x2160p30.00
(4400x2250)
[ 535.747854] fdee0000.hdmi_receiver: s_dv_timings: 3840x2160p30.00
(4400x2250)
[ 535.747875] fdee0000.hdmi_receiver: hdmirx_s_dv_timings: no change
[ 535.747924] fdee0000.hdmi_receiver: vid-cap-mplane: count 4, size 8294400
[ 535.752754] fdee0000.hdmi_receiver: C-Plane 0 size: 24883200, Total
imagesize: 24883200
[ 535.752884] fdee0000.hdmi_receiver: C-Plane 0 size: 24883200, Total
imagesize: 24883200
[ 535.752904] fdee0000.hdmi_receiver: hdmirx_start_streaming:
start_stream cur_buf y_addr:0xe0ea5000, uv_addr:0xe168e000
[ 535.752920] fdee0000.hdmi_receiver: hdmirx_start_streaming: enable dma
[ 535.780112] fdee0000.hdmi_receiver: dma_irq st1:0x100, st13:1085
[ 535.780128] fdee0000.hdmi_receiver: line_flag_int_handler: last have
no dma_idle_irq
[ 535.796093] fdee0000.hdmi_receiver: dma_irq st1:0x80, st13:2160
[ 535.813435] fdee0000.hdmi_receiver: dma_irq st1:0x100, st13:1085
[ 535.829423] fdee0000.hdmi_receiver: dma_irq st1:0x80, st13:2160
[...]
---------------------------
###source set to 4k60fps####
---------------------------
v4l2-ctl --verbose -d /dev/video1
--set-fmt-video=width=3840,height=2160,pixelformat='NV12'
--stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll
---------------------------
VIDIOC_QUERYCAP: ok
VIDIOC_G_FMT: ok
VIDIOC_S_FMT: ok
Format Video Capture Multiplanar:
Width/Height : 3840/2160
Pixel Format : 'NV12' (Y/UV 4:2:0)
Field : None
Number of planes : 1
Flags :
Colorspace : sRGB
Transfer Function : Default
YCbCr/HSV Encoding: Default
Quantization : Default
Plane 0 :
Bytes per Line : 3840
Size Image : 8294400
[stuck here, have to end with ctrl c]
---------------------------
dmesg
---------------------------
[ 1520.198123] fdee0000.hdmi_receiver: hb
[ 1521.211383] fdee0000.hdmi_receiver: hb
[ 1522.224680] fdee0000.hdmi_receiver: hb
[ 1523.237936] fdee0000.hdmi_receiver: hb
[ 1524.251313] fdee0000.hdmi_receiver: hb
[ 1525.264606] fdee0000.hdmi_receiver: hb
[ 1526.277937] fdee0000.hdmi_receiver: hb
[ 1526.427540] fdee0000.hdmi_receiver: C-Plane 0 size: 8294400, Total
imagesize: 8294400
[ 1526.427587] fdee0000.hdmi_receiver: C-Plane 0 size: 8294400, Total
imagesize: 8294400
[ 1526.427598] fdee0000.hdmi_receiver: hdmirx_set_fmt: req(3840, 2160),
out(3840, 2160), fmt:0x3231564e
[ 1526.435569] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1526.435588] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1526.435597] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1526.435618] fdee0000.hdmi_receiver: get timings from dma
[ 1526.435626] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1526.435637] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1526.435648] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1526.435656] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1526.435665] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1527.291212] fdee0000.hdmi_receiver: hb
[ 1527.443533] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1527.443550] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1527.443560] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1527.443579] fdee0000.hdmi_receiver: get timings from dma
[ 1527.443586] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1527.443597] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1527.443608] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1527.443615] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1527.443625] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1528.304515] fdee0000.hdmi_receiver: hb
[ 1528.451543] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1528.451560] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1528.451569] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1528.451588] fdee0000.hdmi_receiver: get timings from dma
[ 1528.451595] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1528.451606] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1528.451617] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1528.451624] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1528.451634] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1529.317814] fdee0000.hdmi_receiver: hb
[ 1529.459549] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1529.459566] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1529.459575] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1529.459593] fdee0000.hdmi_receiver: get timings from dma
[ 1529.459601] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1529.459612] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1529.459623] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1529.459630] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1529.459640] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1530.331085] fdee0000.hdmi_receiver: hb
[ 1530.467555] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1530.467571] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1530.467580] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1530.467599] fdee0000.hdmi_receiver: get timings from dma
[ 1530.467606] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1530.467618] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1530.467628] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1530.467635] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1530.467664] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1531.344437] fdee0000.hdmi_receiver: hb
[ 1531.475649] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1531.475665] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1531.475674] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1531.475693] fdee0000.hdmi_receiver: get timings from dma
[ 1531.475700] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1531.475711] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1531.475722] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1531.475729] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1531.475739] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1532.357695] fdee0000.hdmi_receiver: hb
[ 1532.483716] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1532.483733] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1532.483742] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1532.483761] fdee0000.hdmi_receiver: get timings from dma
[ 1532.483768] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1532.483780] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1532.483790] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1532.483797] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1532.483807] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1533.370994] fdee0000.hdmi_receiver: hb
[ 1533.491726] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1533.491743] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1533.491752] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1533.491770] fdee0000.hdmi_receiver: get timings from dma
[ 1533.491778] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1533.491789] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1533.491800] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1533.491807] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1533.491816] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1534.384324] fdee0000.hdmi_receiver: hb
[ 1534.499888] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1534.499904] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1534.499913] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1534.499932] fdee0000.hdmi_receiver: get timings from dma
[ 1534.499939] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1534.499950] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1534.499960] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1534.499968] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1534.499977] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1535.397596] fdee0000.hdmi_receiver: hb
[ 1535.507961] fdee0000.hdmi_receiver: tx_5v_power_present: 1
[ 1535.507977] fdee0000.hdmi_receiver: hdmirx_get_pix_fmt: pix_fmt: YUV420
[ 1535.507986] fdee0000.hdmi_receiver: hdmirx_get_colordepth:
color_depth: 24, reg_val:4
[ 1535.508005] fdee0000.hdmi_receiver: get timings from dma
[ 1535.508012] fdee0000.hdmi_receiver: act:3840x2160, total:4400x2250,
fps:60, pixclk:297008000
[ 1535.508023] fdee0000.hdmi_receiver: hfp:4294965460, hs:48, hbp:148,
vfp:8, vs:10, vbp:72
[ 1535.508034] fdee0000.hdmi_receiver: tmds_clk:297008000
[ 1535.508041] fdee0000.hdmi_receiver: interlace:0, fmt:3, vic:127,
color:24, mode:hdmi
[ 1535.508051] fdee0000.hdmi_receiver: deframer_st:0x11
[ 1536.410933] fdee0000.hdmi_receiver: hb
[ 1537.424223] fdee0000.hdmi_receiver: hb
[ 1538.437523] fdee0000.hdmi_receiver: hb
[ 1539.450848] fdee0000.hdmi_receiver: hb
Best regards
Tim
On 06.08.24 13:58, Dmitry Osipenko wrote:
> On 8/4/24 02:57, Tim Surber wrote:
>> Hi Shreeya,
>>
>> I tested your patch and noticed problems when using 3840x2160 resolution
>> at 60fps.
>>
>> For my testing I connected an HDMI source and set it to 4k60fps. I
>> verified that this source and the cables work on a screen at this
>> resolution.
>>
>> Using
>> 'v4l2-ctl --verbose -d /dev/video1
>> --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
>> --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
>> I get the video format output, but not the periodic output which shows
>> the fps.
>>
>> Using
>> 'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
>> fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
>> I get the following error message:
>>
>> (gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
>> gst_memory_resize: assertion 'size + mem->offset + offset <=
>> mem->maxsize' failed
>> 0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
>> gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:<v4l2src0:pool0:src> Dropping truncated buffer, this is likely a driver bug.
>> 0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
>> gstbufferpool.c:1252:default_reset_buffer:<v4l2src0:pool0:src> Buffer
>> 0xffff98008e80 without the memory tag has maxsize (8294400) that is
>> smaller than the configured buffer pool size (12441600). The buffer will
>> be not be reused. This is most likely a bug in this GstBufferPool subclass
>>
>>
>> Everything works with 4k30fps or 1080p 60fps. The hardware should
>> support 4k60fps.
> Please do `echo 3 > /sys/module/synopsys_hdmirx/parameters/debug` and
> show the kernel log of capturing 4k@60 with v4l2-ctl.
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-06 20:37 ` Tim Surber
@ 2024-08-06 21:17 ` Nicolas Dufresne
0 siblings, 0 replies; 40+ messages in thread
From: Nicolas Dufresne @ 2024-08-06 21:17 UTC (permalink / raw)
To: Tim Surber, Dmitry Osipenko, Shreeya Patel, heiko, mchehab, robh,
krzk+dt, conor+dt, mturquette, sboyd, p.zabel, jose.abreu,
nelson.costa, shawn.wen, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
Hi Tim,
Le mardi 06 août 2024 à 22:37 +0200, Tim Surber a écrit :
> ###source set to 4k60fps####
> ---------------------------
> v4l2-ctl --verbose -d /dev/video1
> --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
> --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll
> ---------------------------
> VIDIOC_QUERYCAP: ok
> VIDIOC_G_FMT: ok
> VIDIOC_S_FMT: ok
> Format Video Capture Multiplanar:
> Width/Height : 3840/2160
> Pixel Format : 'NV12' (Y/UV 4:2:0)
> Field : None
> Number of planes : 1
> Flags :
> Colorspace : sRGB
> Transfer Function : Default
> YCbCr/HSV Encoding: Default
> Quantization : Default
> Plane 0 :
> Bytes per Line : 3840
> Size Image : 8294400
You have highlighted a bug here. NV12 has 2 planes, but the size image only
allow for the luma to be stored.
Size Image: 3840 * 2160 * 3 / 2 = 12441600 bytes.
Lets at least get that fixed, and check again.
Nicolas
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-03 23:57 ` [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Tim Surber
2024-08-05 17:02 ` Nicolas Dufresne
2024-08-06 11:58 ` Dmitry Osipenko
@ 2024-08-14 10:22 ` Shreeya Patel
2024-08-24 23:03 ` Tim Surber
2024-08-28 22:13 ` Tim Surber
2 siblings, 2 replies; 40+ messages in thread
From: Shreeya Patel @ 2024-08-14 10:22 UTC (permalink / raw)
To: Tim Surber
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip
---- On Sun, 04 Aug 2024 05:27:08 +0530 Tim Surber wrote ---
> Hi Shreeya,
>
Hi Tim,
> I tested your patch and noticed problems when using 3840x2160 resolution
> at 60fps.
>
> For my testing I connected an HDMI source and set it to 4k60fps. I
> verified that this source and the cables work on a screen at this
> resolution.
>
> Using
> 'v4l2-ctl --verbose -d /dev/video1
> --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
> --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
> I get the video format output, but not the periodic output which shows
> the fps.
>
> Using
> 'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
> fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
> I get the following error message:
>
> (gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
> gst_memory_resize: assertion 'size + mem->offset + offset <=
> mem->maxsize' failed
> 0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
> gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:
> Dropping truncated buffer, this is likely a driver bug.
> 0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
> gstbufferpool.c:1252:default_reset_buffer: Buffer
> 0xffff98008e80 without the memory tag has maxsize (8294400) that is
> smaller than the configured buffer pool size (12441600). The buffer will
> be not be reused. This is most likely a bug in this GstBufferPool subclass
>
>
> Everything works with 4k30fps or 1080p 60fps. The hardware should
> support 4k60fps.
>
Sorry for the delayed response, I've been trying to reproduce this on my side
and to also fix it.
It seems you are right, 4K@60 fps doesn't work with the latest version of HDMIRX.
We found out that it could be because of the current EDID which shows some failures.
Though I wasn't able to test the following on my side since my device doesn't support
4K, one of my colleague tried to replace the EDID and 4K@60 fps worked fine after that.
If you'd like to try it yourself then following is the command to get the new EDID
v4l2-ctl --show-edid type=hdmi-4k-600mhz
You will have to replace the EDID in the driver with the EDID you get the from the above
command in HEX format.
Thanks for reporting this, I will soon send v5 with this change included in it.
Thanks,
Shreeya Patel
> Best regards,
> Tim
>
> On 19.07.24 14:40, Shreeya Patel wrote:
> > This series implements support for the Synopsys DesignWare
> > HDMI RX Controller, being compliant with standard HDMI 1.4b
> > and HDMI 2.0.
> >
> > Features that are currently supported by the HDMI RX driver
> > have been tested on rock5b board using a HDMI to micro-HDMI cable.
> > It is recommended to use a good quality cable as there were
> > multiple issues seen during testing the driver.
> >
> > Please note the below information :-
> > * While testing the driver on rock5b we noticed that the binary BL31
> > from Rockchip contains some unknown code to get the HDMI-RX PHY
> > access working without any errors.
> > With TF-A BL31, the HDMI-RX PHY also works fine but there were no
> > interrupts seen for rk_hdmirx-hdmi leading to some errors when
> > loading the driver [0]. It doesn't affect the functionality of the
> > driver though.
> > * We have tested the working of OBS studio with HDMIRX driver and
> > there were no issues seen.
> > * We also tested and verified the support for interlaced video.
> >
> > [0] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/trusted-firmware-a/-/issues/1
> >
> > To test the HDMI RX Controller driver, following example commands can be used :-
> >
> > root@debian-rockchip-rock5b-rk3588:~# v4l2-ctl --verbose -d /dev/video0 \
> > --set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 \
> > --stream-skip=3 --stream-count=100 --stream-to=/home/hdmiin4k.raw --stream-poll
> >
> > root@debian-rockchip-rock5b-rk3588:~# ffmpeg -f rawvideo -vcodec rawvideo \
> > -s 1920x1080 -r 60 -pix_fmt bgr24 -i /home/hdmiin4k.raw output.mkv
> >
> > CEC compliance test results :-
> >
> > * https://gitlab.collabora.com/-/snippets/381
> > * https://gitlab.collabora.com/-/snippets/380
> >
> > Following is the v4l2-compliance test result :-
> >
> > root@debian-rockchip-rock5b-rk3588:~# v4l2-compliance -d /dev/video0
> > v4l2-compliance 1.27.0-5220, 64 bits, 64-bit time_t
> > v4l2-compliance SHA: 8387e3673837 2024-07-01 11:09:32
> >
> > Compliance test for snps_hdmirx device /dev/video0:
> >
> > Driver Info:
> > Driver name : snps_hdmirx
> > Card type : snps_hdmirx
> > Bus info : platform:fdee0000.hdmi-receiver
> > Driver version : 6.10.0
> > Capabilities : 0x84201000
> > Video Capture Multiplanar
> > Streaming
> > Extended Pix Format
> > Device Capabilities
> > Device Caps : 0x04201000
> > Video Capture Multiplanar
> > Streaming
> > Extended Pix Format
> >
> > Required ioctls:
> > test VIDIOC_QUERYCAP: OK
> > test invalid ioctls: OK
> >
> > Allow for multiple opens:
> > test second /dev/video0 open: OK
> > test VIDIOC_QUERYCAP: OK
> > test VIDIOC_G/S_PRIORITY: OK
> > test for unlimited opens: OK
> >
> > Debug ioctls:
> > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> > test VIDIOC_LOG_STATUS: OK
> >
> > Input ioctls:
> > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > test VIDIOC_G/S/ENUMINPUT: OK
> > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > Inputs: 1 Audio Inputs: 0 Tuners: 0
> >
> > Output ioctls:
> > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > Outputs: 0 Audio Outputs: 0 Modulators: 0
> >
> > Input/Output configuration ioctls:
> > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
> > test VIDIOC_DV_TIMINGS_CAP: OK
> > test VIDIOC_G/S_EDID: OK
> >
> > Control ioctls (Input 0):
> > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > test VIDIOC_QUERYCTRL: OK
> > test VIDIOC_G/S_CTRL: OK
> > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > Standard Controls: 3 Private Controls: 0
> >
> > Format ioctls (Input 0):
> > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> > test VIDIOC_G/S_PARM: OK
> > test VIDIOC_G_FBUF: OK (Not Supported)
> > test VIDIOC_G_FMT: OK
> > test VIDIOC_TRY_FMT: OK
> > test VIDIOC_S_FMT: OK
> > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > test Cropping: OK (Not Supported)
> > test Composing: OK (Not Supported)
> > test Scaling: OK (Not Supported)
> >
> > Codec ioctls (Input 0):
> > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> >
> > Buffer ioctls (Input 0):
> > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> > test CREATE_BUFS maximum buffers: OK
> > test VIDIOC_REMOVE_BUFS: OK
> > test VIDIOC_EXPBUF: OK
> > test Requests: OK (Not Supported)
> >
> > Total for snps_hdmirx device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
> >
> > Changes in v4 :-
> > - Remove DTS changes included in the device tree patch
> > - Remove the hdmi rx pin info as it's already present
> > in the rk3588-base-pinctrl.dtsi
> > - Create a separate config option for selecting the EDID
> > and enable it by default
> > - Improve the comment related to DV timings and move it
> > to the side of hdmirx_get_detected_timings
> > - Add 100ms delay before pulling the HPD high
> > - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> > - Drop the bus info from hdmirx_querycap
> > - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> > - Set queue->min_queued_buffers to 1
> > - Drop q->allow_cache_hints = 0; as it's always 0 by default
> > - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> > - Drop .read = vb2_fop_read as it's not supported by driver
> > - Remove redundant edid_init_data_600M
> > - Make HPD low when driver is loaded
> > - Add support for reading AVI Infoframe
> > - Remove msg_len checks from hdmirx_cec_transmit
> > - Add info about the CEC compliance test in the cover letter
> > - Add arbitration lost status
> > - Validate the physical address inside the EDID
> >
> > Changes in v3 :-
> > - Use v4l2-common helpers in the HDMIRX driver
> > - Rename cma node and phandle names
> > - Elaborate the comment to explain 160MiB calculation
> > - Move &hdmi_receiver_cma to the rock5b dts file
> > - Add information about interlaced video testing in the
> > cover-letter
> >
> > Changes in v2 :-
> > - Fix checkpatch --strict warnings
> > - Move the dt-binding include file changes in a separate patch
> > - Add a description for the hardware in the dt-bindings file
> > - Rename resets, vo1 grf and HPD properties
> > - Add a proper description for grf and vo1-grf phandles in the
> > bindings
> > - Rename the HDMI RX node name to hdmi-receiver
> > - Include gpio header file in binding example to fix the
> > dt_binding_check failure
> > - Move hdmirx_cma node to the rk3588.dtsi file
> > - Add an entry to MAINTAINERS file for the HDMIRX driver
> >
> > Shreeya Patel (4):
> > MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
> > dt-bindings: media: Document bindings for HDMI RX Controller
> > arm64: dts: rockchip: Add device tree support for HDMI RX Controller
> > media: platform: synopsys: Add support for hdmi input driver
> >
> > .../bindings/media/snps,dw-hdmi-rx.yaml | 132 +
> > MAINTAINERS | 8 +
> > .../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +
> > .../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 56 +
> > drivers/media/platform/Kconfig | 1 +
> > drivers/media/platform/Makefile | 1 +
> > drivers/media/platform/synopsys/Kconfig | 3 +
> > drivers/media/platform/synopsys/Makefile | 2 +
> > .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> > .../media/platform/synopsys/hdmirx/Makefile | 4 +
> > .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> > .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> > .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> > .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> > 14 files changed, 3734 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> > create mode 100644 drivers/media/platform/synopsys/Kconfig
> > create mode 100644 drivers/media/platform/synopsys/Makefile
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> >
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-14 10:22 ` Shreeya Patel
@ 2024-08-24 23:03 ` Tim Surber
2024-08-28 22:13 ` Tim Surber
1 sibling, 0 replies; 40+ messages in thread
From: Tim Surber @ 2024-08-24 23:03 UTC (permalink / raw)
To: Shreeya Patel
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip
Hi Shreeya,
On 14.08.24 12:22, Shreeya Patel wrote:
> If you'd like to try it yourself then following is the command to get the new EDID
>
> v4l2-ctl --show-edid type=hdmi-4k-600mhz
>
> You will have to replace the EDID in the driver with the EDID you get the from the above
> command in HEX format.
I updated the EDID with
v4l2-ctl --show-edid type=hdmi-4k-600mhz
and this seems to fix the issue for me.
Best regards,
Tim
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
2024-07-19 22:10 ` Rob Herring (Arm)
2024-07-20 10:44 ` Johan Jonker
@ 2024-08-25 7:03 ` Krzysztof Kozlowski
2024-08-26 8:19 ` Michael Riesch
3 siblings, 0 replies; 40+ messages in thread
From: Krzysztof Kozlowski @ 2024-08-25 7:03 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
On 19/07/2024 14:40, Shreeya Patel wrote:
> Document bindings for the Synopsys DesignWare HDMI RX Controller.
>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
> Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
If you are going to send a new version, then:
A nit, subject: drop second/last, redundant "Document bindings for". The
"dt-bindings" prefix is already stating that these are bindings and this
is documentation.
See also:
https://elixir.bootlin.com/linux/v6.7-rc8/source/Documentation/devicetree/bindings/submitting-patches.rst#L18
"Add Synopsys HDMI RX Controller".
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
` (2 preceding siblings ...)
2024-08-25 7:03 ` Krzysztof Kozlowski
@ 2024-08-26 8:19 ` Michael Riesch
3 siblings, 0 replies; 40+ messages in thread
From: Michael Riesch @ 2024-08-26 8:19 UTC (permalink / raw)
To: Shreeya Patel, heiko, mchehab, robh, krzk+dt, conor+dt,
mturquette, sboyd, p.zabel, jose.abreu, nelson.costa, shawn.wen,
nicolas.dufresne, hverkuil, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip, Dmitry Osipenko
Hi Shreeya, hi all,
First of all thank you very much for your efforts and patches!
I have got more of a general question that came up during my recent work
on a HDMI RX companion chip driver. It may or may not be out of scope of
this initial submission for the Synopsys HDMI RX IP core, but anyway I
feel like I should ask it now:
On 7/19/24 14:40, Shreeya Patel wrote:
> [...]
> +examples:
> + - |
> + #include <dt-bindings/clock/rockchip,rk3588-cru.h>
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> + #include <dt-bindings/power/rk3588-power.h>
> + #include <dt-bindings/reset/rockchip,rk3588-cru.h>
> + hdmi_receiver: hdmi-receiver@fdee0000 {
> + compatible = "rockchip,rk3588-hdmirx-ctrler", "snps,dw-hdmi-rx";
> + reg = <0xfdee0000 0x6000>;
> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH 0>,
> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH 0>;
> + interrupt-names = "cec", "hdmi", "dma";
> + clocks = <&cru ACLK_HDMIRX>,
> + <&cru CLK_HDMIRX_AUD>,
> + <&cru CLK_CR_PARA>,
> + <&cru PCLK_HDMIRX>,
> + <&cru CLK_HDMIRX_REF>,
> + <&cru PCLK_S_HDMIRX>,
> + <&cru HCLK_VO1>;
> + clock-names = "aclk",
> + "audio",
> + "cr_para",
> + "pclk",
> + "ref",
> + "hclk_s_hdmirx",
> + "hclk_vo1";
> + power-domains = <&power RK3588_PD_VO1>;
> + resets = <&cru SRST_A_HDMIRX>, <&cru SRST_P_HDMIRX>,
> + <&cru SRST_HDMIRX_REF>, <&cru SRST_A_HDMIRX_BIU>;
> + reset-names = "axi", "apb", "ref", "biu";
> + memory-region = <&hdmi_receiver_cma>;
> + pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
> + pinctrl-names = "default";
> + hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
> + };
Should HDMI RX connectors be described in the device tree?
It seems that V4L2 features support for svidio and composite connectors,
but newer standards such as HDMI never made it to e.g,
drivers/media/v4l2-core/v4l2-fwnode.c. The name of the corresponding DT
binding
Documentation/devicetree/bindings/display/connector/analog-tv-connector.yaml
somewhat confirms that only analog connectors are in the scope there.
This means that some initial discussion and effort on the framework
would be required.
However, this would enable some nice (although not exactly killer)
features: describing label, orientation, and connector (sub)type on DT
level, thus eliminating the need to know the HW description in the
higher SW layers.
If the notion of HDMI RX (or, in general, digital video input)
connectors in DT sounds reasonable to you all, there may be the chance
to rework the submission at hand accordingly to avoid any compatibility
issues with older/newer device trees. This is why I am bringing this up
right now.
What do you all think?
Best regards,
Michael
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-14 10:22 ` Shreeya Patel
2024-08-24 23:03 ` Tim Surber
@ 2024-08-28 22:13 ` Tim Surber
2024-08-29 10:03 ` Shreeya Patel
1 sibling, 1 reply; 40+ messages in thread
From: Tim Surber @ 2024-08-28 22:13 UTC (permalink / raw)
To: Shreeya Patel
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip
Hi Shreeya,
another hint:
Changing the EDID like you described fixed the 4k60fps BGR3 input.
But still for NV16/NV24 (I can't output NV12 for some reason) there is
the error which Nicolas described.
The output from v4l2-ctl for NV16/NV24 is:
Plane 0 :
Bytes per Line : 3840
Size Image : 8294400
According to Nicolas there should be an additional plane/more memory
reserved.
This leads to errors when trying to read the device using for example
gstreamer:
gst_memory_resize: assertion 'size + mem->offset + offset <=
mem->maxsize' failed
This seems to always happen using the NV16/NV24 formats regardless of
resolution/fps.
Best regards,
Tim
On 14.08.24 12:22, Shreeya Patel wrote:
> ---- On Sun, 04 Aug 2024 05:27:08 +0530 Tim Surber wrote ---
> > Hi Shreeya,
> >
>
> Hi Tim,
>
>
> > I tested your patch and noticed problems when using 3840x2160 resolution
> > at 60fps.
> >
> > For my testing I connected an HDMI source and set it to 4k60fps. I
> > verified that this source and the cables work on a screen at this
> > resolution.
> >
> > Using
> > 'v4l2-ctl --verbose -d /dev/video1
> > --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
> > --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
> > I get the video format output, but not the periodic output which shows
> > the fps.
> >
> > Using
> > 'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
> > fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
> > I get the following error message:
> >
> > (gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
> > gst_memory_resize: assertion 'size + mem->offset + offset <=
> > mem->maxsize' failed
> > 0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
> > gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:
> > Dropping truncated buffer, this is likely a driver bug.
> > 0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
> > gstbufferpool.c:1252:default_reset_buffer: Buffer
> > 0xffff98008e80 without the memory tag has maxsize (8294400) that is
> > smaller than the configured buffer pool size (12441600). The buffer will
> > be not be reused. This is most likely a bug in this GstBufferPool subclass
> >
> >
> > Everything works with 4k30fps or 1080p 60fps. The hardware should
> > support 4k60fps.
> >
>
>
> Sorry for the delayed response, I've been trying to reproduce this on my side
> and to also fix it.
>
> It seems you are right, 4K@60 fps doesn't work with the latest version of HDMIRX.
> We found out that it could be because of the current EDID which shows some failures.
>
> Though I wasn't able to test the following on my side since my device doesn't support
> 4K, one of my colleague tried to replace the EDID and 4K@60 fps worked fine after that.
>
> If you'd like to try it yourself then following is the command to get the new EDID
>
> v4l2-ctl --show-edid type=hdmi-4k-600mhz
>
> You will have to replace the EDID in the driver with the EDID you get the from the above
> command in HEX format.
>
> Thanks for reporting this, I will soon send v5 with this change included in it.
>
>
> Thanks,
> Shreeya Patel
>
>
> > Best regards,
> > Tim
> >
> > On 19.07.24 14:40, Shreeya Patel wrote:
> > > This series implements support for the Synopsys DesignWare
> > > HDMI RX Controller, being compliant with standard HDMI 1.4b
> > > and HDMI 2.0.
> > >
> > > Features that are currently supported by the HDMI RX driver
> > > have been tested on rock5b board using a HDMI to micro-HDMI cable.
> > > It is recommended to use a good quality cable as there were
> > > multiple issues seen during testing the driver.
> > >
> > > Please note the below information :-
> > > * While testing the driver on rock5b we noticed that the binary BL31
> > > from Rockchip contains some unknown code to get the HDMI-RX PHY
> > > access working without any errors.
> > > With TF-A BL31, the HDMI-RX PHY also works fine but there were no
> > > interrupts seen for rk_hdmirx-hdmi leading to some errors when
> > > loading the driver [0]. It doesn't affect the functionality of the
> > > driver though.
> > > * We have tested the working of OBS studio with HDMIRX driver and
> > > there were no issues seen.
> > > * We also tested and verified the support for interlaced video.
> > >
> > > [0] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/trusted-firmware-a/-/issues/1
> > >
> > > To test the HDMI RX Controller driver, following example commands can be used :-
> > >
> > > root@debian-rockchip-rock5b-rk3588:~# v4l2-ctl --verbose -d /dev/video0 \
> > > --set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 \
> > > --stream-skip=3 --stream-count=100 --stream-to=/home/hdmiin4k.raw --stream-poll
> > >
> > > root@debian-rockchip-rock5b-rk3588:~# ffmpeg -f rawvideo -vcodec rawvideo \
> > > -s 1920x1080 -r 60 -pix_fmt bgr24 -i /home/hdmiin4k.raw output.mkv
> > >
> > > CEC compliance test results :-
> > >
> > > * https://gitlab.collabora.com/-/snippets/381
> > > * https://gitlab.collabora.com/-/snippets/380
> > >
> > > Following is the v4l2-compliance test result :-
> > >
> > > root@debian-rockchip-rock5b-rk3588:~# v4l2-compliance -d /dev/video0
> > > v4l2-compliance 1.27.0-5220, 64 bits, 64-bit time_t
> > > v4l2-compliance SHA: 8387e3673837 2024-07-01 11:09:32
> > >
> > > Compliance test for snps_hdmirx device /dev/video0:
> > >
> > > Driver Info:
> > > Driver name : snps_hdmirx
> > > Card type : snps_hdmirx
> > > Bus info : platform:fdee0000.hdmi-receiver
> > > Driver version : 6.10.0
> > > Capabilities : 0x84201000
> > > Video Capture Multiplanar
> > > Streaming
> > > Extended Pix Format
> > > Device Capabilities
> > > Device Caps : 0x04201000
> > > Video Capture Multiplanar
> > > Streaming
> > > Extended Pix Format
> > >
> > > Required ioctls:
> > > test VIDIOC_QUERYCAP: OK
> > > test invalid ioctls: OK
> > >
> > > Allow for multiple opens:
> > > test second /dev/video0 open: OK
> > > test VIDIOC_QUERYCAP: OK
> > > test VIDIOC_G/S_PRIORITY: OK
> > > test for unlimited opens: OK
> > >
> > > Debug ioctls:
> > > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> > > test VIDIOC_LOG_STATUS: OK
> > >
> > > Input ioctls:
> > > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > > test VIDIOC_G/S/ENUMINPUT: OK
> > > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > > Inputs: 1 Audio Inputs: 0 Tuners: 0
> > >
> > > Output ioctls:
> > > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > > Outputs: 0 Audio Outputs: 0 Modulators: 0
> > >
> > > Input/Output configuration ioctls:
> > > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
> > > test VIDIOC_DV_TIMINGS_CAP: OK
> > > test VIDIOC_G/S_EDID: OK
> > >
> > > Control ioctls (Input 0):
> > > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > > test VIDIOC_QUERYCTRL: OK
> > > test VIDIOC_G/S_CTRL: OK
> > > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > > Standard Controls: 3 Private Controls: 0
> > >
> > > Format ioctls (Input 0):
> > > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> > > test VIDIOC_G/S_PARM: OK
> > > test VIDIOC_G_FBUF: OK (Not Supported)
> > > test VIDIOC_G_FMT: OK
> > > test VIDIOC_TRY_FMT: OK
> > > test VIDIOC_S_FMT: OK
> > > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > > test Cropping: OK (Not Supported)
> > > test Composing: OK (Not Supported)
> > > test Scaling: OK (Not Supported)
> > >
> > > Codec ioctls (Input 0):
> > > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> > >
> > > Buffer ioctls (Input 0):
> > > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> > > test CREATE_BUFS maximum buffers: OK
> > > test VIDIOC_REMOVE_BUFS: OK
> > > test VIDIOC_EXPBUF: OK
> > > test Requests: OK (Not Supported)
> > >
> > > Total for snps_hdmirx device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
> > >
> > > Changes in v4 :-
> > > - Remove DTS changes included in the device tree patch
> > > - Remove the hdmi rx pin info as it's already present
> > > in the rk3588-base-pinctrl.dtsi
> > > - Create a separate config option for selecting the EDID
> > > and enable it by default
> > > - Improve the comment related to DV timings and move it
> > > to the side of hdmirx_get_detected_timings
> > > - Add 100ms delay before pulling the HPD high
> > > - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> > > - Drop the bus info from hdmirx_querycap
> > > - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> > > - Set queue->min_queued_buffers to 1
> > > - Drop q->allow_cache_hints = 0; as it's always 0 by default
> > > - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> > > - Drop .read = vb2_fop_read as it's not supported by driver
> > > - Remove redundant edid_init_data_600M
> > > - Make HPD low when driver is loaded
> > > - Add support for reading AVI Infoframe
> > > - Remove msg_len checks from hdmirx_cec_transmit
> > > - Add info about the CEC compliance test in the cover letter
> > > - Add arbitration lost status
> > > - Validate the physical address inside the EDID
> > >
> > > Changes in v3 :-
> > > - Use v4l2-common helpers in the HDMIRX driver
> > > - Rename cma node and phandle names
> > > - Elaborate the comment to explain 160MiB calculation
> > > - Move &hdmi_receiver_cma to the rock5b dts file
> > > - Add information about interlaced video testing in the
> > > cover-letter
> > >
> > > Changes in v2 :-
> > > - Fix checkpatch --strict warnings
> > > - Move the dt-binding include file changes in a separate patch
> > > - Add a description for the hardware in the dt-bindings file
> > > - Rename resets, vo1 grf and HPD properties
> > > - Add a proper description for grf and vo1-grf phandles in the
> > > bindings
> > > - Rename the HDMI RX node name to hdmi-receiver
> > > - Include gpio header file in binding example to fix the
> > > dt_binding_check failure
> > > - Move hdmirx_cma node to the rk3588.dtsi file
> > > - Add an entry to MAINTAINERS file for the HDMIRX driver
> > >
> > > Shreeya Patel (4):
> > > MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
> > > dt-bindings: media: Document bindings for HDMI RX Controller
> > > arm64: dts: rockchip: Add device tree support for HDMI RX Controller
> > > media: platform: synopsys: Add support for hdmi input driver
> > >
> > > .../bindings/media/snps,dw-hdmi-rx.yaml | 132 +
> > > MAINTAINERS | 8 +
> > > .../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +
> > > .../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 56 +
> > > drivers/media/platform/Kconfig | 1 +
> > > drivers/media/platform/Makefile | 1 +
> > > drivers/media/platform/synopsys/Kconfig | 3 +
> > > drivers/media/platform/synopsys/Makefile | 2 +
> > > .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> > > .../media/platform/synopsys/hdmirx/Makefile | 4 +
> > > .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> > > .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> > > .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> > > .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> > > 14 files changed, 3734 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> > > create mode 100644 drivers/media/platform/synopsys/Kconfig
> > > create mode 100644 drivers/media/platform/synopsys/Makefile
> > > create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> > > create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> > >
> >
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-28 22:13 ` Tim Surber
@ 2024-08-29 10:03 ` Shreeya Patel
2024-09-06 20:02 ` Nicolas Dufresne
0 siblings, 1 reply; 40+ messages in thread
From: Shreeya Patel @ 2024-08-29 10:03 UTC (permalink / raw)
To: Tim Surber
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, nicolas.dufresne,
hverkuil, hverkuil-cisco, kernel, linux-kernel, linux-media,
devicetree, linux-arm-kernel, linux-rockchip
---- On Thu, 29 Aug 2024 03:43:40 +0530 Tim Surber wrote ---
> Hi Shreeya,
>
> another hint:
>
> Changing the EDID like you described fixed the 4k60fps BGR3 input.
>
> But still for NV16/NV24 (I can't output NV12 for some reason) there is
> the error which Nicolas described.
>
> The output from v4l2-ctl for NV16/NV24 is:
>
> Plane 0 :
> Bytes per Line : 3840
> Size Image : 8294400
>
> According to Nicolas there should be an additional plane/more memory
> reserved.
>
Yes, it could be possible that the imagesize is incorrect as we made some
modifications related to it in v4. I'll increase this as required by the NV12/24
and see if that fixes the issue.
Thanks,
Shreeya Patel
> This leads to errors when trying to read the device using for example
> gstreamer:
>
> gst_memory_resize: assertion 'size + mem->offset + offset <=
> mem->maxsize' failed
>
>
> This seems to always happen using the NV16/NV24 formats regardless of
> resolution/fps.
>
>
> Best regards,
>
> Tim
>
> On 14.08.24 12:22, Shreeya Patel wrote:
> > ---- On Sun, 04 Aug 2024 05:27:08 +0530 Tim Surber wrote ---
> > > Hi Shreeya,
> > >
> >
> > Hi Tim,
> >
> >
> > > I tested your patch and noticed problems when using 3840x2160 resolution
> > > at 60fps.
> > >
> > > For my testing I connected an HDMI source and set it to 4k60fps. I
> > > verified that this source and the cables work on a screen at this
> > > resolution.
> > >
> > > Using
> > > 'v4l2-ctl --verbose -d /dev/video1
> > > --set-fmt-video=width=3840,height=2160,pixelformat='NV12'
> > > --stream-mmap=4 --stream-skip=3 --stream-count=100 --stream-poll'
> > > I get the video format output, but not the periodic output which shows
> > > the fps.
> > >
> > > Using
> > > 'GST_DEBUG=4 gst-launch-1.0 -v v4l2src device=/dev/video1 !
> > > fpsdisplaysink text-overlay=false video-sink="fakevideosink"'
> > > I get the following error message:
> > >
> > > (gst-launch-1.0:3231): GStreamer-CRITICAL **: 01:34:39.137:
> > > gst_memory_resize: assertion 'size + mem->offset + offset <=
> > > mem->maxsize' failed
> > > 0:00:03.489382529 3231 0xffffa0000b90 WARN v4l2bufferpool
> > > gstv4l2bufferpool.c:2209:gst_v4l2_buffer_pool_process:
> > > Dropping truncated buffer, this is likely a driver bug.
> > > 0:00:03.489421906 3231 0xffffa0000b90 WARN bufferpool
> > > gstbufferpool.c:1252:default_reset_buffer: Buffer
> > > 0xffff98008e80 without the memory tag has maxsize (8294400) that is
> > > smaller than the configured buffer pool size (12441600). The buffer will
> > > be not be reused. This is most likely a bug in this GstBufferPool subclass
> > >
> > >
> > > Everything works with 4k30fps or 1080p 60fps. The hardware should
> > > support 4k60fps.
> > >
> >
> >
> > Sorry for the delayed response, I've been trying to reproduce this on my side
> > and to also fix it.
> >
> > It seems you are right, 4K@60 fps doesn't work with the latest version of HDMIRX.
> > We found out that it could be because of the current EDID which shows some failures.
> >
> > Though I wasn't able to test the following on my side since my device doesn't support
> > 4K, one of my colleague tried to replace the EDID and 4K@60 fps worked fine after that.
> >
> > If you'd like to try it yourself then following is the command to get the new EDID
> >
> > v4l2-ctl --show-edid type=hdmi-4k-600mhz
> >
> > You will have to replace the EDID in the driver with the EDID you get the from the above
> > command in HEX format.
> >
> > Thanks for reporting this, I will soon send v5 with this change included in it.
> >
> >
> > Thanks,
> > Shreeya Patel
> >
> >
> > > Best regards,
> > > Tim
> > >
> > > On 19.07.24 14:40, Shreeya Patel wrote:
> > > > This series implements support for the Synopsys DesignWare
> > > > HDMI RX Controller, being compliant with standard HDMI 1.4b
> > > > and HDMI 2.0.
> > > >
> > > > Features that are currently supported by the HDMI RX driver
> > > > have been tested on rock5b board using a HDMI to micro-HDMI cable.
> > > > It is recommended to use a good quality cable as there were
> > > > multiple issues seen during testing the driver.
> > > >
> > > > Please note the below information :-
> > > > * While testing the driver on rock5b we noticed that the binary BL31
> > > > from Rockchip contains some unknown code to get the HDMI-RX PHY
> > > > access working without any errors.
> > > > With TF-A BL31, the HDMI-RX PHY also works fine but there were no
> > > > interrupts seen for rk_hdmirx-hdmi leading to some errors when
> > > > loading the driver [0]. It doesn't affect the functionality of the
> > > > driver though.
> > > > * We have tested the working of OBS studio with HDMIRX driver and
> > > > there were no issues seen.
> > > > * We also tested and verified the support for interlaced video.
> > > >
> > > > [0] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/trusted-firmware-a/-/issues/1
> > > >
> > > > To test the HDMI RX Controller driver, following example commands can be used :-
> > > >
> > > > root@debian-rockchip-rock5b-rk3588:~# v4l2-ctl --verbose -d /dev/video0 \
> > > > --set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 \
> > > > --stream-skip=3 --stream-count=100 --stream-to=/home/hdmiin4k.raw --stream-poll
> > > >
> > > > root@debian-rockchip-rock5b-rk3588:~# ffmpeg -f rawvideo -vcodec rawvideo \
> > > > -s 1920x1080 -r 60 -pix_fmt bgr24 -i /home/hdmiin4k.raw output.mkv
> > > >
> > > > CEC compliance test results :-
> > > >
> > > > * https://gitlab.collabora.com/-/snippets/381
> > > > * https://gitlab.collabora.com/-/snippets/380
> > > >
> > > > Following is the v4l2-compliance test result :-
> > > >
> > > > root@debian-rockchip-rock5b-rk3588:~# v4l2-compliance -d /dev/video0
> > > > v4l2-compliance 1.27.0-5220, 64 bits, 64-bit time_t
> > > > v4l2-compliance SHA: 8387e3673837 2024-07-01 11:09:32
> > > >
> > > > Compliance test for snps_hdmirx device /dev/video0:
> > > >
> > > > Driver Info:
> > > > Driver name : snps_hdmirx
> > > > Card type : snps_hdmirx
> > > > Bus info : platform:fdee0000.hdmi-receiver
> > > > Driver version : 6.10.0
> > > > Capabilities : 0x84201000
> > > > Video Capture Multiplanar
> > > > Streaming
> > > > Extended Pix Format
> > > > Device Capabilities
> > > > Device Caps : 0x04201000
> > > > Video Capture Multiplanar
> > > > Streaming
> > > > Extended Pix Format
> > > >
> > > > Required ioctls:
> > > > test VIDIOC_QUERYCAP: OK
> > > > test invalid ioctls: OK
> > > >
> > > > Allow for multiple opens:
> > > > test second /dev/video0 open: OK
> > > > test VIDIOC_QUERYCAP: OK
> > > > test VIDIOC_G/S_PRIORITY: OK
> > > > test for unlimited opens: OK
> > > >
> > > > Debug ioctls:
> > > > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
> > > > test VIDIOC_LOG_STATUS: OK
> > > >
> > > > Input ioctls:
> > > > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
> > > > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > > > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
> > > > test VIDIOC_ENUMAUDIO: OK (Not Supported)
> > > > test VIDIOC_G/S/ENUMINPUT: OK
> > > > test VIDIOC_G/S_AUDIO: OK (Not Supported)
> > > > Inputs: 1 Audio Inputs: 0 Tuners: 0
> > > >
> > > > Output ioctls:
> > > > test VIDIOC_G/S_MODULATOR: OK (Not Supported)
> > > > test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
> > > > test VIDIOC_ENUMAUDOUT: OK (Not Supported)
> > > > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
> > > > test VIDIOC_G/S_AUDOUT: OK (Not Supported)
> > > > Outputs: 0 Audio Outputs: 0 Modulators: 0
> > > >
> > > > Input/Output configuration ioctls:
> > > > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
> > > > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK
> > > > test VIDIOC_DV_TIMINGS_CAP: OK
> > > > test VIDIOC_G/S_EDID: OK
> > > >
> > > > Control ioctls (Input 0):
> > > > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
> > > > test VIDIOC_QUERYCTRL: OK
> > > > test VIDIOC_G/S_CTRL: OK
> > > > test VIDIOC_G/S/TRY_EXT_CTRLS: OK
> > > > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
> > > > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > > > Standard Controls: 3 Private Controls: 0
> > > >
> > > > Format ioctls (Input 0):
> > > > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
> > > > test VIDIOC_G/S_PARM: OK
> > > > test VIDIOC_G_FBUF: OK (Not Supported)
> > > > test VIDIOC_G_FMT: OK
> > > > test VIDIOC_TRY_FMT: OK
> > > > test VIDIOC_S_FMT: OK
> > > > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
> > > > test Cropping: OK (Not Supported)
> > > > test Composing: OK (Not Supported)
> > > > test Scaling: OK (Not Supported)
> > > >
> > > > Codec ioctls (Input 0):
> > > > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
> > > > test VIDIOC_G_ENC_INDEX: OK (Not Supported)
> > > > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
> > > >
> > > > Buffer ioctls (Input 0):
> > > > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
> > > > test CREATE_BUFS maximum buffers: OK
> > > > test VIDIOC_REMOVE_BUFS: OK
> > > > test VIDIOC_EXPBUF: OK
> > > > test Requests: OK (Not Supported)
> > > >
> > > > Total for snps_hdmirx device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0
> > > >
> > > > Changes in v4 :-
> > > > - Remove DTS changes included in the device tree patch
> > > > - Remove the hdmi rx pin info as it's already present
> > > > in the rk3588-base-pinctrl.dtsi
> > > > - Create a separate config option for selecting the EDID
> > > > and enable it by default
> > > > - Improve the comment related to DV timings and move it
> > > > to the side of hdmirx_get_detected_timings
> > > > - Add 100ms delay before pulling the HPD high
> > > > - Do not return the detected timings from VIDIOC_G_DV_TIMINGS
> > > > - Drop the bus info from hdmirx_querycap
> > > > - If *num_planes != 0 then return 0 in hdmirx_queue_setup
> > > > - Set queue->min_queued_buffers to 1
> > > > - Drop q->allow_cache_hints = 0; as it's always 0 by default
> > > > - Add a comment for q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
> > > > - Drop .read = vb2_fop_read as it's not supported by driver
> > > > - Remove redundant edid_init_data_600M
> > > > - Make HPD low when driver is loaded
> > > > - Add support for reading AVI Infoframe
> > > > - Remove msg_len checks from hdmirx_cec_transmit
> > > > - Add info about the CEC compliance test in the cover letter
> > > > - Add arbitration lost status
> > > > - Validate the physical address inside the EDID
> > > >
> > > > Changes in v3 :-
> > > > - Use v4l2-common helpers in the HDMIRX driver
> > > > - Rename cma node and phandle names
> > > > - Elaborate the comment to explain 160MiB calculation
> > > > - Move &hdmi_receiver_cma to the rock5b dts file
> > > > - Add information about interlaced video testing in the
> > > > cover-letter
> > > >
> > > > Changes in v2 :-
> > > > - Fix checkpatch --strict warnings
> > > > - Move the dt-binding include file changes in a separate patch
> > > > - Add a description for the hardware in the dt-bindings file
> > > > - Rename resets, vo1 grf and HPD properties
> > > > - Add a proper description for grf and vo1-grf phandles in the
> > > > bindings
> > > > - Rename the HDMI RX node name to hdmi-receiver
> > > > - Include gpio header file in binding example to fix the
> > > > dt_binding_check failure
> > > > - Move hdmirx_cma node to the rk3588.dtsi file
> > > > - Add an entry to MAINTAINERS file for the HDMIRX driver
> > > >
> > > > Shreeya Patel (4):
> > > > MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver
> > > > dt-bindings: media: Document bindings for HDMI RX Controller
> > > > arm64: dts: rockchip: Add device tree support for HDMI RX Controller
> > > > media: platform: synopsys: Add support for hdmi input driver
> > > >
> > > > .../bindings/media/snps,dw-hdmi-rx.yaml | 132 +
> > > > MAINTAINERS | 8 +
> > > > .../dts/rockchip/rk3588-base-pinctrl.dtsi | 14 +
> > > > .../arm64/boot/dts/rockchip/rk3588-extra.dtsi | 56 +
> > > > drivers/media/platform/Kconfig | 1 +
> > > > drivers/media/platform/Makefile | 1 +
> > > > drivers/media/platform/synopsys/Kconfig | 3 +
> > > > drivers/media/platform/synopsys/Makefile | 2 +
> > > > .../media/platform/synopsys/hdmirx/Kconfig | 27 +
> > > > .../media/platform/synopsys/hdmirx/Makefile | 4 +
> > > > .../platform/synopsys/hdmirx/snps_hdmirx.c | 2763 +++++++++++++++++
> > > > .../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
> > > > .../synopsys/hdmirx/snps_hdmirx_cec.c | 285 ++
> > > > .../synopsys/hdmirx/snps_hdmirx_cec.h | 44 +
> > > > 14 files changed, 3734 insertions(+)
> > > > create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
> > > > create mode 100644 drivers/media/platform/synopsys/Kconfig
> > > > create mode 100644 drivers/media/platform/synopsys/Makefile
> > > > create mode 100644 drivers/media/platform/synopsys/hdmirx/Kconfig
> > > > create mode 100644 drivers/media/platform/synopsys/hdmirx/Makefile
> > > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
> > > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
> > > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
> > > > create mode 100644 drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
> > > >
> > >
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller
2024-08-29 10:03 ` Shreeya Patel
@ 2024-09-06 20:02 ` Nicolas Dufresne
0 siblings, 0 replies; 40+ messages in thread
From: Nicolas Dufresne @ 2024-09-06 20:02 UTC (permalink / raw)
To: Shreeya Patel, Tim Surber
Cc: heiko, mchehab, robh, krzk+dt, conor+dt, mturquette, sboyd,
p.zabel, jose.abreu, nelson.costa, shawn.wen, hverkuil,
hverkuil-cisco, kernel, linux-kernel, linux-media, devicetree,
linux-arm-kernel, linux-rockchip
Hi Shreya,
Le jeudi 29 août 2024 à 15:33 +0530, Shreeya Patel a écrit :
> ---- On Thu, 29 Aug 2024 03:43:40 +0530 Tim Surber wrote ---
> > Hi Shreeya,
> >
> > another hint:
> >
> > Changing the EDID like you described fixed the 4k60fps BGR3 input.
> >
> > But still for NV16/NV24 (I can't output NV12 for some reason) there is
> > the error which Nicolas described.
> >
> > The output from v4l2-ctl for NV16/NV24 is:
> >
> > Plane 0 :
> > Bytes per Line : 3840
> > Size Image : 8294400
> >
> > According to Nicolas there should be an additional plane/more memory
> > reserved.
> >
>
> Yes, it could be possible that the imagesize is incorrect as we made some
> modifications related to it in v4. I'll increase this as required by the NV12/24
> and see if that fixes the issue.
Sorry for the slow feedback, just noticed this message. Please make sure to us
v4l2-common, these exists exactly because no one can get strides and image size
right on first go.
Nicolas
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-23 8:48 ` Hans Verkuil
@ 2024-09-23 22:24 ` Dmitry Osipenko
2024-09-23 22:36 ` Dmitry Osipenko
1 sibling, 0 replies; 40+ messages in thread
From: Dmitry Osipenko @ 2024-09-23 22:24 UTC (permalink / raw)
To: Hans Verkuil, Shreeya Patel, heiko, mchehab, robh, krzk+dt,
conor+dt, mturquette, sboyd, p.zabel, jose.abreu, nelson.costa,
shawn.wen, nicolas.dufresne, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
On 7/23/24 11:48, Hans Verkuil wrote:
>> +static u8 edid_init_data_340M[] = {
> This should be under #ifdef CONFIG_HDMIRX_LOAD_DEFAULT_EDID, since there is
> no point to have this if you are not using it.
No need to use #ifdef since EDID array will be compiled out at a build
time. Kernel doesn't support old compilers that can't eliminate dead code.
#ifdef makes code less readable and adds requirement to build-test all
variants with/without the #ifdef, we don't want either of these.
--
Best regards,
Dmitry
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver
2024-07-23 8:48 ` Hans Verkuil
2024-09-23 22:24 ` Dmitry Osipenko
@ 2024-09-23 22:36 ` Dmitry Osipenko
1 sibling, 0 replies; 40+ messages in thread
From: Dmitry Osipenko @ 2024-09-23 22:36 UTC (permalink / raw)
To: Hans Verkuil, Shreeya Patel, heiko, mchehab, robh, krzk+dt,
conor+dt, mturquette, sboyd, p.zabel, jose.abreu, nelson.costa,
shawn.wen, nicolas.dufresne, hverkuil-cisco
Cc: kernel, linux-kernel, linux-media, devicetree, linux-arm-kernel,
linux-rockchip
On 7/23/24 11:48, Hans Verkuil wrote:
>> + val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
>> + field_type = (val & HDMIRX_TYPE_MASK) >> 7;
>> + hdmirx_get_pix_fmt(hdmirx_dev);
> Hold on, this changes the pixel format based on the detected video format.
> Does that mean this hardware does not have a colorspace conversion block?
> All the HDMI receiver hardware I have seen always had a CSC matrix and
> could convert between 4:4:4, 4:2:2 and 4:2:0.
TRM doesn't mention that CSC is supported.
--
Best regards,
Dmitry
^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2024-09-23 22:37 UTC | newest]
Thread overview: 40+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-19 12:40 [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 1/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI RX Driver Shreeya Patel
2024-07-19 12:47 ` Christopher Obbard
2024-07-19 12:40 ` [PATCH v4 2/4] dt-bindings: media: Document bindings for HDMI RX Controller Shreeya Patel
2024-07-19 22:10 ` Rob Herring (Arm)
2024-07-25 9:46 ` Shreeya Patel
2024-07-20 10:44 ` Johan Jonker
2024-07-22 13:53 ` Shreeya Patel
2024-07-23 11:16 ` Johan Jonker
2024-07-23 17:28 ` Sebastian Reichel
2024-07-24 13:20 ` Johan Jonker
2024-07-25 6:35 ` Krzysztof Kozlowski
2024-07-25 6:38 ` Krzysztof Kozlowski
2024-07-25 7:58 ` AngeloGioacchino Del Regno
2024-07-25 14:10 ` Sebastian Reichel
2024-08-25 7:03 ` Krzysztof Kozlowski
2024-08-26 8:19 ` Michael Riesch
2024-07-19 12:40 ` [PATCH v4 3/4] arm64: dts: rockchip: Add device tree support " Shreeya Patel
2024-07-19 12:40 ` [PATCH v4 4/4] media: platform: synopsys: Add support for hdmi input driver Shreeya Patel
2024-07-20 11:33 ` Johan Jonker
2024-07-22 14:19 ` Shreeya Patel
2024-07-20 23:43 ` George Stark
2024-07-21 8:51 ` Hans Verkuil
2024-07-25 9:56 ` Shreeya Patel
2024-07-21 15:06 ` Markus Elfring
2024-07-23 8:48 ` Hans Verkuil
2024-09-23 22:24 ` Dmitry Osipenko
2024-09-23 22:36 ` Dmitry Osipenko
2024-07-22 19:39 ` hoff.benjamin.k
2024-07-22 20:26 ` Shreeya Patel
2024-08-03 23:57 ` [PATCH v4 0/4] Add Synopsys DesignWare HDMI RX Controller Tim Surber
2024-08-05 17:02 ` Nicolas Dufresne
2024-08-06 11:58 ` Dmitry Osipenko
2024-08-06 20:37 ` Tim Surber
2024-08-06 21:17 ` Nicolas Dufresne
2024-08-14 10:22 ` Shreeya Patel
2024-08-24 23:03 ` Tim Surber
2024-08-28 22:13 ` Tim Surber
2024-08-29 10:03 ` Shreeya Patel
2024-09-06 20:02 ` Nicolas Dufresne
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).