12674 lines
400 KiB
Diff
12674 lines
400 KiB
Diff
|
From 6b7104d36138a01859f23a81a565f1bbba89ecac Mon Sep 17 00:00:00 2001
|
||
|
From: Christopher Obbard <chris.obbard@collabora.com>
|
||
|
Date: Mon, 20 Feb 2023 16:59:04 +0000
|
||
|
Subject: [PATCH 01/54] [NOUPSTREAM] Add GitLab CI support
|
||
|
|
||
|
Add CI support. This will do the following:
|
||
|
|
||
|
1. Run dt_binding_check to make sure no major flaws were introduced in
|
||
|
the DT bindings
|
||
|
2. Run dtbs_check, for Rock 5A, Rock 5B and EVB1. If warnings are
|
||
|
generated the CI will report that as warning
|
||
|
3. Build a Kernel .deb package
|
||
|
4. Generate a test job for LAVA and run it
|
||
|
|
||
|
Co-developed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Co-developed-by: Sjoerd Simons <sjoerd@collabora.com>
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Signed-off-by: Sjoerd Simons <sjoerd@collabora.com>
|
||
|
Signed-off-by: Christopher Obbard <chris.obbard@collabora.com>
|
||
|
---
|
||
|
.gitignore | 1 +
|
||
|
.gitlab-ci.yml | 142 ++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
lava/testjob.yaml | 73 ++++++++++++++++++++++++
|
||
|
3 files changed, 216 insertions(+)
|
||
|
create mode 100644 .gitlab-ci.yml
|
||
|
create mode 100644 lava/testjob.yaml
|
||
|
|
||
|
diff --git a/.gitignore b/.gitignore
|
||
|
index c59dc60ba62e..59aa4da11b89 100644
|
||
|
--- a/.gitignore
|
||
|
+++ b/.gitignore
|
||
|
@@ -104,6 +104,7 @@ modules.order
|
||
|
!.kunitconfig
|
||
|
!.mailmap
|
||
|
!.rustfmt.toml
|
||
|
+!.gitlab-ci.yml
|
||
|
|
||
|
#
|
||
|
# Generated include files
|
||
|
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
|
||
|
new file mode 100644
|
||
|
index 000000000000..5582b71f0c67
|
||
|
--- /dev/null
|
||
|
+++ b/.gitlab-ci.yml
|
||
|
@@ -0,0 +1,142 @@
|
||
|
+default:
|
||
|
+ image: debian:testing
|
||
|
+ tags:
|
||
|
+ - bookworm
|
||
|
+
|
||
|
+stages:
|
||
|
+ - build
|
||
|
+ - test
|
||
|
+ - generate
|
||
|
+ - lava
|
||
|
+
|
||
|
+check devicetrees:
|
||
|
+ stage: build
|
||
|
+ tags:
|
||
|
+ - ultra-heavyweight
|
||
|
+ variables:
|
||
|
+ DEBIAN_FRONTEND: noninteractive
|
||
|
+ GIT_SUBMODULE_STRATEGY: normal
|
||
|
+ ARCH: arm64
|
||
|
+ DEFCONFIG: defconfig
|
||
|
+ CROSS_COMPILE: aarch64-linux-gnu-
|
||
|
+ DUT: rockchip/rk3588s-rock-5a.dtb rockchip/rk3588-rock-5b.dtb rockchip/rk3588-evb1-v10.dtb
|
||
|
+ before_script:
|
||
|
+ - apt update
|
||
|
+ - apt install -y devscripts
|
||
|
+ build-essential
|
||
|
+ crossbuild-essential-arm64
|
||
|
+ bc
|
||
|
+ bison
|
||
|
+ flex
|
||
|
+ swig
|
||
|
+ python3-dev
|
||
|
+ python3-pip
|
||
|
+ yamllint
|
||
|
+ - pip3 install --break-system-packages dtschema
|
||
|
+ script:
|
||
|
+ - make $DEFCONFIG
|
||
|
+ - make -j$(nproc) dt_binding_check
|
||
|
+ - make -j$(nproc) CHECK_DTBS=y $DUT 2> dt-warnings.txt
|
||
|
+ - if [[ $(cat dt-warnings.txt | wc -l) > 0 ]]; then cat dt-warnings.txt; exit 42; fi
|
||
|
+ allow_failure:
|
||
|
+ exit_codes:
|
||
|
+ - 42
|
||
|
+
|
||
|
+.build debian package:
|
||
|
+ stage: build
|
||
|
+ tags:
|
||
|
+ - ultra-heavyweight
|
||
|
+ cache:
|
||
|
+ when: on_success
|
||
|
+ key: $CI_COMMIT_REF_SLUG
|
||
|
+ paths:
|
||
|
+ - ccache
|
||
|
+ variables:
|
||
|
+ DEBIAN_FRONTEND: noninteractive
|
||
|
+ GIT_SUBMODULE_STRATEGY: normal
|
||
|
+ ARCH: amd64
|
||
|
+ DEFCONFIG: defconfig
|
||
|
+ CCACHE_BASEDIR: $CI_PROJECT_DIR
|
||
|
+ CCACHE_DIR: $CI_PROJECT_DIR/ccache
|
||
|
+ before_script:
|
||
|
+ - apt update
|
||
|
+ - apt install -y devscripts
|
||
|
+ build-essential
|
||
|
+ crossbuild-essential-arm64
|
||
|
+ bc
|
||
|
+ bison
|
||
|
+ ccache
|
||
|
+ flex
|
||
|
+ rsync
|
||
|
+ kmod
|
||
|
+ cpio
|
||
|
+ libelf-dev
|
||
|
+ libssl-dev
|
||
|
+ # Setup ccache
|
||
|
+ - export PATH="/usr/lib/ccache:$PATH"
|
||
|
+ - ccache -s
|
||
|
+ script:
|
||
|
+ - make $DEFCONFIG
|
||
|
+ - ./scripts/config -e WLAN -e WLAN_VENDOR_BROADCOM -m BRCMUTIL -m BRCMFMAC
|
||
|
+ -e BRCMFMAC_PROTO_BCDC -e BRCMFMAC_PROTO_MSGBUF
|
||
|
+ -e BRCMFMAC_USB
|
||
|
+ -e WLAN_VENDOR_REALTEK -m RTW89 -m RTW89_CORE
|
||
|
+ -m RTW89_PCI -m RTW89_8825B -m RTW89_8852BE
|
||
|
+ -m BINFMT_MISC
|
||
|
+ - make -j$(nproc) $ADDITIONAL_BUILD_CMD bindeb-pkg
|
||
|
+ - mkdir artifacts && dcmd mv ../*.changes artifacts/
|
||
|
+ artifacts:
|
||
|
+ paths:
|
||
|
+ - artifacts
|
||
|
+
|
||
|
+build arm64 debian package:
|
||
|
+ extends: .build debian package
|
||
|
+ variables:
|
||
|
+ ARCH: arm64
|
||
|
+ CROSS_COMPILE: aarch64-linux-gnu-
|
||
|
+ ADDITIONAL_BUILD_CMD: KBUILD_IMAGE=arch/arm64/boot/Image
|
||
|
+
|
||
|
+generate tests:
|
||
|
+ image: debian:bookworm-slim
|
||
|
+ stage: generate
|
||
|
+ tags:
|
||
|
+ - lightweight
|
||
|
+ variables:
|
||
|
+ GIT_STRATEGY: fetch
|
||
|
+ GIT_DEPTH: "1"
|
||
|
+ needs:
|
||
|
+ - "build arm64 debian package"
|
||
|
+ before_script:
|
||
|
+ - apt update
|
||
|
+ - apt install -y wget
|
||
|
+ script:
|
||
|
+ - mkdir deb
|
||
|
+ - "for x in artifacts/linux-image*.deb ; do dpkg -x ${x} deb ; done"
|
||
|
+ - cp deb/boot/vmlinuz* vmlinuz
|
||
|
+ - mkdir -p deb/lib/firmware/arm/mali/arch10.8
|
||
|
+ - wget -O deb/lib/firmware/arm/mali/arch10.8/mali_csffw.bin "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/arm/mali/arch10.8/mali_csffw.bin"
|
||
|
+ - tar -f modules.tar.gz -C deb -c -z -v lib/modules lib/firmware
|
||
|
+ - mkdir dtbs
|
||
|
+ - cp -r deb/usr/lib/linux-image*/* dtbs
|
||
|
+ - sed -i s,%%KERNEL_BUILD_JOB%%,${CI_JOB_ID},g lava/testjob.yaml
|
||
|
+ artifacts:
|
||
|
+ paths:
|
||
|
+ - vmlinuz*
|
||
|
+ - modules.tar.gz
|
||
|
+ - dtbs
|
||
|
+ - lava/testjob.yaml
|
||
|
+
|
||
|
+lava test:
|
||
|
+ stage: lava
|
||
|
+ tags:
|
||
|
+ - lava-runner
|
||
|
+ script:
|
||
|
+ - submit lava/testjob.yaml
|
||
|
+ needs:
|
||
|
+ - "generate tests"
|
||
|
+ artifacts:
|
||
|
+ when: always
|
||
|
+ paths:
|
||
|
+ - "*"
|
||
|
+ reports:
|
||
|
+ junit: "*.xml"
|
||
|
diff --git a/lava/testjob.yaml b/lava/testjob.yaml
|
||
|
new file mode 100644
|
||
|
index 000000000000..8dfaf772296c
|
||
|
--- /dev/null
|
||
|
+++ b/lava/testjob.yaml
|
||
|
@@ -0,0 +1,73 @@
|
||
|
+device_type: rk3588-rock-5b
|
||
|
+
|
||
|
+job_name: Hardware enablement tests {{job.CI_JOB_ID}}
|
||
|
+timeouts:
|
||
|
+ job:
|
||
|
+ minutes: 15
|
||
|
+ action:
|
||
|
+ minutes: 5
|
||
|
+priority: high
|
||
|
+visibility: public
|
||
|
+
|
||
|
+context:
|
||
|
+ extra_kernel_args: rootwait
|
||
|
+
|
||
|
+actions:
|
||
|
+ - deploy:
|
||
|
+ timeout:
|
||
|
+ minutes: 2
|
||
|
+ to: tftp
|
||
|
+ kernel:
|
||
|
+ url: "{{job.CI_PROJECT_URL}}/-/jobs/%%KERNEL_BUILD_JOB%%/artifacts/raw/vmlinuz"
|
||
|
+ type: image
|
||
|
+ modules:
|
||
|
+ url: "{{job.CI_PROJECT_URL}}/-/jobs/%%KERNEL_BUILD_JOB%%/artifacts/raw/modules.tar.gz"
|
||
|
+ compression: gz
|
||
|
+ dtb:
|
||
|
+ url: "{{job.CI_PROJECT_URL}}/-/jobs/%%KERNEL_BUILD_JOB%%/artifacts/raw/dtbs/rockchip/rk3588-rock-5b.dtb"
|
||
|
+ ramdisk:
|
||
|
+ url: https://gitlab.collabora.com/lava/health-check-images/-/jobs/artifacts/bookworm/raw/bookworm/bookworm-rootfs-arm64-initramfs.gz?job=build+bookworm+image:+%5Barm64,+rootfs%5D
|
||
|
+ compression: gz
|
||
|
+ nfsrootfs:
|
||
|
+ url: https://gitlab.collabora.com/lava/health-check-images/-/jobs/artifacts/bookworm/raw/bookworm/bookworm-rootfs-arm64.tar.gz?job=build+bookworm+image:+%5Barm64,+rootfs%5D
|
||
|
+ compression: gz
|
||
|
+
|
||
|
+ - boot:
|
||
|
+ method: u-boot
|
||
|
+ commands: nfs
|
||
|
+ timeout:
|
||
|
+ minutes: 10
|
||
|
+ auto_login:
|
||
|
+ login_prompt: 'login:'
|
||
|
+ username: user
|
||
|
+ password_prompt: 'Password:'
|
||
|
+ password: user
|
||
|
+ login_commands:
|
||
|
+ - sudo su
|
||
|
+ - env
|
||
|
+ - systemctl --failed
|
||
|
+ prompts:
|
||
|
+ - 'user@health(.*)$'
|
||
|
+ - 'root@health(.*)#'
|
||
|
+
|
||
|
+ - test:
|
||
|
+ timeout:
|
||
|
+ minutes: 1
|
||
|
+ definitions:
|
||
|
+ - repository:
|
||
|
+ metadata:
|
||
|
+ format: Lava-Test Test Definition 1.0
|
||
|
+ name: health
|
||
|
+ description: "health check"
|
||
|
+ os:
|
||
|
+ - apertis
|
||
|
+ scope:
|
||
|
+ - functional
|
||
|
+ environment:
|
||
|
+ - lava-test-shell
|
||
|
+ run:
|
||
|
+ steps:
|
||
|
+ - ip a s
|
||
|
+ from: inline
|
||
|
+ name: network
|
||
|
+ path: inline/health.yaml
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From f851992e8c9758ccd3720b2b0a18a5f6a81bd820 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 25 Jul 2023 18:35:56 +0200
|
||
|
Subject: [PATCH 02/54] arm64: dts: rockchip: rk3588-rock5b: add USB-C support
|
||
|
|
||
|
Add hardware description for the USB-C port in the Radxa Rock 5 Model B.
|
||
|
This describes the OHCI, EHCI and XHCI USB parts, but not yet the
|
||
|
DisplayPort AltMode (bindings are not yet upstream).
|
||
|
|
||
|
For now the fusb302 node is marked with status "fail", since the board
|
||
|
is usually powered through the USB-C port. Handling of errors can result
|
||
|
in hard resets, which removed the bus power for some time resulting in
|
||
|
a board reset.
|
||
|
|
||
|
The main problem right now is that devices are supposed to interact with
|
||
|
the power-supply within 5 seconds after the plug event according to the
|
||
|
USB PD specification. This is more or less impossible to achieve when
|
||
|
the kernel is the first software communicating with the power-supply.
|
||
|
|
||
|
Currently the most likely workaround will be USB-PD handling added to
|
||
|
U-Boot. In that case U-Boot can update the status to "okay". That way
|
||
|
booting a kernel with the updated DT on an old U-Boot avoids a reset
|
||
|
loop.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
.../boot/dts/rockchip/rk3588-rock-5b.dts | 121 ++++++++++++++++++
|
||
|
1 file changed, 121 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
index b8e15b76a8a6..0c4359f1b97e 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
@@ -4,6 +4,7 @@
|
||
|
|
||
|
#include <dt-bindings/gpio/gpio.h>
|
||
|
#include <dt-bindings/leds/common.h>
|
||
|
+#include <dt-bindings/usb/pd.h>
|
||
|
#include "rk3588.dtsi"
|
||
|
|
||
|
/ {
|
||
|
@@ -65,6 +66,15 @@ rfkill {
|
||
|
shutdown-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>;
|
||
|
};
|
||
|
|
||
|
+ vcc12v_dcin: vcc12v-dcin-regulator {
|
||
|
+ compatible = "regulator-fixed";
|
||
|
+ regulator-name = "vcc12v_dcin";
|
||
|
+ regulator-always-on;
|
||
|
+ regulator-boot-on;
|
||
|
+ regulator-min-microvolt = <12000000>;
|
||
|
+ regulator-max-microvolt = <12000000>;
|
||
|
+ };
|
||
|
+
|
||
|
vcc3v3_pcie2x1l0: vcc3v3-pcie2x1l0-regulator {
|
||
|
compatible = "regulator-fixed";
|
||
|
enable-active-high;
|
||
|
@@ -123,6 +133,7 @@ vcc5v0_sys: vcc5v0-sys-regulator {
|
||
|
regulator-boot-on;
|
||
|
regulator-min-microvolt = <5000000>;
|
||
|
regulator-max-microvolt = <5000000>;
|
||
|
+ vin-supply = <&vcc12v_dcin>;
|
||
|
};
|
||
|
|
||
|
vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator {
|
||
|
@@ -225,6 +236,67 @@ regulator-state-mem {
|
||
|
};
|
||
|
};
|
||
|
|
||
|
+&i2c4 {
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&i2c4m1_xfer>;
|
||
|
+ status = "okay";
|
||
|
+
|
||
|
+ usbc0: usb-typec@22 {
|
||
|
+ compatible = "fcs,fusb302";
|
||
|
+ reg = <0x22>;
|
||
|
+ interrupt-parent = <&gpio3>;
|
||
|
+ interrupts = <RK_PB4 IRQ_TYPE_LEVEL_LOW>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&usbc0_int>;
|
||
|
+ vbus-supply = <&vcc12v_dcin>;
|
||
|
+ /*
|
||
|
+ * When the board is starting to send power-delivery messages
|
||
|
+ * too late (5 seconds according to the specification), the
|
||
|
+ * power-supply reacts with a hard-reset. That removes the
|
||
|
+ * power from VBUS for some time, which resets te whole board.
|
||
|
+ */
|
||
|
+ status = "fail";
|
||
|
+
|
||
|
+ usb_con: connector {
|
||
|
+ compatible = "usb-c-connector";
|
||
|
+ label = "USB-C";
|
||
|
+ data-role = "dual";
|
||
|
+ power-role = "sink";
|
||
|
+ try-power-role = "sink";
|
||
|
+ op-sink-microwatt = <1000000>;
|
||
|
+ sink-pdos =
|
||
|
+ <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>,
|
||
|
+ <PDO_VAR(5000, 20000, 5000)>;
|
||
|
+
|
||
|
+ ports {
|
||
|
+ #address-cells = <1>;
|
||
|
+ #size-cells = <0>;
|
||
|
+
|
||
|
+ port@0 {
|
||
|
+ reg = <0>;
|
||
|
+ usbc0_orien_sw: endpoint {
|
||
|
+ remote-endpoint = <&usbdp_phy0_orientation_switch>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ port@1 {
|
||
|
+ reg = <1>;
|
||
|
+ usbc0_role_sw: endpoint {
|
||
|
+ remote-endpoint = <&dwc3_0_role_switch>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ port@2 {
|
||
|
+ reg = <2>;
|
||
|
+ dp_altmode_mux: endpoint {
|
||
|
+ remote-endpoint = <&usbdp_phy0_dp_altmode_mux>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
&i2c6 {
|
||
|
status = "okay";
|
||
|
|
||
|
@@ -354,6 +426,10 @@ usb {
|
||
|
vcc5v0_host_en: vcc5v0-host-en {
|
||
|
rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
};
|
||
|
+
|
||
|
+ usbc0_int: usbc0-int {
|
||
|
+ rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
+ };
|
||
|
};
|
||
|
};
|
||
|
|
||
|
@@ -747,6 +823,14 @@ &uart2 {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&u2phy0 {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&u2phy0_otg {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
&u2phy1 {
|
||
|
status = "okay";
|
||
|
};
|
||
|
@@ -778,6 +862,29 @@ &usbdp_phy1 {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&usbdp_phy0 {
|
||
|
+ mode-switch;
|
||
|
+ orientation-switch;
|
||
|
+ sbu1-dc-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>;
|
||
|
+ sbu2-dc-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_HIGH>;
|
||
|
+ status = "okay";
|
||
|
+
|
||
|
+ port {
|
||
|
+ #address-cells = <1>;
|
||
|
+ #size-cells = <0>;
|
||
|
+
|
||
|
+ usbdp_phy0_orientation_switch: endpoint@0 {
|
||
|
+ reg = <0>;
|
||
|
+ remote-endpoint = <&usbc0_orien_sw>;
|
||
|
+ };
|
||
|
+
|
||
|
+ usbdp_phy0_dp_altmode_mux: endpoint@1 {
|
||
|
+ reg = <1>;
|
||
|
+ remote-endpoint = <&dp_altmode_mux>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
&usb_host0_ehci {
|
||
|
status = "okay";
|
||
|
};
|
||
|
@@ -786,6 +893,20 @@ &usb_host0_ohci {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&usb_host0_xhci {
|
||
|
+ usb-role-switch;
|
||
|
+ status = "okay";
|
||
|
+
|
||
|
+ port {
|
||
|
+ #address-cells = <1>;
|
||
|
+ #size-cells = <0>;
|
||
|
+
|
||
|
+ dwc3_0_role_switch: endpoint {
|
||
|
+ remote-endpoint = <&usbc0_role_sw>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
&usb_host1_ehci {
|
||
|
status = "okay";
|
||
|
};
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From eb006b9c23a873d2d7dbeb1af0c0d9418d35fd31 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 24 Oct 2023 16:09:35 +0200
|
||
|
Subject: [PATCH 03/54] math.h: add DIV_ROUND_UP_NO_OVERFLOW
|
||
|
|
||
|
Add a new DIV_ROUND_UP helper, which cannot overflow when
|
||
|
big numbers are being used.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
include/linux/math.h | 11 +++++++++++
|
||
|
1 file changed, 11 insertions(+)
|
||
|
|
||
|
diff --git a/include/linux/math.h b/include/linux/math.h
|
||
|
index dd4152711de7..f80bfb375ab9 100644
|
||
|
--- a/include/linux/math.h
|
||
|
+++ b/include/linux/math.h
|
||
|
@@ -36,6 +36,17 @@
|
||
|
|
||
|
#define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP
|
||
|
|
||
|
+/**
|
||
|
+ * DIV_ROUND_UP_NO_OVERFLOW - divide two numbers and always round up
|
||
|
+ * @n: numerator / dividend
|
||
|
+ * @d: denominator / divisor
|
||
|
+ *
|
||
|
+ * This functions does the same as DIV_ROUND_UP, but internally uses a
|
||
|
+ * division and a modulo operation instead of math tricks. This way it
|
||
|
+ * avoids overflowing when handling big numbers.
|
||
|
+ */
|
||
|
+#define DIV_ROUND_UP_NO_OVERFLOW(n, d) (((n) / (d)) + !!((n) % (d)))
|
||
|
+
|
||
|
#define DIV_ROUND_DOWN_ULL(ll, d) \
|
||
|
({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
|
||
|
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From bba6ab440970c2b9a69d3b32a16f2680e38ea9e1 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 24 Oct 2023 16:13:50 +0200
|
||
|
Subject: [PATCH 04/54] clk: divider: Fix divisor masking on 64 bit platforms
|
||
|
|
||
|
The clock framework handles clock rates as "unsigned long", so u32 on
|
||
|
32-bit architectures and u64 on 64-bit architectures.
|
||
|
|
||
|
The current code casts the dividend to u64 on 32-bit to avoid a
|
||
|
potential overflow. For example DIV_ROUND_UP(3000000000, 1500000000)
|
||
|
= (3.0G + 1.5G - 1) / 1.5G = = OVERFLOW / 1.5G, which has been
|
||
|
introduced in commit 9556f9dad8f5 ("clk: divider: handle integer overflow
|
||
|
when dividing large clock rates").
|
||
|
|
||
|
On 64 bit platforms this masks the divisor, so that only the lower
|
||
|
32 bit are used. Thus requesting a frequency >= 4.3GHz results
|
||
|
in incorrect values. For example requesting 4300000000 (4.3 GHz) will
|
||
|
effectively request ca. 5 MHz. Requesting clk_round_rate(clk, ULONG_MAX)
|
||
|
is a bit of a special case, since that still returns correct values as
|
||
|
long as the parent clock is below 8.5 GHz.
|
||
|
|
||
|
Fix this by switching to DIV_ROUND_UP_NO_OVERFLOW, which cannot
|
||
|
overflow. This avoids any requirements on the arguments (except
|
||
|
that divisor should not be 0 obviously).
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/clk-divider.c | 6 +++---
|
||
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
|
||
|
index a2c2b5203b0a..94b4fb66a60f 100644
|
||
|
--- a/drivers/clk/clk-divider.c
|
||
|
+++ b/drivers/clk/clk-divider.c
|
||
|
@@ -220,7 +220,7 @@ static int _div_round_up(const struct clk_div_table *table,
|
||
|
unsigned long parent_rate, unsigned long rate,
|
||
|
unsigned long flags)
|
||
|
{
|
||
|
- int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||
|
+ int div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate);
|
||
|
|
||
|
if (flags & CLK_DIVIDER_POWER_OF_TWO)
|
||
|
div = __roundup_pow_of_two(div);
|
||
|
@@ -237,7 +237,7 @@ static int _div_round_closest(const struct clk_div_table *table,
|
||
|
int up, down;
|
||
|
unsigned long up_rate, down_rate;
|
||
|
|
||
|
- up = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||
|
+ up = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate);
|
||
|
down = parent_rate / rate;
|
||
|
|
||
|
if (flags & CLK_DIVIDER_POWER_OF_TWO) {
|
||
|
@@ -473,7 +473,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
|
||
|
{
|
||
|
unsigned int div, value;
|
||
|
|
||
|
- div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||
|
+ div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate);
|
||
|
|
||
|
if (!_is_valid_div(table, div, flags))
|
||
|
return -EINVAL;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 37865cfc273efe64b16651ff167566564ca8cd2e Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 24 Oct 2023 18:09:57 +0200
|
||
|
Subject: [PATCH 05/54] clk: composite: replace open-coded abs_diff()
|
||
|
|
||
|
Replace the open coded abs_diff() with the existing helper function.
|
||
|
|
||
|
Suggested-by: Andy Shevchenko <andriy.shevchenko@intel.com>
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/clk-composite.c | 6 ++----
|
||
|
1 file changed, 2 insertions(+), 4 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
|
||
|
index 66759fe28fad..478a4e594336 100644
|
||
|
--- a/drivers/clk/clk-composite.c
|
||
|
+++ b/drivers/clk/clk-composite.c
|
||
|
@@ -6,6 +6,7 @@
|
||
|
#include <linux/clk-provider.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/err.h>
|
||
|
+#include <linux/math.h>
|
||
|
#include <linux/slab.h>
|
||
|
|
||
|
static u8 clk_composite_get_parent(struct clk_hw *hw)
|
||
|
@@ -119,10 +120,7 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
|
||
|
if (ret)
|
||
|
continue;
|
||
|
|
||
|
- if (req->rate >= tmp_req.rate)
|
||
|
- rate_diff = req->rate - tmp_req.rate;
|
||
|
- else
|
||
|
- rate_diff = tmp_req.rate - req->rate;
|
||
|
+ rate_diff = abs_diff(req->rate, tmp_req.rate);
|
||
|
|
||
|
if (!rate_diff || !req->best_parent_hw
|
||
|
|| best_rate_diff > rate_diff) {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 346137db0818fa1d55da82180f40c5986daec9a5 Mon Sep 17 00:00:00 2001
|
||
|
From: Alexey Charkov <alchark@gmail.com>
|
||
|
Date: Thu, 29 Feb 2024 23:26:32 +0400
|
||
|
Subject: [PATCH 06/54] arm64: dts: rockchip: enable built-in thermal
|
||
|
monitoring on RK3588
|
||
|
|
||
|
Include thermal zones information in device tree for RK3588 variants.
|
||
|
|
||
|
This also enables the TSADC controller unconditionally on all boards
|
||
|
to ensure that thermal protections are in place via throttling and
|
||
|
emergency reset, once OPPs are added to enable CPU DVFS.
|
||
|
|
||
|
The default settings (using CRU as the emergency reset mechanism)
|
||
|
should work on all boards regardless of their wiring, as CRU resets
|
||
|
do not depend on any external components. Boards that have the TSHUT
|
||
|
signal wired to the reset line of the PMIC may opt to switch to GPIO
|
||
|
tshut mode instead (rockchip,hw-tshut-mode = <1>;)
|
||
|
|
||
|
It seems though that downstream kernels don't use that, even for
|
||
|
those boards where the wiring allows for GPIO based tshut, such as
|
||
|
Radxa Rock 5B [1], [2], [3]
|
||
|
|
||
|
[1] https://github.com/radxa/kernel/blob/stable-5.10-rock5/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts#L540
|
||
|
[2] https://github.com/radxa/kernel/blob/stable-5.10-rock5/arch/arm64/boot/dts/rockchip/rk3588s.dtsi#L5433
|
||
|
[3] https://dl.radxa.com/rock5/5b/docs/hw/radxa_rock_5b_v1423_sch.pdf page 11 (TSADC_SHUT_H)
|
||
|
|
||
|
Signed-off-by: Alexey Charkov <alchark@gmail.com>
|
||
|
Link: https://lore.kernel.org/r/20240229-rk-dts-additions-v3-1-6afe8473a631@gmail.com
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 176 +++++++++++++++++++++-
|
||
|
1 file changed, 175 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
index 6ac5ac8b48ab..e2b24d4bfc2e 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
@@ -10,6 +10,7 @@
|
||
|
#include <dt-bindings/reset/rockchip,rk3588-cru.h>
|
||
|
#include <dt-bindings/phy/phy.h>
|
||
|
#include <dt-bindings/ata/ahci.h>
|
||
|
+#include <dt-bindings/thermal/thermal.h>
|
||
|
|
||
|
/ {
|
||
|
compatible = "rockchip,rk3588";
|
||
|
@@ -2385,7 +2386,180 @@ tsadc: tsadc@fec00000 {
|
||
|
pinctrl-1 = <&tsadc_shut>;
|
||
|
pinctrl-names = "gpio", "otpout";
|
||
|
#thermal-sensor-cells = <1>;
|
||
|
- status = "disabled";
|
||
|
+ status = "okay";
|
||
|
+ };
|
||
|
+
|
||
|
+ thermal_zones: thermal-zones {
|
||
|
+ /* sensor near the center of the SoC */
|
||
|
+ package_thermal: package-thermal {
|
||
|
+ polling-delay-passive = <0>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 0>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ package_crit: package-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ /* sensor between A76 cores 0 and 1 */
|
||
|
+ bigcore0_thermal: bigcore0-thermal {
|
||
|
+ polling-delay-passive = <100>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 1>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ /* threshold to start collecting temperature
|
||
|
+ * statistics e.g. with the IPA governor
|
||
|
+ */
|
||
|
+ bigcore0_alert0: bigcore0-alert0 {
|
||
|
+ temperature = <75000>;
|
||
|
+ hysteresis = <2000>;
|
||
|
+ type = "passive";
|
||
|
+ };
|
||
|
+ /* actual control temperature */
|
||
|
+ bigcore0_alert1: bigcore0-alert1 {
|
||
|
+ temperature = <85000>;
|
||
|
+ hysteresis = <2000>;
|
||
|
+ type = "passive";
|
||
|
+ };
|
||
|
+ bigcore0_crit: bigcore0-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ cooling-maps {
|
||
|
+ map0 {
|
||
|
+ trip = <&bigcore0_alert1>;
|
||
|
+ cooling-device =
|
||
|
+ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
||
|
+ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ /* sensor between A76 cores 2 and 3 */
|
||
|
+ bigcore2_thermal: bigcore2-thermal {
|
||
|
+ polling-delay-passive = <100>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 2>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ /* threshold to start collecting temperature
|
||
|
+ * statistics e.g. with the IPA governor
|
||
|
+ */
|
||
|
+ bigcore2_alert0: bigcore2-alert0 {
|
||
|
+ temperature = <75000>;
|
||
|
+ hysteresis = <2000>;
|
||
|
+ type = "passive";
|
||
|
+ };
|
||
|
+ /* actual control temperature */
|
||
|
+ bigcore2_alert1: bigcore2-alert1 {
|
||
|
+ temperature = <85000>;
|
||
|
+ hysteresis = <2000>;
|
||
|
+ type = "passive";
|
||
|
+ };
|
||
|
+ bigcore2_crit: bigcore2-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ cooling-maps {
|
||
|
+ map0 {
|
||
|
+ trip = <&bigcore2_alert1>;
|
||
|
+ cooling-device =
|
||
|
+ <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
||
|
+ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ /* sensor between the four A55 cores */
|
||
|
+ little_core_thermal: littlecore-thermal {
|
||
|
+ polling-delay-passive = <100>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 3>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ /* threshold to start collecting temperature
|
||
|
+ * statistics e.g. with the IPA governor
|
||
|
+ */
|
||
|
+ littlecore_alert0: littlecore-alert0 {
|
||
|
+ temperature = <75000>;
|
||
|
+ hysteresis = <2000>;
|
||
|
+ type = "passive";
|
||
|
+ };
|
||
|
+ /* actual control temperature */
|
||
|
+ littlecore_alert1: littlecore-alert1 {
|
||
|
+ temperature = <85000>;
|
||
|
+ hysteresis = <2000>;
|
||
|
+ type = "passive";
|
||
|
+ };
|
||
|
+ littlecore_crit: littlecore-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ cooling-maps {
|
||
|
+ map0 {
|
||
|
+ trip = <&littlecore_alert1>;
|
||
|
+ cooling-device =
|
||
|
+ <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
||
|
+ <&cpu_l1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
||
|
+ <&cpu_l2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
||
|
+ <&cpu_l3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ /* sensor near the PD_CENTER power domain */
|
||
|
+ center_thermal: center-thermal {
|
||
|
+ polling-delay-passive = <0>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 4>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ center_crit: center-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ gpu_thermal: gpu-thermal {
|
||
|
+ polling-delay-passive = <0>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 5>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ gpu_crit: gpu-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ npu_thermal: npu-thermal {
|
||
|
+ polling-delay-passive = <0>;
|
||
|
+ polling-delay = <0>;
|
||
|
+ thermal-sensors = <&tsadc 6>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ npu_crit: npu-crit {
|
||
|
+ temperature = <115000>;
|
||
|
+ hysteresis = <0>;
|
||
|
+ type = "critical";
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
};
|
||
|
|
||
|
saradc: adc@fec10000 {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From c81266f4e265da4e7a56222b6ea03e1936d11287 Mon Sep 17 00:00:00 2001
|
||
|
From: Alexey Charkov <alchark@gmail.com>
|
||
|
Date: Thu, 29 Feb 2024 23:26:33 +0400
|
||
|
Subject: [PATCH 07/54] arm64: dts: rockchip: enable automatic active cooling
|
||
|
on Rock 5B
|
||
|
|
||
|
This links the PWM fan on Radxa Rock 5B as an active cooling device
|
||
|
managed automatically by the thermal subsystem, with a target SoC
|
||
|
temperature of 65C and a minimum-spin interval from 55C to 65C to
|
||
|
ensure airflow when the system gets warm
|
||
|
|
||
|
Signed-off-by: Alexey Charkov <alchark@gmail.com>
|
||
|
Helped-by: Dragan Simic <dsimic@manjaro.org>
|
||
|
Reviewed-by: Dragan Simic <dsimic@manjaro.org>
|
||
|
Link: https://lore.kernel.org/r/20240229-rk-dts-additions-v3-2-6afe8473a631@gmail.com
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
.../boot/dts/rockchip/rk3588-rock-5b.dts | 30 ++++++++++++++++++-
|
||
|
1 file changed, 29 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
index 0c4359f1b97e..e18e970393a6 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
@@ -53,7 +53,7 @@ led_rgb_b {
|
||
|
|
||
|
fan: pwm-fan {
|
||
|
compatible = "pwm-fan";
|
||
|
- cooling-levels = <0 95 145 195 255>;
|
||
|
+ cooling-levels = <0 120 150 180 210 240 255>;
|
||
|
fan-supply = <&vcc5v0_sys>;
|
||
|
pwms = <&pwm1 0 50000 0>;
|
||
|
#cooling-cells = <2>;
|
||
|
@@ -351,6 +351,34 @@ i2s0_8ch_p0_0: endpoint {
|
||
|
};
|
||
|
};
|
||
|
|
||
|
+&package_thermal {
|
||
|
+ polling-delay = <1000>;
|
||
|
+
|
||
|
+ trips {
|
||
|
+ package_fan0: package-fan0 {
|
||
|
+ hysteresis = <2000>;
|
||
|
+ temperature = <55000>;
|
||
|
+ type = "active";
|
||
|
+ };
|
||
|
+ package_fan1: package-fan1 {
|
||
|
+ hysteresis = <2000>;
|
||
|
+ temperature = <65000>;
|
||
|
+ type = "active";
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ cooling-maps {
|
||
|
+ map1 {
|
||
|
+ cooling-device = <&fan THERMAL_NO_LIMIT 1>;
|
||
|
+ trip = <&package_fan0>;
|
||
|
+ };
|
||
|
+ map2 {
|
||
|
+ cooling-device = <&fan 2 THERMAL_NO_LIMIT>;
|
||
|
+ trip = <&package_fan1>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
&pcie2x1l0 {
|
||
|
pinctrl-names = "default";
|
||
|
pinctrl-0 = <&pcie2_0_rst>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From e6fac8dde2ec69f02435b3c5ccd68ea1355bac05 Mon Sep 17 00:00:00 2001
|
||
|
From: Alexey Charkov <alchark@gmail.com>
|
||
|
Date: Thu, 29 Feb 2024 23:26:34 +0400
|
||
|
Subject: [PATCH 08/54] arm64: dts: rockchip: Add CPU/memory regulator coupling
|
||
|
for RK3588
|
||
|
|
||
|
RK3588 chips allow for their CPU cores to be powered by a different
|
||
|
supply vs. their corresponding memory interfaces, and two of the
|
||
|
boards currently upstream do that (EVB1 and QuartzPro64).
|
||
|
|
||
|
The voltage of the memory interface though has to match that of the
|
||
|
CPU cores that use it, which downstream kernels achieve by the means
|
||
|
of a custom cpufreq driver which adjusts both at the same time.
|
||
|
|
||
|
It seems that regulator coupling is a more appropriate generic
|
||
|
interface for it, so this patch introduces coupling to affected
|
||
|
device trees to ensure that memory interface voltage is also updated
|
||
|
whenever cpufreq switches between CPU OPPs.
|
||
|
|
||
|
Note that other boards, such as Radxa Rock 5B, define both the CPU
|
||
|
and memory interface regulators as aliases to the same DT node, so
|
||
|
this doesn't apply there.
|
||
|
|
||
|
Signed-off-by: Alexey Charkov <alchark@gmail.com>
|
||
|
Link: https://lore.kernel.org/r/20240229-rk-dts-additions-v3-3-6afe8473a631@gmail.com
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts | 12 ++++++++++++
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-quartzpro64.dts | 12 ++++++++++++
|
||
|
2 files changed, 24 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
index 7be2190244ba..7c5826da452e 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
@@ -878,6 +878,8 @@ regulators {
|
||
|
vdd_cpu_big1_s0: dcdc-reg1 {
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big1_mem_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <550000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -890,6 +892,8 @@ regulator-state-mem {
|
||
|
vdd_cpu_big0_s0: dcdc-reg2 {
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big0_mem_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <550000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -902,6 +906,8 @@ regulator-state-mem {
|
||
|
vdd_cpu_lit_s0: dcdc-reg3 {
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_lit_mem_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <550000>;
|
||
|
regulator-max-microvolt = <950000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -926,6 +932,8 @@ regulator-state-mem {
|
||
|
vdd_cpu_big1_mem_s0: dcdc-reg5 {
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big1_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <675000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -939,6 +947,8 @@ regulator-state-mem {
|
||
|
vdd_cpu_big0_mem_s0: dcdc-reg6 {
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big0_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <675000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -963,6 +973,8 @@ regulator-state-mem {
|
||
|
vdd_cpu_lit_mem_s0: dcdc-reg8 {
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_lit_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <675000>;
|
||
|
regulator-max-microvolt = <950000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-quartzpro64.dts b/arch/arm64/boot/dts/rockchip/rk3588-quartzpro64.dts
|
||
|
index b4f22d95ac0e..baeb08d665c7 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-quartzpro64.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-quartzpro64.dts
|
||
|
@@ -832,6 +832,8 @@ vdd_cpu_big1_s0: dcdc-reg1 {
|
||
|
regulator-name = "vdd_cpu_big1_s0";
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big1_mem_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <550000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -845,6 +847,8 @@ vdd_cpu_big0_s0: dcdc-reg2 {
|
||
|
regulator-name = "vdd_cpu_big0_s0";
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big0_mem_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <550000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -858,6 +862,8 @@ vdd_cpu_lit_s0: dcdc-reg3 {
|
||
|
regulator-name = "vdd_cpu_lit_s0";
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_lit_mem_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <550000>;
|
||
|
regulator-max-microvolt = <950000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -884,6 +890,8 @@ vdd_cpu_big1_mem_s0: dcdc-reg5 {
|
||
|
regulator-name = "vdd_cpu_big1_mem_s0";
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big1_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <675000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -898,6 +906,8 @@ vdd_cpu_big0_mem_s0: dcdc-reg6 {
|
||
|
regulator-name = "vdd_cpu_big0_mem_s0";
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_big0_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <675000>;
|
||
|
regulator-max-microvolt = <1050000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
@@ -924,6 +934,8 @@ vdd_cpu_lit_mem_s0: dcdc-reg8 {
|
||
|
regulator-name = "vdd_cpu_lit_mem_s0";
|
||
|
regulator-always-on;
|
||
|
regulator-boot-on;
|
||
|
+ regulator-coupled-with = <&vdd_cpu_lit_s0>;
|
||
|
+ regulator-coupled-max-spread = <10000>;
|
||
|
regulator-min-microvolt = <675000>;
|
||
|
regulator-max-microvolt = <950000>;
|
||
|
regulator-ramp-delay = <12500>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From bfc28c478d96020a987e19b393762b92c5a62896 Mon Sep 17 00:00:00 2001
|
||
|
From: Alexey Charkov <alchark@gmail.com>
|
||
|
Date: Thu, 29 Feb 2024 23:26:35 +0400
|
||
|
Subject: [PATCH 09/54] arm64: dts: rockchip: Add OPP data for CPU cores on
|
||
|
RK3588
|
||
|
|
||
|
By default the CPUs on RK3588 start up in a conservative performance
|
||
|
mode. Add frequency and voltage mappings to the device tree to enable
|
||
|
dynamic scaling via cpufreq.
|
||
|
|
||
|
OPP values are adapted from Radxa's downstream kernel for Rock 5B [1],
|
||
|
stripping them down to the minimum frequency and voltage combinations
|
||
|
as expected by the generic upstream cpufreq-dt driver, and also dropping
|
||
|
those OPPs that don't differ in voltage but only in frequency (keeping
|
||
|
the top frequency OPP in each case).
|
||
|
|
||
|
Note that this patch ignores voltage scaling for the CPU memory
|
||
|
interface which the downstream kernel does through a custom cpufreq
|
||
|
driver, and which is why the downstream version has two sets of voltage
|
||
|
values for each OPP (the second one being meant for the memory
|
||
|
interface supply regulator). This is done instead via regulator
|
||
|
coupling between CPU and memory interface supplies on affected boards.
|
||
|
|
||
|
This has been tested on Rock 5B with u-boot 2023.11 compiled from
|
||
|
Collabora's integration tree [2] with binary bl31 and appears to be
|
||
|
stable both under active cooling and passive cooling (with throttling)
|
||
|
|
||
|
[1] https://github.com/radxa/kernel/blob/stable-5.10-rock5/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
[2] https://gitlab.collabora.com/hardware-enablement/rockchip-3588/u-boot
|
||
|
|
||
|
Signed-off-by: Alexey Charkov <alchark@gmail.com>
|
||
|
Link: https://lore.kernel.org/r/20240229-rk-dts-additions-v3-4-6afe8473a631@gmail.com
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 122 ++++++++++++++++++++++
|
||
|
1 file changed, 122 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
index e2b24d4bfc2e..5104eebd563a 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
@@ -97,6 +97,7 @@ cpu_l0: cpu@0 {
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUL>;
|
||
|
assigned-clocks = <&scmi_clk SCMI_CLK_CPUL>;
|
||
|
assigned-clock-rates = <816000000>;
|
||
|
+ operating-points-v2 = <&cluster0_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <32768>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -116,6 +117,7 @@ cpu_l1: cpu@100 {
|
||
|
enable-method = "psci";
|
||
|
capacity-dmips-mhz = <530>;
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUL>;
|
||
|
+ operating-points-v2 = <&cluster0_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <32768>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -135,6 +137,7 @@ cpu_l2: cpu@200 {
|
||
|
enable-method = "psci";
|
||
|
capacity-dmips-mhz = <530>;
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUL>;
|
||
|
+ operating-points-v2 = <&cluster0_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <32768>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -154,6 +157,7 @@ cpu_l3: cpu@300 {
|
||
|
enable-method = "psci";
|
||
|
capacity-dmips-mhz = <530>;
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUL>;
|
||
|
+ operating-points-v2 = <&cluster0_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <32768>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -175,6 +179,7 @@ cpu_b0: cpu@400 {
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUB01>;
|
||
|
assigned-clocks = <&scmi_clk SCMI_CLK_CPUB01>;
|
||
|
assigned-clock-rates = <816000000>;
|
||
|
+ operating-points-v2 = <&cluster1_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <65536>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -194,6 +199,7 @@ cpu_b1: cpu@500 {
|
||
|
enable-method = "psci";
|
||
|
capacity-dmips-mhz = <1024>;
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUB01>;
|
||
|
+ operating-points-v2 = <&cluster1_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <65536>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -215,6 +221,7 @@ cpu_b2: cpu@600 {
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUB23>;
|
||
|
assigned-clocks = <&scmi_clk SCMI_CLK_CPUB23>;
|
||
|
assigned-clock-rates = <816000000>;
|
||
|
+ operating-points-v2 = <&cluster2_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <65536>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -234,6 +241,7 @@ cpu_b3: cpu@700 {
|
||
|
enable-method = "psci";
|
||
|
capacity-dmips-mhz = <1024>;
|
||
|
clocks = <&scmi_clk SCMI_CLK_CPUB23>;
|
||
|
+ operating-points-v2 = <&cluster2_opp_table>;
|
||
|
cpu-idle-states = <&CPU_SLEEP>;
|
||
|
i-cache-size = <65536>;
|
||
|
i-cache-line-size = <64>;
|
||
|
@@ -348,6 +356,120 @@ l3_cache: l3-cache {
|
||
|
};
|
||
|
};
|
||
|
|
||
|
+ cluster0_opp_table: opp-table-cluster0 {
|
||
|
+ compatible = "operating-points-v2";
|
||
|
+ opp-shared;
|
||
|
+
|
||
|
+ opp-1008000000 {
|
||
|
+ opp-hz = /bits/ 64 <1008000000>;
|
||
|
+ opp-microvolt = <675000 675000 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1200000000 {
|
||
|
+ opp-hz = /bits/ 64 <1200000000>;
|
||
|
+ opp-microvolt = <712500 712500 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1416000000 {
|
||
|
+ opp-hz = /bits/ 64 <1416000000>;
|
||
|
+ opp-microvolt = <762500 762500 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ opp-suspend;
|
||
|
+ };
|
||
|
+ opp-1608000000 {
|
||
|
+ opp-hz = /bits/ 64 <1608000000>;
|
||
|
+ opp-microvolt = <850000 850000 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1800000000 {
|
||
|
+ opp-hz = /bits/ 64 <1800000000>;
|
||
|
+ opp-microvolt = <950000 950000 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ cluster1_opp_table: opp-table-cluster1 {
|
||
|
+ compatible = "operating-points-v2";
|
||
|
+ opp-shared;
|
||
|
+
|
||
|
+ opp-1200000000 {
|
||
|
+ opp-hz = /bits/ 64 <1200000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1416000000 {
|
||
|
+ opp-hz = /bits/ 64 <1416000000>;
|
||
|
+ opp-microvolt = <725000 725000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1608000000 {
|
||
|
+ opp-hz = /bits/ 64 <1608000000>;
|
||
|
+ opp-microvolt = <762500 762500 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1800000000 {
|
||
|
+ opp-hz = /bits/ 64 <1800000000>;
|
||
|
+ opp-microvolt = <850000 850000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2016000000 {
|
||
|
+ opp-hz = /bits/ 64 <2016000000>;
|
||
|
+ opp-microvolt = <925000 925000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2208000000 {
|
||
|
+ opp-hz = /bits/ 64 <2208000000>;
|
||
|
+ opp-microvolt = <987500 987500 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2400000000 {
|
||
|
+ opp-hz = /bits/ 64 <2400000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ cluster2_opp_table: opp-table-cluster2 {
|
||
|
+ compatible = "operating-points-v2";
|
||
|
+ opp-shared;
|
||
|
+
|
||
|
+ opp-1200000000 {
|
||
|
+ opp-hz = /bits/ 64 <1200000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1416000000 {
|
||
|
+ opp-hz = /bits/ 64 <1416000000>;
|
||
|
+ opp-microvolt = <725000 725000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1608000000 {
|
||
|
+ opp-hz = /bits/ 64 <1608000000>;
|
||
|
+ opp-microvolt = <762500 762500 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1800000000 {
|
||
|
+ opp-hz = /bits/ 64 <1800000000>;
|
||
|
+ opp-microvolt = <850000 850000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2016000000 {
|
||
|
+ opp-hz = /bits/ 64 <2016000000>;
|
||
|
+ opp-microvolt = <925000 925000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2208000000 {
|
||
|
+ opp-hz = /bits/ 64 <2208000000>;
|
||
|
+ opp-microvolt = <987500 987500 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2400000000 {
|
||
|
+ opp-hz = /bits/ 64 <2400000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
display_subsystem: display-subsystem {
|
||
|
compatible = "rockchip,display-subsystem";
|
||
|
ports = <&vop_out>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From d4c81e098ca05e31b6b6e3faae8bd278faa41b6c Mon Sep 17 00:00:00 2001
|
||
|
From: Alexey Charkov <alchark@gmail.com>
|
||
|
Date: Thu, 29 Feb 2024 23:26:36 +0400
|
||
|
Subject: [PATCH 10/54] arm64: dts: rockchip: Add further granularity in RK3588
|
||
|
CPU OPPs
|
||
|
|
||
|
This introduces additional OPPs that share the same voltage as
|
||
|
another OPP already present in the .dtsi but with lower frequency.
|
||
|
|
||
|
The idea is to try and limit system throughput more gradually upon
|
||
|
reaching the throttling condition for workloads that are close to
|
||
|
sustainable power already, thus avoiding needless performance loss.
|
||
|
|
||
|
My limited synthetic benchmarking [1] showed around 3.8% performance
|
||
|
benefit when these are in place, other things equal (not meant to
|
||
|
be comprehensive). Though dmesg complains about these OPPs being
|
||
|
'inefficient':
|
||
|
|
||
|
[ 9.009561] cpu cpu0: EM: OPP:816000 is inefficient
|
||
|
[ 9.009580] cpu cpu0: EM: OPP:600000 is inefficient
|
||
|
[ 9.009591] cpu cpu0: EM: OPP:408000 is inefficient
|
||
|
[ 9.011370] cpu cpu4: EM: OPP:2352000 is inefficient
|
||
|
[ 9.011379] cpu cpu4: EM: OPP:2304000 is inefficient
|
||
|
[ 9.011384] cpu cpu4: EM: OPP:2256000 is inefficient
|
||
|
[ 9.011389] cpu cpu4: EM: OPP:600000 is inefficient
|
||
|
[ 9.011393] cpu cpu4: EM: OPP:408000 is inefficient
|
||
|
[ 9.012978] cpu cpu6: EM: OPP:2352000 is inefficient
|
||
|
[ 9.012987] cpu cpu6: EM: OPP:2304000 is inefficient
|
||
|
[ 9.012992] cpu cpu6: EM: OPP:2256000 is inefficient
|
||
|
[ 9.012996] cpu cpu6: EM: OPP:600000 is inefficient
|
||
|
[ 9.013000] cpu cpu6: EM: OPP:408000 is inefficient
|
||
|
|
||
|
[1] https://lore.kernel.org/linux-rockchip/CABjd4YxqarUCbZ-a2XLe3TWJ-qjphGkyq=wDnctnEhdoSdPPpw@mail.gmail.com/T/#me92aa0ee25e6eeb1d1501ce85f5af4e58b3b13c5
|
||
|
|
||
|
Signed-off-by: Alexey Charkov <alchark@gmail.com>
|
||
|
Link: https://lore.kernel.org/r/20240229-rk-dts-additions-v3-5-6afe8473a631@gmail.com
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 87 +++++++++++++++++++++++
|
||
|
1 file changed, 87 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
index 5104eebd563a..ae3ccafe6871 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
@@ -360,6 +360,21 @@ cluster0_opp_table: opp-table-cluster0 {
|
||
|
compatible = "operating-points-v2";
|
||
|
opp-shared;
|
||
|
|
||
|
+ opp-408000000 {
|
||
|
+ opp-hz = /bits/ 64 <408000000>;
|
||
|
+ opp-microvolt = <675000 675000 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-600000000 {
|
||
|
+ opp-hz = /bits/ 64 <600000000>;
|
||
|
+ opp-microvolt = <675000 675000 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-816000000 {
|
||
|
+ opp-hz = /bits/ 64 <816000000>;
|
||
|
+ opp-microvolt = <675000 675000 950000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
opp-1008000000 {
|
||
|
opp-hz = /bits/ 64 <1008000000>;
|
||
|
opp-microvolt = <675000 675000 950000>;
|
||
|
@@ -392,6 +407,27 @@ cluster1_opp_table: opp-table-cluster1 {
|
||
|
compatible = "operating-points-v2";
|
||
|
opp-shared;
|
||
|
|
||
|
+ opp-408000000 {
|
||
|
+ opp-hz = /bits/ 64 <408000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ opp-suspend;
|
||
|
+ };
|
||
|
+ opp-600000000 {
|
||
|
+ opp-hz = /bits/ 64 <600000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-816000000 {
|
||
|
+ opp-hz = /bits/ 64 <816000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1008000000 {
|
||
|
+ opp-hz = /bits/ 64 <1008000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
opp-1200000000 {
|
||
|
opp-hz = /bits/ 64 <1200000000>;
|
||
|
opp-microvolt = <675000 675000 1000000>;
|
||
|
@@ -422,6 +458,21 @@ opp-2208000000 {
|
||
|
opp-microvolt = <987500 987500 1000000>;
|
||
|
clock-latency-ns = <40000>;
|
||
|
};
|
||
|
+ opp-2256000000 {
|
||
|
+ opp-hz = /bits/ 64 <2256000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2304000000 {
|
||
|
+ opp-hz = /bits/ 64 <2304000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2352000000 {
|
||
|
+ opp-hz = /bits/ 64 <2352000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
opp-2400000000 {
|
||
|
opp-hz = /bits/ 64 <2400000000>;
|
||
|
opp-microvolt = <1000000 1000000 1000000>;
|
||
|
@@ -433,6 +484,27 @@ cluster2_opp_table: opp-table-cluster2 {
|
||
|
compatible = "operating-points-v2";
|
||
|
opp-shared;
|
||
|
|
||
|
+ opp-408000000 {
|
||
|
+ opp-hz = /bits/ 64 <408000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ opp-suspend;
|
||
|
+ };
|
||
|
+ opp-600000000 {
|
||
|
+ opp-hz = /bits/ 64 <600000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-816000000 {
|
||
|
+ opp-hz = /bits/ 64 <816000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-1008000000 {
|
||
|
+ opp-hz = /bits/ 64 <1008000000>;
|
||
|
+ opp-microvolt = <675000 675000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
opp-1200000000 {
|
||
|
opp-hz = /bits/ 64 <1200000000>;
|
||
|
opp-microvolt = <675000 675000 1000000>;
|
||
|
@@ -463,6 +535,21 @@ opp-2208000000 {
|
||
|
opp-microvolt = <987500 987500 1000000>;
|
||
|
clock-latency-ns = <40000>;
|
||
|
};
|
||
|
+ opp-2256000000 {
|
||
|
+ opp-hz = /bits/ 64 <2256000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2304000000 {
|
||
|
+ opp-hz = /bits/ 64 <2304000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
+ opp-2352000000 {
|
||
|
+ opp-hz = /bits/ 64 <2352000000>;
|
||
|
+ opp-microvolt = <1000000 1000000 1000000>;
|
||
|
+ clock-latency-ns = <40000>;
|
||
|
+ };
|
||
|
opp-2400000000 {
|
||
|
opp-hz = /bits/ 64 <2400000000>;
|
||
|
opp-microvolt = <1000000 1000000 1000000>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From b81afac501f04f1137009cec98a1447321b84467 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Fri, 14 Jul 2023 17:38:24 +0200
|
||
|
Subject: [PATCH 11/54] [BROKEN] arm64: dts: rockchip: rk3588-evb1: add PCIe2
|
||
|
WLAN controller
|
||
|
|
||
|
Enable PCIe bus used by on-board PCIe Broadcom WLAN controller.
|
||
|
|
||
|
The WLAN controller is not detected. There are two reasons for
|
||
|
that.
|
||
|
|
||
|
First of all it is necessary to keep the HYM8563 clock enabled, but it
|
||
|
is disabled because the kernel does not know about the dependency and
|
||
|
disables any clocks it deems unused.
|
||
|
|
||
|
Apart from that it looks like the controller needs a long time to be
|
||
|
properly initialized. So detection only works when rescanning the bus
|
||
|
some time after boot. This still needs to be investigated.
|
||
|
|
||
|
Both of these issues should be fixable by implementing a pwrseq driver
|
||
|
once the following series has landed:
|
||
|
|
||
|
https://lore.kernel.org/all/20240104130123.37115-1-brgl@bgdev.pl/
|
||
|
|
||
|
In addition to the above issues, the mainline brcmfmac driver does
|
||
|
not yet support the chip version used by AP6275P.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
.../boot/dts/rockchip/rk3588-evb1-v10.dts | 61 +++++++++++++++++++
|
||
|
1 file changed, 61 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
index 7c5826da452e..65e16410084a 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
@@ -120,6 +120,15 @@ backlight: backlight {
|
||
|
pwms = <&pwm2 0 25000 0>;
|
||
|
};
|
||
|
|
||
|
+ wlan-rfkill {
|
||
|
+ compatible = "rfkill-gpio";
|
||
|
+ label = "rfkill-pcie-wlan";
|
||
|
+ radio-type = "wlan";
|
||
|
+ shutdown-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&wifi_pwren>;
|
||
|
+ };
|
||
|
+
|
||
|
pcie20_avdd0v85: pcie20-avdd0v85-regulator {
|
||
|
compatible = "regulator-fixed";
|
||
|
regulator-name = "pcie20_avdd0v85";
|
||
|
@@ -237,12 +246,36 @@ vcc5v0_usb: vcc5v0-usb-regulator {
|
||
|
regulator-max-microvolt = <5000000>;
|
||
|
vin-supply = <&vcc5v0_usbdcin>;
|
||
|
};
|
||
|
+
|
||
|
+ vccio_wl: vccio-wl {
|
||
|
+ compatible = "regulator-fixed";
|
||
|
+ regulator-name = "wlan-vddio";
|
||
|
+ regulator-min-microvolt = <1800000>;
|
||
|
+ regulator-max-microvolt = <1800000>;
|
||
|
+ regulator-always-on;
|
||
|
+ regulator-boot-on;
|
||
|
+ vin-supply = <&vcc_1v8_s0>;
|
||
|
+ };
|
||
|
+
|
||
|
+ vcc3v3_pciewl_vbat: vcc3v3-pciewl-vbat {
|
||
|
+ compatible = "regulator-fixed";
|
||
|
+ regulator-name = "wlan-vbat";
|
||
|
+ regulator-min-microvolt = <3300000>;
|
||
|
+ regulator-max-microvolt = <3300000>;
|
||
|
+ regulator-always-on;
|
||
|
+ regulator-boot-on;
|
||
|
+ vin-supply = <&vcc_3v3_s0>;
|
||
|
+ };
|
||
|
};
|
||
|
|
||
|
&combphy0_ps {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&combphy1_ps {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
&combphy2_psu {
|
||
|
status = "okay";
|
||
|
};
|
||
|
@@ -408,6 +441,12 @@ rgmii_phy: ethernet-phy@1 {
|
||
|
};
|
||
|
};
|
||
|
|
||
|
+&pcie2x1l0 {
|
||
|
+ reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>;
|
||
|
+ pinctrl-0 = <&pcie2_0_rst>, <&pcie2_0_wake>, <&pcie2_0_clkreq>, <&wifi_host_wake_irq>;
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
&pcie2x1l1 {
|
||
|
reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>;
|
||
|
pinctrl-names = "default";
|
||
|
@@ -462,6 +501,18 @@ hym8563_int: hym8563-int {
|
||
|
};
|
||
|
|
||
|
pcie2 {
|
||
|
+ pcie2_0_rst: pcie2-0-rst {
|
||
|
+ rockchip,pins = <4 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
+ pcie2_0_wake: pcie2-0-wake {
|
||
|
+ rockchip,pins = <4 RK_PA4 4 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
+ pcie2_0_clkreq: pcie2-0-clkreq {
|
||
|
+ rockchip,pins = <4 RK_PA3 4 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
pcie2_1_rst: pcie2-1-rst {
|
||
|
rockchip,pins = <4 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
};
|
||
|
@@ -492,6 +543,16 @@ usbc0_int: usbc0-int {
|
||
|
rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>;
|
||
|
};
|
||
|
};
|
||
|
+
|
||
|
+ wlan {
|
||
|
+ wifi_host_wake_irq: wifi-host-wake-irq {
|
||
|
+ rockchip,pins = <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_down>;
|
||
|
+ };
|
||
|
+
|
||
|
+ wifi_pwren: wifi-pwren {
|
||
|
+ rockchip,pins = <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>;
|
||
|
+ };
|
||
|
+ };
|
||
|
};
|
||
|
|
||
|
&pwm2 {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 9a4e298de029f488c6857b23eb98370dd861b6c7 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Mon, 11 Mar 2024 18:05:34 +0100
|
||
|
Subject: [PATCH 12/54] clk: rockchip: rk3588: drop unused code
|
||
|
|
||
|
All clocks are registered early using CLK_OF_DECLARE(), which marks
|
||
|
the DT node as processed. For the processed DT node the probe routine
|
||
|
is never called. Thus this whole code is never executed. This could
|
||
|
be "fixed" by using CLK_OF_DECLARE_DRIVER, which avoids marking the
|
||
|
DT node as processed. But then the probe routine would re-register
|
||
|
all the clocks by calling rk3588_clk_init() again.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/clk-rk3588.c | 40 -------------------------------
|
||
|
1 file changed, 40 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
index b30279a96dc8..74051277ecea 100644
|
||
|
--- a/drivers/clk/rockchip/clk-rk3588.c
|
||
|
+++ b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
@@ -2502,43 +2502,3 @@ static void __init rk3588_clk_init(struct device_node *np)
|
||
|
}
|
||
|
|
||
|
CLK_OF_DECLARE(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_init);
|
||
|
-
|
||
|
-struct clk_rk3588_inits {
|
||
|
- void (*inits)(struct device_node *np);
|
||
|
-};
|
||
|
-
|
||
|
-static const struct clk_rk3588_inits clk_3588_cru_init = {
|
||
|
- .inits = rk3588_clk_init,
|
||
|
-};
|
||
|
-
|
||
|
-static const struct of_device_id clk_rk3588_match_table[] = {
|
||
|
- {
|
||
|
- .compatible = "rockchip,rk3588-cru",
|
||
|
- .data = &clk_3588_cru_init,
|
||
|
- },
|
||
|
- { }
|
||
|
-};
|
||
|
-
|
||
|
-static int __init clk_rk3588_probe(struct platform_device *pdev)
|
||
|
-{
|
||
|
- const struct clk_rk3588_inits *init_data;
|
||
|
- struct device *dev = &pdev->dev;
|
||
|
-
|
||
|
- init_data = device_get_match_data(dev);
|
||
|
- if (!init_data)
|
||
|
- return -EINVAL;
|
||
|
-
|
||
|
- if (init_data->inits)
|
||
|
- init_data->inits(dev->of_node);
|
||
|
-
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
-static struct platform_driver clk_rk3588_driver = {
|
||
|
- .driver = {
|
||
|
- .name = "clk-rk3588",
|
||
|
- .of_match_table = clk_rk3588_match_table,
|
||
|
- .suppress_bind_attrs = true,
|
||
|
- },
|
||
|
-};
|
||
|
-builtin_platform_driver_probe(clk_rk3588_driver, clk_rk3588_probe);
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 5b5631ef482995c737a805d126344e23b8d30c3e Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Fri, 8 Mar 2024 23:19:55 +0100
|
||
|
Subject: [PATCH 13/54] clk: rockchip: handle missing clocks with -EPROBE_DEFER
|
||
|
|
||
|
In the future some clocks will be registered using CLK_OF_DECLARE
|
||
|
and some are registered later from the driver probe routine. Any
|
||
|
clock handled by the probe routine should return -EPROBE_DEFER
|
||
|
until that routine has been called.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/clk.c | 2 +-
|
||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
|
||
|
index 73d2cbdc716b..31b7cc243d82 100644
|
||
|
--- a/drivers/clk/rockchip/clk.c
|
||
|
+++ b/drivers/clk/rockchip/clk.c
|
||
|
@@ -376,7 +376,7 @@ struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
|
||
|
goto err_free;
|
||
|
|
||
|
for (i = 0; i < nr_clks; ++i)
|
||
|
- clk_table[i] = ERR_PTR(-ENOENT);
|
||
|
+ clk_table[i] = ERR_PTR(-EPROBE_DEFER);
|
||
|
|
||
|
ctx->reg_base = base;
|
||
|
ctx->clk_data.clks = clk_table;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From b7d54daa05c7ff7777eb6ed11de732e681ba3786 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 12 Mar 2024 16:12:18 +0100
|
||
|
Subject: [PATCH 14/54] clk: rockchip: rk3588: register GATE_LINK later
|
||
|
|
||
|
The proper GATE_LINK implementation will use runtime PM to handle the
|
||
|
linked gate clocks, which requires device context. Currently all clocks
|
||
|
are registered early via CLK_OF_DECLARE, which is before the kernel
|
||
|
knows about devices.
|
||
|
|
||
|
Moving the full clocks registration to the probe routine does not work,
|
||
|
since the clocks needed for timers must be registered early.
|
||
|
|
||
|
To work around this issue, most of the clock tree is registered early,
|
||
|
but GATE_LINK clocks are handled in the probe routine. Since the resets
|
||
|
are not needed early either, they have also been moved to the probe
|
||
|
routine.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/clk-rk3588.c | 64 +++++++++++++++++++++++++++----
|
||
|
1 file changed, 56 insertions(+), 8 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
index 74051277ecea..29f31a5526fa 100644
|
||
|
--- a/drivers/clk/rockchip/clk-rk3588.c
|
||
|
+++ b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
@@ -266,6 +266,8 @@ static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
|
||
|
}, \
|
||
|
}
|
||
|
|
||
|
+static struct rockchip_clk_provider *early_ctx;
|
||
|
+
|
||
|
static struct rockchip_cpuclk_rate_table rk3588_cpub0clk_rates[] __initdata = {
|
||
|
RK3588_CPUB01CLK_RATE(2496000000, 1),
|
||
|
RK3588_CPUB01CLK_RATE(2400000000, 1),
|
||
|
@@ -694,7 +696,7 @@ static struct rockchip_pll_clock rk3588_pll_clks[] __initdata = {
|
||
|
RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
|
||
|
};
|
||
|
|
||
|
-static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
|
||
|
+static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
/*
|
||
|
* CRU Clock-Architecture
|
||
|
*/
|
||
|
@@ -2428,7 +2430,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
|
||
|
RK3588_CLKGATE_CON(68), 5, GFLAGS),
|
||
|
GATE(ACLK_AV1, "aclk_av1", "aclk_av1_pre", 0,
|
||
|
RK3588_CLKGATE_CON(68), 2, GFLAGS),
|
||
|
+};
|
||
|
|
||
|
+static struct rockchip_clk_branch rk3588_clk_branches[] = {
|
||
|
GATE_LINK(ACLK_ISP1_PRE, "aclk_isp1_pre", "aclk_isp1_root", ACLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 6, GFLAGS),
|
||
|
GATE_LINK(HCLK_ISP1_PRE, "hclk_isp1_pre", "hclk_isp1_root", HCLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 8, GFLAGS),
|
||
|
GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(31), 2, GFLAGS),
|
||
|
@@ -2453,14 +2457,18 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
|
||
|
GATE_LINK(PCLK_VO1GRF, "pclk_vo1grf", "pclk_vo1_root", HCLK_VO1, CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(59), 12, GFLAGS),
|
||
|
};
|
||
|
|
||
|
-static void __init rk3588_clk_init(struct device_node *np)
|
||
|
+static void __init rk3588_clk_early_init(struct device_node *np)
|
||
|
{
|
||
|
struct rockchip_clk_provider *ctx;
|
||
|
- unsigned long clk_nr_clks;
|
||
|
+ unsigned long clk_nr_clks, max_clk_id1, max_clk_id2;
|
||
|
void __iomem *reg_base;
|
||
|
|
||
|
- clk_nr_clks = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
|
||
|
- ARRAY_SIZE(rk3588_clk_branches)) + 1;
|
||
|
+ max_clk_id1 = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
|
||
|
+ ARRAY_SIZE(rk3588_clk_branches));
|
||
|
+ max_clk_id2 = rockchip_clk_find_max_clk_id(rk3588_early_clk_branches,
|
||
|
+ ARRAY_SIZE(rk3588_early_clk_branches));
|
||
|
+ clk_nr_clks = max(max_clk_id1, max_clk_id2) + 1;
|
||
|
+
|
||
|
reg_base = of_iomap(np, 0);
|
||
|
if (!reg_base) {
|
||
|
pr_err("%s: could not map cru region\n", __func__);
|
||
|
@@ -2473,6 +2481,7 @@ static void __init rk3588_clk_init(struct device_node *np)
|
||
|
iounmap(reg_base);
|
||
|
return;
|
||
|
}
|
||
|
+ early_ctx = ctx;
|
||
|
|
||
|
rockchip_clk_register_plls(ctx, rk3588_pll_clks,
|
||
|
ARRAY_SIZE(rk3588_pll_clks),
|
||
|
@@ -2491,14 +2500,53 @@ static void __init rk3588_clk_init(struct device_node *np)
|
||
|
&rk3588_cpub1clk_data, rk3588_cpub1clk_rates,
|
||
|
ARRAY_SIZE(rk3588_cpub1clk_rates));
|
||
|
|
||
|
+ rockchip_clk_register_branches(ctx, rk3588_early_clk_branches,
|
||
|
+ ARRAY_SIZE(rk3588_early_clk_branches));
|
||
|
+
|
||
|
+ rockchip_clk_of_add_provider(np, ctx);
|
||
|
+}
|
||
|
+CLK_OF_DECLARE_DRIVER(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_early_init);
|
||
|
+
|
||
|
+static int clk_rk3588_probe(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct rockchip_clk_provider *ctx = early_ctx;
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
+ struct device_node *np = dev->of_node;
|
||
|
+
|
||
|
rockchip_clk_register_branches(ctx, rk3588_clk_branches,
|
||
|
ARRAY_SIZE(rk3588_clk_branches));
|
||
|
|
||
|
- rk3588_rst_init(np, reg_base);
|
||
|
-
|
||
|
+ rk3588_rst_init(np, ctx->reg_base);
|
||
|
rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST, NULL);
|
||
|
|
||
|
+ /*
|
||
|
+ * Re-add clock provider, so that the newly added clocks are also
|
||
|
+ * re-parented and get their defaults configured.
|
||
|
+ */
|
||
|
+ of_clk_del_provider(np);
|
||
|
rockchip_clk_of_add_provider(np, ctx);
|
||
|
+
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
-CLK_OF_DECLARE(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_init);
|
||
|
+static const struct of_device_id clk_rk3588_match_table[] = {
|
||
|
+ {
|
||
|
+ .compatible = "rockchip,rk3588-cru",
|
||
|
+ },
|
||
|
+ { }
|
||
|
+};
|
||
|
+
|
||
|
+static struct platform_driver clk_rk3588_driver = {
|
||
|
+ .probe = clk_rk3588_probe,
|
||
|
+ .driver = {
|
||
|
+ .name = "clk-rk3588",
|
||
|
+ .of_match_table = clk_rk3588_match_table,
|
||
|
+ .suppress_bind_attrs = true,
|
||
|
+ },
|
||
|
+};
|
||
|
+
|
||
|
+static int __init rockchip_clk_rk3588_drv_register(void)
|
||
|
+{
|
||
|
+ return platform_driver_register(&clk_rk3588_driver);
|
||
|
+}
|
||
|
+core_initcall(rockchip_clk_rk3588_drv_register);
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 94082226ef32d0e97dc188cd9a065de6c55e8c77 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Mon, 25 Mar 2024 19:29:22 +0100
|
||
|
Subject: [PATCH 15/54] clk: rockchip: expose rockchip_clk_set_lookup
|
||
|
|
||
|
Move rockchip_clk_add_lookup to clk.h, so that it can be used
|
||
|
by sub-devices with their own driver. These might also have to
|
||
|
do a lookup, so rename the function to rockchip_clk_set_lookup
|
||
|
and add a matching rockchip_clk_add_lookup.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/clk.c | 14 ++++----------
|
||
|
drivers/clk/rockchip/clk.h | 12 ++++++++++++
|
||
|
2 files changed, 16 insertions(+), 10 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
|
||
|
index 31b7cc243d82..ef2408f10f39 100644
|
||
|
--- a/drivers/clk/rockchip/clk.c
|
||
|
+++ b/drivers/clk/rockchip/clk.c
|
||
|
@@ -197,12 +197,6 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
|
||
|
clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
|
||
|
}
|
||
|
|
||
|
-static void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
|
||
|
- struct clk *clk, unsigned int id)
|
||
|
-{
|
||
|
- ctx->clk_data.clks[id] = clk;
|
||
|
-}
|
||
|
-
|
||
|
static struct clk *rockchip_clk_register_frac_branch(
|
||
|
struct rockchip_clk_provider *ctx, const char *name,
|
||
|
const char *const *parent_names, u8 num_parents,
|
||
|
@@ -292,7 +286,7 @@ static struct clk *rockchip_clk_register_frac_branch(
|
||
|
return mux_clk;
|
||
|
}
|
||
|
|
||
|
- rockchip_clk_add_lookup(ctx, mux_clk, child->id);
|
||
|
+ rockchip_clk_set_lookup(ctx, mux_clk, child->id);
|
||
|
|
||
|
/* notifier on the fraction divider to catch rate changes */
|
||
|
if (frac->mux_frac_idx >= 0) {
|
||
|
@@ -424,7 +418,7 @@ void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
- rockchip_clk_add_lookup(ctx, clk, list->id);
|
||
|
+ rockchip_clk_set_lookup(ctx, clk, list->id);
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
|
||
|
@@ -585,7 +579,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
- rockchip_clk_add_lookup(ctx, clk, list->id);
|
||
|
+ rockchip_clk_set_lookup(ctx, clk, list->id);
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
|
||
|
@@ -609,7 +603,7 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
- rockchip_clk_add_lookup(ctx, clk, lookup_id);
|
||
|
+ rockchip_clk_set_lookup(ctx, clk, lookup_id);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
|
||
|
index fd3b476dedda..e39392e1c2a2 100644
|
||
|
--- a/drivers/clk/rockchip/clk.h
|
||
|
+++ b/drivers/clk/rockchip/clk.h
|
||
|
@@ -969,6 +969,18 @@ struct rockchip_clk_branch {
|
||
|
#define SGRF_GATE(_id, cname, pname) \
|
||
|
FACTOR(_id, cname, pname, 0, 1, 1)
|
||
|
|
||
|
+static inline struct clk *rockchip_clk_get_lookup(struct rockchip_clk_provider *ctx,
|
||
|
+ unsigned int id)
|
||
|
+{
|
||
|
+ return ctx->clk_data.clks[id];
|
||
|
+}
|
||
|
+
|
||
|
+static inline void rockchip_clk_set_lookup(struct rockchip_clk_provider *ctx,
|
||
|
+ struct clk *clk, unsigned int id)
|
||
|
+{
|
||
|
+ ctx->clk_data.clks[id] = clk;
|
||
|
+}
|
||
|
+
|
||
|
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
|
||
|
void __iomem *base, unsigned long nr_clks);
|
||
|
void rockchip_clk_of_add_provider(struct device_node *np,
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 8af78be9475200087bbedcc6565ae5e50490901b Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Mon, 25 Mar 2024 19:31:59 +0100
|
||
|
Subject: [PATCH 16/54] clk: rockchip: fix error for unknown clocks
|
||
|
|
||
|
There is a clk == NULL check after the switch to check for
|
||
|
unsupported clk types. Since clk is re-assigned in a loop,
|
||
|
this check is useless right now for anything but the first
|
||
|
round. Let's fix this up by assigning clk = NULL in the
|
||
|
loop before the switch statement.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/clk.c | 3 ++-
|
||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
|
||
|
index ef2408f10f39..e150bc1fc319 100644
|
||
|
--- a/drivers/clk/rockchip/clk.c
|
||
|
+++ b/drivers/clk/rockchip/clk.c
|
||
|
@@ -444,12 +444,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
|
||
|
struct rockchip_clk_branch *list,
|
||
|
unsigned int nr_clk)
|
||
|
{
|
||
|
- struct clk *clk = NULL;
|
||
|
+ struct clk *clk;
|
||
|
unsigned int idx;
|
||
|
unsigned long flags;
|
||
|
|
||
|
for (idx = 0; idx < nr_clk; idx++, list++) {
|
||
|
flags = list->flags;
|
||
|
+ clk = NULL;
|
||
|
|
||
|
/* catch simple muxes */
|
||
|
switch (list->branch_type) {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 75751a4bf52c74c6c742f0ed404307d1a894c623 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Mon, 25 Mar 2024 19:42:07 +0100
|
||
|
Subject: [PATCH 17/54] clk: rockchip: implement linked gate clock support
|
||
|
|
||
|
Recent Rockchip SoCs have a new hardware block called Native Interface
|
||
|
Unit (NIU), which gates clocks to devices behind them. These clock
|
||
|
gates will only have a running output clock when all of the following
|
||
|
conditions are met:
|
||
|
|
||
|
1. the parent clock is enabled
|
||
|
2. the enable bit is set correctly
|
||
|
3. the linked clock is enabled
|
||
|
|
||
|
To handle them this code registers them as a normal gate type clock,
|
||
|
which takes care of condition 1 + 2. The linked clock is handled by
|
||
|
using runtime PM clocks. Handling it via runtime PM requires setting
|
||
|
up a struct device for each of these clocks with a driver attached
|
||
|
to use the correct runtime PM operations. Thus the complete handling
|
||
|
of these clocks has been moved into its own driver.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/Makefile | 1 +
|
||
|
drivers/clk/rockchip/clk-rk3588.c | 23 +------
|
||
|
drivers/clk/rockchip/clk.c | 52 ++++++++++++++++
|
||
|
drivers/clk/rockchip/clk.h | 25 ++++++++
|
||
|
drivers/clk/rockchip/gate-link.c | 99 +++++++++++++++++++++++++++++++
|
||
|
5 files changed, 179 insertions(+), 21 deletions(-)
|
||
|
create mode 100644 drivers/clk/rockchip/gate-link.c
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
|
||
|
index 36894f6a7022..179be95c6ffb 100644
|
||
|
--- a/drivers/clk/rockchip/Makefile
|
||
|
+++ b/drivers/clk/rockchip/Makefile
|
||
|
@@ -13,6 +13,7 @@ clk-rockchip-y += clk-inverter.o
|
||
|
clk-rockchip-y += clk-mmc-phase.o
|
||
|
clk-rockchip-y += clk-muxgrf.o
|
||
|
clk-rockchip-y += clk-ddr.o
|
||
|
+clk-rockchip-y += gate-link.o
|
||
|
clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o
|
||
|
|
||
|
obj-$(CONFIG_CLK_PX30) += clk-px30.o
|
||
|
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
index 29f31a5526fa..1bf84ac44e85 100644
|
||
|
--- a/drivers/clk/rockchip/clk-rk3588.c
|
||
|
+++ b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
@@ -12,25 +12,6 @@
|
||
|
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
|
||
|
#include "clk.h"
|
||
|
|
||
|
-/*
|
||
|
- * Recent Rockchip SoCs have a new hardware block called Native Interface
|
||
|
- * Unit (NIU), which gates clocks to devices behind them. These effectively
|
||
|
- * need two parent clocks.
|
||
|
- *
|
||
|
- * Downstream enables the linked clock via runtime PM whenever the gate is
|
||
|
- * enabled. This implementation uses separate clock nodes for each of the
|
||
|
- * linked gate clocks, which leaks parts of the clock tree into DT.
|
||
|
- *
|
||
|
- * The GATE_LINK macro instead takes the second parent via 'linkname', but
|
||
|
- * ignores the information. Once the clock framework is ready to handle it, the
|
||
|
- * information should be passed on here. But since these clocks are required to
|
||
|
- * access multiple relevant IP blocks, such as PCIe or USB, we mark all linked
|
||
|
- * clocks critical until a better solution is available. This will waste some
|
||
|
- * power, but avoids leaking implementation details into DT or hanging the
|
||
|
- * system.
|
||
|
- */
|
||
|
-#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
|
||
|
- GATE(_id, cname, pname, f, o, b, gf)
|
||
|
#define RK3588_LINKED_CLK CLK_IS_CRITICAL
|
||
|
|
||
|
|
||
|
@@ -2513,8 +2494,8 @@ static int clk_rk3588_probe(struct platform_device *pdev)
|
||
|
struct device *dev = &pdev->dev;
|
||
|
struct device_node *np = dev->of_node;
|
||
|
|
||
|
- rockchip_clk_register_branches(ctx, rk3588_clk_branches,
|
||
|
- ARRAY_SIZE(rk3588_clk_branches));
|
||
|
+ rockchip_clk_register_late_branches(dev, ctx, rk3588_clk_branches,
|
||
|
+ ARRAY_SIZE(rk3588_clk_branches));
|
||
|
|
||
|
rk3588_rst_init(np, ctx->reg_base);
|
||
|
rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST, NULL);
|
||
|
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
|
||
|
index e150bc1fc319..f5f11cc60046 100644
|
||
|
--- a/drivers/clk/rockchip/clk.c
|
||
|
+++ b/drivers/clk/rockchip/clk.c
|
||
|
@@ -19,6 +19,7 @@
|
||
|
#include <linux/clk-provider.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/mfd/syscon.h>
|
||
|
+#include <linux/platform_device.h>
|
||
|
#include <linux/regmap.h>
|
||
|
#include <linux/reboot.h>
|
||
|
|
||
|
@@ -440,6 +441,29 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id);
|
||
|
|
||
|
+static struct platform_device *rockchip_clk_register_gate_link(
|
||
|
+ struct device *parent_dev,
|
||
|
+ struct rockchip_clk_provider *ctx,
|
||
|
+ struct rockchip_clk_branch *clkbr)
|
||
|
+{
|
||
|
+ struct rockchip_gate_link_platdata gate_link_pdata = {
|
||
|
+ .ctx = ctx,
|
||
|
+ .clkbr = clkbr,
|
||
|
+ };
|
||
|
+
|
||
|
+ struct platform_device_info pdevinfo = {
|
||
|
+ .parent = parent_dev,
|
||
|
+ .name = "rockchip-gate-link-clk",
|
||
|
+ .id = clkbr->id,
|
||
|
+ .fwnode = dev_fwnode(parent_dev),
|
||
|
+ .of_node_reused = true,
|
||
|
+ .data = &gate_link_pdata,
|
||
|
+ .size_data = sizeof(gate_link_pdata),
|
||
|
+ };
|
||
|
+
|
||
|
+ return platform_device_register_full(&pdevinfo);
|
||
|
+}
|
||
|
+
|
||
|
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
|
||
|
struct rockchip_clk_branch *list,
|
||
|
unsigned int nr_clk)
|
||
|
@@ -565,6 +589,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
|
||
|
list->div_width, list->div_flags,
|
||
|
ctx->reg_base, &ctx->lock);
|
||
|
break;
|
||
|
+ case branch_linked_gate:
|
||
|
+ /* must be registered late, fall-through for error message */
|
||
|
+ break;
|
||
|
}
|
||
|
|
||
|
/* none of the cases above matched */
|
||
|
@@ -585,6 +612,31 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
|
||
|
|
||
|
+void rockchip_clk_register_late_branches(struct device *dev,
|
||
|
+ struct rockchip_clk_provider *ctx,
|
||
|
+ struct rockchip_clk_branch *list,
|
||
|
+ unsigned int nr_clk)
|
||
|
+{
|
||
|
+ unsigned int idx;
|
||
|
+
|
||
|
+ for (idx = 0; idx < nr_clk; idx++, list++) {
|
||
|
+ struct platform_device *pdev = NULL;
|
||
|
+
|
||
|
+ switch (list->branch_type) {
|
||
|
+ case branch_linked_gate:
|
||
|
+ pdev = rockchip_clk_register_gate_link(dev, ctx, list);
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(dev, "unknown clock type %d\n", list->branch_type);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!pdev)
|
||
|
+ dev_err(dev, "failed to register device for clock %s\n", list->name);
|
||
|
+ }
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches);
|
||
|
+
|
||
|
void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
|
||
|
unsigned int lookup_id,
|
||
|
const char *name, const char *const *parent_names,
|
||
|
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
|
||
|
index e39392e1c2a2..15aa2fd5265b 100644
|
||
|
--- a/drivers/clk/rockchip/clk.h
|
||
|
+++ b/drivers/clk/rockchip/clk.h
|
||
|
@@ -517,6 +517,7 @@ enum rockchip_clk_branch_type {
|
||
|
branch_divider,
|
||
|
branch_fraction_divider,
|
||
|
branch_gate,
|
||
|
+ branch_linked_gate,
|
||
|
branch_mmc,
|
||
|
branch_inverter,
|
||
|
branch_factor,
|
||
|
@@ -544,6 +545,7 @@ struct rockchip_clk_branch {
|
||
|
int gate_offset;
|
||
|
u8 gate_shift;
|
||
|
u8 gate_flags;
|
||
|
+ unsigned int linked_clk_id;
|
||
|
struct rockchip_clk_branch *child;
|
||
|
};
|
||
|
|
||
|
@@ -842,6 +844,20 @@ struct rockchip_clk_branch {
|
||
|
.gate_flags = gf, \
|
||
|
}
|
||
|
|
||
|
+#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
|
||
|
+ { \
|
||
|
+ .id = _id, \
|
||
|
+ .branch_type = branch_linked_gate, \
|
||
|
+ .name = cname, \
|
||
|
+ .parent_names = (const char *[]){ pname }, \
|
||
|
+ .linked_clk_id = linkedclk, \
|
||
|
+ .num_parents = 1, \
|
||
|
+ .flags = f, \
|
||
|
+ .gate_offset = o, \
|
||
|
+ .gate_shift = b, \
|
||
|
+ .gate_flags = gf, \
|
||
|
+ }
|
||
|
+
|
||
|
#define MMC(_id, cname, pname, offset, shift) \
|
||
|
{ \
|
||
|
.id = _id, \
|
||
|
@@ -981,6 +997,11 @@ static inline void rockchip_clk_set_lookup(struct rockchip_clk_provider *ctx,
|
||
|
ctx->clk_data.clks[id] = clk;
|
||
|
}
|
||
|
|
||
|
+struct rockchip_gate_link_platdata {
|
||
|
+ struct rockchip_clk_provider *ctx;
|
||
|
+ struct rockchip_clk_branch *clkbr;
|
||
|
+};
|
||
|
+
|
||
|
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
|
||
|
void __iomem *base, unsigned long nr_clks);
|
||
|
void rockchip_clk_of_add_provider(struct device_node *np,
|
||
|
@@ -990,6 +1011,10 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
|
||
|
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
|
||
|
struct rockchip_clk_branch *list,
|
||
|
unsigned int nr_clk);
|
||
|
+void rockchip_clk_register_late_branches(struct device *dev,
|
||
|
+ struct rockchip_clk_provider *ctx,
|
||
|
+ struct rockchip_clk_branch *list,
|
||
|
+ unsigned int nr_clk);
|
||
|
void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
|
||
|
struct rockchip_pll_clock *pll_list,
|
||
|
unsigned int nr_pll, int grf_lock_offset);
|
||
|
diff --git a/drivers/clk/rockchip/gate-link.c b/drivers/clk/rockchip/gate-link.c
|
||
|
new file mode 100644
|
||
|
index 000000000000..47b6f3e7a6a2
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/clk/rockchip/gate-link.c
|
||
|
@@ -0,0 +1,99 @@
|
||
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
+/*
|
||
|
+ * Copyright (c) 2024 Collabora Ltd.
|
||
|
+ * Author: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#include <linux/clk.h>
|
||
|
+#include <linux/platform_device.h>
|
||
|
+#include <linux/pm_clock.h>
|
||
|
+#include <linux/pm_runtime.h>
|
||
|
+#include <linux/property.h>
|
||
|
+#include "clk.h"
|
||
|
+
|
||
|
+static int rk_clk_gate_link_register(struct device *dev,
|
||
|
+ struct rockchip_clk_provider *ctx,
|
||
|
+ struct rockchip_clk_branch *clkbr)
|
||
|
+{
|
||
|
+ unsigned long flags = clkbr->flags | CLK_SET_RATE_PARENT;
|
||
|
+ struct clk *clk;
|
||
|
+
|
||
|
+ clk = clk_register_gate(dev, clkbr->name, clkbr->parent_names[0],
|
||
|
+ flags, ctx->reg_base + clkbr->gate_offset,
|
||
|
+ clkbr->gate_shift, clkbr->gate_flags,
|
||
|
+ &ctx->lock);
|
||
|
+
|
||
|
+ if (IS_ERR(clk))
|
||
|
+ return PTR_ERR(clk);
|
||
|
+
|
||
|
+ rockchip_clk_set_lookup(ctx, clk, clkbr->id);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rk_clk_gate_link_probe(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct rockchip_gate_link_platdata *pdata;
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
+ struct clk *linked_clk;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ pdata = dev_get_platdata(dev);
|
||
|
+ if (!pdata)
|
||
|
+ return dev_err_probe(dev, -ENODEV, "missing platform data");
|
||
|
+
|
||
|
+ ret = devm_pm_runtime_enable(dev);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = devm_pm_clk_create(dev);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id);
|
||
|
+ ret = pm_clk_add_clk(dev, linked_clk);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = rk_clk_gate_link_register(dev, pdata->ctx, pdata->clkbr);
|
||
|
+ if (ret)
|
||
|
+ goto err;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+
|
||
|
+err:
|
||
|
+ pm_clk_remove_clk(dev, linked_clk);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static void rk_clk_gate_link_remove(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct rockchip_gate_link_platdata *pdata;
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
+ struct clk *clk, *linked_clk;
|
||
|
+
|
||
|
+ pdata = dev_get_platdata(dev);
|
||
|
+ clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->id);
|
||
|
+ linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id);
|
||
|
+ rockchip_clk_set_lookup(pdata->ctx, ERR_PTR(-ENODEV), pdata->clkbr->id);
|
||
|
+ clk_unregister_gate(clk);
|
||
|
+ pm_clk_remove_clk(dev, linked_clk);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct dev_pm_ops rk_clk_gate_link_pm_ops = {
|
||
|
+ SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
|
||
|
+};
|
||
|
+
|
||
|
+struct platform_driver rk_clk_gate_link_driver = {
|
||
|
+ .probe = rk_clk_gate_link_probe,
|
||
|
+ .remove_new = rk_clk_gate_link_remove,
|
||
|
+ .driver = {
|
||
|
+ .name = "rockchip-gate-link-clk",
|
||
|
+ .pm = &rk_clk_gate_link_pm_ops,
|
||
|
+ },
|
||
|
+};
|
||
|
+
|
||
|
+static int __init rk_clk_gate_link_drv_register(void)
|
||
|
+{
|
||
|
+ return platform_driver_register(&rk_clk_gate_link_driver);
|
||
|
+}
|
||
|
+core_initcall(rk_clk_gate_link_drv_register);
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 97f9b5fa575c07a80527abdf17bf9153d258df23 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Fri, 8 Mar 2024 23:32:05 +0100
|
||
|
Subject: [PATCH 18/54] clk: rockchip: rk3588: drop RK3588_LINKED_CLK
|
||
|
|
||
|
With the proper GATE_LINK support, we no longer need to keep the
|
||
|
linked clocks always on. Thus it's time to drop the CLK_IS_CRITICAL
|
||
|
flag for them.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/clk/rockchip/clk-rk3588.c | 27 ++++++++++++---------------
|
||
|
1 file changed, 12 insertions(+), 15 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
index 1bf84ac44e85..42579b6f74b4 100644
|
||
|
--- a/drivers/clk/rockchip/clk-rk3588.c
|
||
|
+++ b/drivers/clk/rockchip/clk-rk3588.c
|
||
|
@@ -12,9 +12,6 @@
|
||
|
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
|
||
|
#include "clk.h"
|
||
|
|
||
|
-#define RK3588_LINKED_CLK CLK_IS_CRITICAL
|
||
|
-
|
||
|
-
|
||
|
#define RK3588_GRF_SOC_STATUS0 0x600
|
||
|
#define RK3588_PHYREF_ALT_GATE 0xc38
|
||
|
|
||
|
@@ -1439,7 +1436,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
COMPOSITE_NODIV(HCLK_NVM_ROOT, "hclk_nvm_root", mux_200m_100m_50m_24m_p, 0,
|
||
|
RK3588_CLKSEL_CON(77), 0, 2, MFLAGS,
|
||
|
RK3588_CLKGATE_CON(31), 0, GFLAGS),
|
||
|
- COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, 0,
|
||
|
RK3588_CLKSEL_CON(77), 7, 1, MFLAGS, 2, 5, DFLAGS,
|
||
|
RK3588_CLKGATE_CON(31), 1, GFLAGS),
|
||
|
GATE(ACLK_EMMC, "aclk_emmc", "aclk_nvm_root", 0,
|
||
|
@@ -1668,13 +1665,13 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
RK3588_CLKGATE_CON(42), 9, GFLAGS),
|
||
|
|
||
|
/* vdpu */
|
||
|
- COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, 0,
|
||
|
RK3588_CLKSEL_CON(98), 5, 2, MFLAGS, 0, 5, DFLAGS,
|
||
|
RK3588_CLKGATE_CON(44), 0, GFLAGS),
|
||
|
COMPOSITE_NODIV(ACLK_VDPU_LOW_ROOT, "aclk_vdpu_low_root", mux_400m_200m_100m_24m_p, 0,
|
||
|
RK3588_CLKSEL_CON(98), 7, 2, MFLAGS,
|
||
|
RK3588_CLKGATE_CON(44), 1, GFLAGS),
|
||
|
- COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, 0,
|
||
|
RK3588_CLKSEL_CON(98), 9, 2, MFLAGS,
|
||
|
RK3588_CLKGATE_CON(44), 2, GFLAGS),
|
||
|
COMPOSITE(ACLK_JPEG_DECODER_ROOT, "aclk_jpeg_decoder_root", gpll_cpll_aupll_spll_p, 0,
|
||
|
@@ -1725,9 +1722,9 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
COMPOSITE(ACLK_RKVENC0_ROOT, "aclk_rkvenc0_root", gpll_cpll_npll_p, 0,
|
||
|
RK3588_CLKSEL_CON(102), 7, 2, MFLAGS, 2, 5, DFLAGS,
|
||
|
RK3588_CLKGATE_CON(47), 1, GFLAGS),
|
||
|
- GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", RK3588_LINKED_CLK,
|
||
|
+ GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", 0,
|
||
|
RK3588_CLKGATE_CON(47), 4, GFLAGS),
|
||
|
- GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", RK3588_LINKED_CLK,
|
||
|
+ GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", 0,
|
||
|
RK3588_CLKGATE_CON(47), 5, GFLAGS),
|
||
|
COMPOSITE(CLK_RKVENC0_CORE, "clk_rkvenc0_core", gpll_cpll_aupll_npll_p, 0,
|
||
|
RK3588_CLKSEL_CON(102), 14, 2, MFLAGS, 9, 5, DFLAGS,
|
||
|
@@ -1737,10 +1734,10 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
RK3588_CLKGATE_CON(48), 6, GFLAGS),
|
||
|
|
||
|
/* vi */
|
||
|
- COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, 0,
|
||
|
RK3588_CLKSEL_CON(106), 5, 3, MFLAGS, 0, 5, DFLAGS,
|
||
|
RK3588_CLKGATE_CON(49), 0, GFLAGS),
|
||
|
- COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, 0,
|
||
|
RK3588_CLKSEL_CON(106), 8, 2, MFLAGS,
|
||
|
RK3588_CLKGATE_CON(49), 1, GFLAGS),
|
||
|
COMPOSITE_NODIV(PCLK_VI_ROOT, "pclk_vi_root", mux_100m_50m_24m_p, 0,
|
||
|
@@ -1910,10 +1907,10 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
COMPOSITE(ACLK_VOP_ROOT, "aclk_vop_root", gpll_cpll_dmyaupll_npll_spll_p, 0,
|
||
|
RK3588_CLKSEL_CON(110), 5, 3, MFLAGS, 0, 5, DFLAGS,
|
||
|
RK3588_CLKGATE_CON(52), 0, GFLAGS),
|
||
|
- COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, 0,
|
||
|
RK3588_CLKSEL_CON(110), 8, 2, MFLAGS,
|
||
|
RK3588_CLKGATE_CON(52), 1, GFLAGS),
|
||
|
- COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
|
||
|
+ COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, 0,
|
||
|
RK3588_CLKSEL_CON(110), 10, 2, MFLAGS,
|
||
|
RK3588_CLKGATE_CON(52), 2, GFLAGS),
|
||
|
COMPOSITE_NODIV(PCLK_VOP_ROOT, "pclk_vop_root", mux_100m_50m_24m_p, 0,
|
||
|
@@ -2416,7 +2413,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
|
||
|
static struct rockchip_clk_branch rk3588_clk_branches[] = {
|
||
|
GATE_LINK(ACLK_ISP1_PRE, "aclk_isp1_pre", "aclk_isp1_root", ACLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 6, GFLAGS),
|
||
|
GATE_LINK(HCLK_ISP1_PRE, "hclk_isp1_pre", "hclk_isp1_root", HCLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 8, GFLAGS),
|
||
|
- GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(31), 2, GFLAGS),
|
||
|
+ GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, 0, RK3588_CLKGATE_CON(31), 2, GFLAGS),
|
||
|
GATE_LINK(ACLK_USB, "aclk_usb", "aclk_usb_root", ACLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(42), 2, GFLAGS),
|
||
|
GATE_LINK(HCLK_USB, "hclk_usb", "hclk_usb_root", HCLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(42), 3, GFLAGS),
|
||
|
GATE_LINK(ACLK_JPEG_DECODER_PRE, "aclk_jpeg_decoder_pre", "aclk_jpeg_decoder_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(44), 7, GFLAGS),
|
||
|
@@ -2428,9 +2425,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] = {
|
||
|
GATE_LINK(HCLK_RKVDEC1_PRE, "hclk_rkvdec1_pre", "hclk_rkvdec1_root", HCLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(41), 4, GFLAGS),
|
||
|
GATE_LINK(ACLK_RKVDEC1_PRE, "aclk_rkvdec1_pre", "aclk_rkvdec1_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(41), 5, GFLAGS),
|
||
|
GATE_LINK(ACLK_HDCP0_PRE, "aclk_hdcp0_pre", "aclk_vo0_root", ACLK_VOP_LOW_ROOT, 0, RK3588_CLKGATE_CON(55), 9, GFLAGS),
|
||
|
- GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", HCLK_VOP_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(55), 5, GFLAGS),
|
||
|
+ GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", HCLK_VOP_ROOT, 0, RK3588_CLKGATE_CON(55), 5, GFLAGS),
|
||
|
GATE_LINK(ACLK_HDCP1_PRE, "aclk_hdcp1_pre", "aclk_hdcp1_root", ACLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(59), 6, GFLAGS),
|
||
|
- GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", HCLK_VO1USB_TOP_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(59), 9, GFLAGS),
|
||
|
+ GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", HCLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(59), 9, GFLAGS),
|
||
|
GATE_LINK(ACLK_AV1_PRE, "aclk_av1_pre", "aclk_av1_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(68), 1, GFLAGS),
|
||
|
GATE_LINK(PCLK_AV1_PRE, "pclk_av1_pre", "pclk_av1_root", HCLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(68), 4, GFLAGS),
|
||
|
GATE_LINK(HCLK_SDIO_PRE, "hclk_sdio_pre", "hclk_sdio_root", HCLK_NVM, 0, RK3588_CLKGATE_CON(75), 1, GFLAGS),
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 70a3aa25bcb8fa0c5f63d613ee562d6b50e43843 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 2 Jan 2024 09:35:43 +0100
|
||
|
Subject: [PATCH 19/54] arm64: dts: rockchip: rk3588-evb1: add bluetooth rfkill
|
||
|
|
||
|
Add rfkill support for bluetooth. Bluetooth support itself is still
|
||
|
missing, but this ensures bluetooth can be powered off properly.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts | 15 +++++++++++++++
|
||
|
1 file changed, 15 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
index 65e16410084a..761e0108285d 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
@@ -120,6 +120,15 @@ backlight: backlight {
|
||
|
pwms = <&pwm2 0 25000 0>;
|
||
|
};
|
||
|
|
||
|
+ bluetooth-rfkill {
|
||
|
+ compatible = "rfkill-gpio";
|
||
|
+ label = "rfkill-bluetooth";
|
||
|
+ radio-type = "bluetooth";
|
||
|
+ shutdown-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&bluetooth_pwren>;
|
||
|
+ };
|
||
|
+
|
||
|
wlan-rfkill {
|
||
|
compatible = "rfkill-gpio";
|
||
|
label = "rfkill-pcie-wlan";
|
||
|
@@ -481,6 +490,12 @@ speaker_amplifier_en: speaker-amplifier-en {
|
||
|
};
|
||
|
};
|
||
|
|
||
|
+ bluetooth {
|
||
|
+ bluetooth_pwren: bluetooth-pwren {
|
||
|
+ rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
rtl8111 {
|
||
|
rtl8111_isolate: rtl8111-isolate {
|
||
|
rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 40e20352a122c1fe1e35c3140034fedd5129199d Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 2 Jan 2024 09:39:11 +0100
|
||
|
Subject: [PATCH 20/54] arm64: dts: rockchip: rk3588-evb1: improve PCIe
|
||
|
ethernet pin muxing
|
||
|
|
||
|
Also describe clkreq and wake signals in the PCIe pinmux used
|
||
|
by the onboard LAN card.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts | 10 +++++++++-
|
||
|
1 file changed, 9 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
index 761e0108285d..dd2fb2515900 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
@@ -459,7 +459,7 @@ &pcie2x1l0 {
|
||
|
&pcie2x1l1 {
|
||
|
reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>;
|
||
|
pinctrl-names = "default";
|
||
|
- pinctrl-0 = <&pcie2_1_rst>, <&rtl8111_isolate>;
|
||
|
+ pinctrl-0 = <&pcie2_1_rst>, <&pcie2_1_wake>, <&pcie2_1_clkreq>, <&rtl8111_isolate>;
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
@@ -531,6 +531,14 @@ pcie2_0_clkreq: pcie2-0-clkreq {
|
||
|
pcie2_1_rst: pcie2-1-rst {
|
||
|
rockchip,pins = <4 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
};
|
||
|
+
|
||
|
+ pcie2_1_wake: pcie2-1-wake {
|
||
|
+ rockchip,pins = <4 RK_PA1 4 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
+ pcie2_1_clkreq: pcie2-1-clkreq {
|
||
|
+ rockchip,pins = <4 RK_PA0 4 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
};
|
||
|
|
||
|
pcie3 {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 42bfdc60fdfa7d27754aaa9116def24f018ca4a9 Mon Sep 17 00:00:00 2001
|
||
|
From: "Carsten Haitzler (Rasterman)" <raster@rasterman.com>
|
||
|
Date: Tue, 6 Feb 2024 10:12:54 +0000
|
||
|
Subject: [PATCH 21/54] arm64: dts: rockchip: Slow down EMMC a bit to keep IO
|
||
|
stable
|
||
|
|
||
|
This drops to hs200 mode and 150Mhz as this is actually stable across
|
||
|
eMMC modules. There exist some that are incompatible at higher rates
|
||
|
with the rk3588 and to avoid your filesystem corrupting due to IO
|
||
|
errors, be more conservative and reduce the max. speed.
|
||
|
|
||
|
Signed-off-by: Carsten Haitzler <raster@rasterman.com>
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 4 ++--
|
||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
index e18e970393a6..f311c8f9d437 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
@@ -475,8 +475,8 @@ &sdhci {
|
||
|
no-sdio;
|
||
|
no-sd;
|
||
|
non-removable;
|
||
|
- mmc-hs400-1_8v;
|
||
|
- mmc-hs400-enhanced-strobe;
|
||
|
+ max-frequency = <150000000>;
|
||
|
+ mmc-hs200-1_8v;
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From bddd59567560d46a3d9363db6692f3232199bd20 Mon Sep 17 00:00:00 2001
|
||
|
From: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
Date: Wed, 20 Dec 2023 18:30:13 +0530
|
||
|
Subject: [PATCH 22/54] dt-bindings: media: Document bindings for HDMI RX
|
||
|
Controller
|
||
|
|
||
|
Document bindings for the Synopsys DesignWare HDMI RX Controller.
|
||
|
|
||
|
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
|
||
|
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
---
|
||
|
.../bindings/media/snps,dw-hdmi-rx.yaml | 130 ++++++++++++++++++
|
||
|
1 file changed, 130 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..903aa62d33ef
|
||
|
--- /dev/null
|
||
|
+++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
|
||
|
@@ -0,0 +1,130 @@
|
||
|
+# SPDX-License-Identifier: (GPL-3.0 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>
|
||
|
+
|
||
|
+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: rst_a
|
||
|
+ - const: rst_p
|
||
|
+ - const: rst_ref
|
||
|
+ - const: rst_biu
|
||
|
+
|
||
|
+ pinctrl-names:
|
||
|
+ const: default
|
||
|
+
|
||
|
+ memory-region:
|
||
|
+ maxItems: 1
|
||
|
+
|
||
|
+ hdmirx-5v-detection-gpios:
|
||
|
+ description: GPIO specifier for 5V detection.
|
||
|
+ maxItems: 1
|
||
|
+
|
||
|
+ rockchip,grf:
|
||
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
||
|
+ description:
|
||
|
+ The phandle of the syscon node for the GRF register.
|
||
|
+
|
||
|
+ rockchip,vo1_grf:
|
||
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
||
|
+ description:
|
||
|
+ The phandle of the syscon node for the VO1 GRF register.
|
||
|
+
|
||
|
+required:
|
||
|
+ - compatible
|
||
|
+ - reg
|
||
|
+ - interrupts
|
||
|
+ - interrupt-names
|
||
|
+ - clocks
|
||
|
+ - clock-names
|
||
|
+ - power-domains
|
||
|
+ - resets
|
||
|
+ - pinctrl-0
|
||
|
+ - pinctrl-names
|
||
|
+ - hdmirx-5v-detection-gpios
|
||
|
+
|
||
|
+additionalProperties: false
|
||
|
+
|
||
|
+examples:
|
||
|
+ - |
|
||
|
+ #include <dt-bindings/clock/rockchip,rk3588-cru.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>
|
||
|
+ #include <dt-bindings/pinctrl/rockchip.h>
|
||
|
+ #include <dt-bindings/gpio/gpio.h>
|
||
|
+ hdmirx_ctrler: hdmirx-controller@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 = "rst_a", "rst_p", "rst_ref", "rst_biu";
|
||
|
+ pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ memory-region = <&hdmirx_cma>;
|
||
|
+ hdmirx-5v-detection-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>;
|
||
|
+ };
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 8353d22573a2b462a538a4b8e826f9fd9b4cdbae Mon Sep 17 00:00:00 2001
|
||
|
From: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
Date: Wed, 20 Dec 2023 16:50:14 +0530
|
||
|
Subject: [PATCH 23/54] arm64: dts: rockchip: Add device tree support for HDMI
|
||
|
RX Controller
|
||
|
|
||
|
Add device tree support for Synopsys DesignWare HDMI RX
|
||
|
Controller.
|
||
|
|
||
|
Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
|
||
|
Co-developed-by: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
|
||
|
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
|
||
|
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
---
|
||
|
.../boot/dts/rockchip/rk3588-pinctrl.dtsi | 41 +++++++++++++++
|
||
|
.../boot/dts/rockchip/rk3588-rock-5b.dts | 18 +++++++
|
||
|
arch/arm64/boot/dts/rockchip/rk3588.dtsi | 50 +++++++++++++++++++
|
||
|
3 files changed, 109 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-pinctrl.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-pinctrl.dtsi
|
||
|
index 244c66faa161..e5f3d0acbd55 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-pinctrl.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-pinctrl.dtsi
|
||
|
@@ -169,6 +169,47 @@ hdmim0_tx1_sda: hdmim0-tx1-sda {
|
||
|
/* hdmim0_tx1_sda */
|
||
|
<2 RK_PB4 4 &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 =
|
||
|
+ /* hdmim1_rx_cec */
|
||
|
+ <3 RK_PD1 5 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
+ /omit-if-no-ref/
|
||
|
+ hdmim1_rx_hpdin: hdmim1-rx-hpdin {
|
||
|
+ rockchip,pins =
|
||
|
+ /* hdmim1_rx_hpdin */
|
||
|
+ <3 RK_PD4 5 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
+ /omit-if-no-ref/
|
||
|
+ hdmim1_rx_scl: hdmim1-rx-scl {
|
||
|
+ rockchip,pins =
|
||
|
+ /* hdmim1_rx_scl */
|
||
|
+ <3 RK_PD2 5 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+
|
||
|
+ /omit-if-no-ref/
|
||
|
+ hdmim1_rx_sda: hdmim1-rx-sda {
|
||
|
+ rockchip,pins =
|
||
|
+ /* hdmim1_rx_sda */
|
||
|
+ <3 RK_PD3 5 &pcfg_pull_none>;
|
||
|
+ };
|
||
|
};
|
||
|
|
||
|
i2c0 {
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
index f311c8f9d437..f013d7841e89 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
@@ -196,6 +196,18 @@ &gpu {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&hdmirx_cma {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&hdmirx_ctrler {
|
||
|
+ status = "okay";
|
||
|
+ hdmirx-5v-detection-gpios = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>;
|
||
|
+ pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ memory-region = <&hdmirx_cma>;
|
||
|
+};
|
||
|
+
|
||
|
&i2c0 {
|
||
|
pinctrl-names = "default";
|
||
|
pinctrl-0 = <&i2c0m2_xfer>;
|
||
|
@@ -408,6 +420,12 @@ &pcie3x4 {
|
||
|
};
|
||
|
|
||
|
&pinctrl {
|
||
|
+ hdmirx {
|
||
|
+ hdmirx_5v_detection: hdmirx-5v-detection {
|
||
|
+ rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
hym8563 {
|
||
|
hym8563_int: hym8563-int {
|
||
|
rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588.dtsi b/arch/arm64/boot/dts/rockchip/rk3588.dtsi
|
||
|
index 5984016b5f96..534c42262c73 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588.dtsi
|
||
|
@@ -7,6 +7,24 @@
|
||
|
#include "rk3588-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.
|
||
|
+ */
|
||
|
+ hdmirx_cma: hdmirx_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 +153,38 @@ i2s10_8ch: i2s@fde00000 {
|
||
|
status = "disabled";
|
||
|
};
|
||
|
|
||
|
+ hdmirx_ctrler: hdmirx-controller@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 = "rst_a", "rst_p", "rst_ref", "rst_biu";
|
||
|
+ pinctrl-0 = <&hdmim1_rx>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ status = "disabled";
|
||
|
+ };
|
||
|
+
|
||
|
pcie3x4: pcie@fe150000 {
|
||
|
compatible = "rockchip,rk3588-pcie", "rockchip,rk3568-pcie";
|
||
|
#address-cells = <3>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 820dbb912706cdfc378cd4e344a92aca94a6dc57 Mon Sep 17 00:00:00 2001
|
||
|
From: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
Date: Wed, 20 Dec 2023 16:52:01 +0530
|
||
|
Subject: [PATCH 24/54] media: platform: synopsys: Add support for hdmi input
|
||
|
driver
|
||
|
|
||
|
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.
|
||
|
|
||
|
Signed-off-by: Dingxian Wen <shawn.wen@rock-chips.com>
|
||
|
Co-developed-by: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
|
||
|
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
|
||
|
Signed-off-by: Shreeya Patel <shreeya.patel@collabora.com>
|
||
|
---
|
||
|
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 | 18 +
|
||
|
.../media/platform/synopsys/hdmirx/Makefile | 4 +
|
||
|
.../platform/synopsys/hdmirx/snps_hdmirx.c | 2856 +++++++++++++++++
|
||
|
.../platform/synopsys/hdmirx/snps_hdmirx.h | 394 +++
|
||
|
.../synopsys/hdmirx/snps_hdmirx_cec.c | 289 ++
|
||
|
.../synopsys/hdmirx/snps_hdmirx_cec.h | 46 +
|
||
|
10 files changed, 3614 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 2d79bfc68c15..a7b84d45cb86 100644
|
||
|
--- a/drivers/media/platform/Kconfig
|
||
|
+++ b/drivers/media/platform/Kconfig
|
||
|
@@ -83,6 +83,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 da17301f7439..3b7aff947f91 100644
|
||
|
--- a/drivers/media/platform/Makefile
|
||
|
+++ b/drivers/media/platform/Makefile
|
||
|
@@ -26,6 +26,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..adcdb7c2ed79
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
|
||
|
@@ -0,0 +1,18 @@
|
||
|
+# 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.
|
||
|
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..63a38ee089ec
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
|
||
|
@@ -0,0 +1,2856 @@
|
||
|
+// 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/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 <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_edid_version {
|
||
|
+ HDMIRX_EDID_USER = 0,
|
||
|
+ HDMIRX_EDID_340M = 1,
|
||
|
+ HDMIRX_EDID_600M = 2,
|
||
|
+};
|
||
|
+
|
||
|
+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_output_fmt {
|
||
|
+ u32 fourcc;
|
||
|
+ u8 cplanes;
|
||
|
+ u8 mplanes;
|
||
|
+ u8 bpp[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 hdmirx_output_fmt *out_fmt;
|
||
|
+ struct mutex vlock;
|
||
|
+ spinlock_t vbq_lock;
|
||
|
+ 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_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;
|
||
|
+ struct mutex work_lock;
|
||
|
+ 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_edid_version edid_version;
|
||
|
+ 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;
|
||
|
+};
|
||
|
+
|
||
|
+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 u8 edid_init_data_600M[] = {
|
||
|
+ 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, 0x00, 0x00, 0x00, 0x01, 0x01,
|
||
|
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||
|
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xE8,
|
||
|
+ 0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58,
|
||
|
+ 0x8A, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E,
|
||
|
+ 0x08, 0xE8, 0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80,
|
||
|
+ 0xB0, 0x58, 0x8A, 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, 0x39,
|
||
|
+
|
||
|
+ 0x02, 0x03, 0x21, 0xD2, 0x41, 0x61, 0x23, 0x09,
|
||
|
+ 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x66, 0x03,
|
||
|
+ 0x0C, 0x00, 0x30, 0x00, 0x10, 0x67, 0xD8, 0x5D,
|
||
|
+ 0xC4, 0x01, 0x78, 0xC0, 0x07, 0xE3, 0x05, 0x03,
|
||
|
+ 0x01, 0x08, 0xE8, 0x00, 0x30, 0xF2, 0x70, 0x5A,
|
||
|
+ 0x80, 0xB0, 0x58, 0x8A, 0x00, 0xC4, 0x8E, 0x21,
|
||
|
+ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8,
|
||
|
+};
|
||
|
+
|
||
|
+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 const struct hdmirx_output_fmt g_out_fmts[] = {
|
||
|
+ {
|
||
|
+ .fourcc = V4L2_PIX_FMT_BGR24,
|
||
|
+ .cplanes = 1,
|
||
|
+ .mplanes = 1,
|
||
|
+ .bpp = { 24 },
|
||
|
+ }, {
|
||
|
+ .fourcc = V4L2_PIX_FMT_NV24,
|
||
|
+ .cplanes = 2,
|
||
|
+ .mplanes = 1,
|
||
|
+ .bpp = { 8, 16 },
|
||
|
+ }, {
|
||
|
+ .fourcc = V4L2_PIX_FMT_NV16,
|
||
|
+ .cplanes = 2,
|
||
|
+ .mplanes = 1,
|
||
|
+ .bpp = { 8, 16 },
|
||
|
+ }, {
|
||
|
+ .fourcc = V4L2_PIX_FMT_NV12,
|
||
|
+ .cplanes = 2,
|
||
|
+ .mplanes = 1,
|
||
|
+ .bpp = { 8, 16 },
|
||
|
+ }
|
||
|
+};
|
||
|
+
|
||
|
+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:
|
||
|
+ return v4l2_ctrl_subscribe_event(fh, sub);
|
||
|
+ }
|
||
|
+
|
||
|
+ 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 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_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;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * query dv timings is during preview, dma's timing is stable,
|
||
|
+ * so we can get from DMA. If the current resolution is negative,
|
||
|
+ * get timing from CTRL need to change polarity of sync,
|
||
|
+ * maybe cause DMA errors.
|
||
|
+ */
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+
|
||
|
+ 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))
|
||
|
+ 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;
|
||
|
+ hdmirx_dev->edid_version = HDMIRX_EDID_USER;
|
||
|
+
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!v4l2_match_dv_timings(&hdmirx_dev->timings, &timings, 0, false)) {
|
||
|
+ /* automatically set timing rather than set by userspace */
|
||
|
+ hdmirx_dev->timings = timings;
|
||
|
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
|
||
|
+ "New format: ", &timings, false);
|
||
|
+ }
|
||
|
+
|
||
|
+ 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, "hdmirx", 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 int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs)
|
||
|
+{
|
||
|
+ /* Note: cbcr plane bpp is 16 bit */
|
||
|
+ switch (fcc) {
|
||
|
+ case V4L2_PIX_FMT_NV24:
|
||
|
+ *xsubs = 1;
|
||
|
+ *ysubs = 1;
|
||
|
+ break;
|
||
|
+ case V4L2_PIX_FMT_NV16:
|
||
|
+ *xsubs = 2;
|
||
|
+ *ysubs = 1;
|
||
|
+ break;
|
||
|
+ case V4L2_PIX_FMT_NV12:
|
||
|
+ *xsubs = 2;
|
||
|
+ *ysubs = 2;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static u32 hdmirx_align_bits_per_pixel(const struct hdmirx_output_fmt *fmt,
|
||
|
+ int plane_index)
|
||
|
+{
|
||
|
+ u32 bpp = 0;
|
||
|
+
|
||
|
+ if (fmt) {
|
||
|
+ switch (fmt->fourcc) {
|
||
|
+ case V4L2_PIX_FMT_NV24:
|
||
|
+ case V4L2_PIX_FMT_NV16:
|
||
|
+ case V4L2_PIX_FMT_NV12:
|
||
|
+ case V4L2_PIX_FMT_BGR24:
|
||
|
+ bpp = fmt->bpp[plane_index];
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ pr_err("fourcc: %#x is not supported\n", fmt->fourcc);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return bpp;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct hdmirx_output_fmt *find_output_fmt(u32 pixelfmt)
|
||
|
+{
|
||
|
+ const struct hdmirx_output_fmt *fmt;
|
||
|
+ u32 i;
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(g_out_fmts); i++) {
|
||
|
+ fmt = &g_out_fmts[i];
|
||
|
+ if (fmt->fourcc == pixelfmt)
|
||
|
+ return fmt;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+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 hdmirx_output_fmt *fmt;
|
||
|
+ unsigned int imagesize = 0, planes;
|
||
|
+ u32 xsubs = 1, ysubs = 1, i;
|
||
|
+
|
||
|
+ memset(&pixm->plane_fmt[0], 0, sizeof(struct v4l2_plane_pix_format));
|
||
|
+ fmt = find_output_fmt(pixm->pixelformat);
|
||
|
+ if (!fmt) {
|
||
|
+ fmt = &g_out_fmts[0];
|
||
|
+ v4l2_err(v4l2_dev,
|
||
|
+ "%s: set_fmt:%#x not supported, use def_fmt:%x\n",
|
||
|
+ __func__, pixm->pixelformat, fmt->fourcc);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!bt->width || !bt->height)
|
||
|
+ v4l2_err(v4l2_dev, "%s: invalid resolution:%#xx%#x\n",
|
||
|
+ __func__, bt->width, bt->height);
|
||
|
+
|
||
|
+ pixm->pixelformat = fmt->fourcc;
|
||
|
+ pixm->width = bt->width;
|
||
|
+ pixm->height = bt->height;
|
||
|
+ pixm->num_planes = fmt->mplanes;
|
||
|
+ 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));
|
||
|
+
|
||
|
+ /* calculate plane size and image size */
|
||
|
+ fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);
|
||
|
+ planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
|
||
|
+
|
||
|
+ for (i = 0; i < planes; i++) {
|
||
|
+ struct v4l2_plane_pix_format *plane_fmt;
|
||
|
+ int width, height, bpl, size, bpp;
|
||
|
+
|
||
|
+ if (!i) {
|
||
|
+ width = pixm->width;
|
||
|
+ height = pixm->height;
|
||
|
+ } else {
|
||
|
+ width = pixm->width / xsubs;
|
||
|
+ height = pixm->height / ysubs;
|
||
|
+ }
|
||
|
+
|
||
|
+ bpp = hdmirx_align_bits_per_pixel(fmt, i);
|
||
|
+ bpl = ALIGN(width * bpp / HDMIRX_STORED_BIT_WIDTH,
|
||
|
+ MEMORY_ALIGN_ROUND_UP_BYTES);
|
||
|
+ size = bpl * height;
|
||
|
+ imagesize += size;
|
||
|
+
|
||
|
+ if (fmt->mplanes > 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.
|
||
|
+ * It's important since we want to unify non-MPLANE and MPLANE.
|
||
|
+ */
|
||
|
+ if (fmt->mplanes == 1)
|
||
|
+ pixm->plane_fmt[0].sizeimage = imagesize;
|
||
|
+
|
||
|
+ if (!try) {
|
||
|
+ stream->out_fmt = fmt;
|
||
|
+ 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, fmt->fourcc);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+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));
|
||
|
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform: snps_hdmirx");
|
||
|
+
|
||
|
+ 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 hdmirx_output_fmt *out_fmt;
|
||
|
+ u32 i, height;
|
||
|
+
|
||
|
+ pixm = &stream->pixm;
|
||
|
+ out_fmt = stream->out_fmt;
|
||
|
+
|
||
|
+ if (!num_planes || !out_fmt) {
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+
|
||
|
+ *num_planes = out_fmt->mplanes;
|
||
|
+ height = pixm->height;
|
||
|
+
|
||
|
+ for (i = 0; i < out_fmt->mplanes; i++) {
|
||
|
+ const struct v4l2_plane_pix_format *plane_fmt;
|
||
|
+ int h = height;
|
||
|
+
|
||
|
+ plane_fmt = &pixm->plane_fmt[i];
|
||
|
+ sizes[i] = plane_fmt->sizeimage / height * h;
|
||
|
+ }
|
||
|
+
|
||
|
+ 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 hdmirx_output_fmt *out_fmt;
|
||
|
+ 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_fmt = stream->out_fmt;
|
||
|
+
|
||
|
+ 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_fmt->mplanes; i++)
|
||
|
+ hdmirx_buf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
|
||
|
+
|
||
|
+ if (out_fmt->mplanes == 1) {
|
||
|
+ if (out_fmt->cplanes == 1) {
|
||
|
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_CBCR] =
|
||
|
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_Y];
|
||
|
+ } else {
|
||
|
+ for (i = 0; i < out_fmt->cplanes - 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_err(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;
|
||
|
+ 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);
|
||
|
+
|
||
|
+ if (!stream->curr_buf) {
|
||
|
+ mutex_unlock(&hdmirx_dev->stream_lock);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ 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;
|
||
|
+ q->allow_cache_hints = 0;
|
||
|
+ q->bidirectional = 1;
|
||
|
+ 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,
|
||
|
+ .read = vb2_fop_read,
|
||
|
+ .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;
|
||
|
+ char *vdev_name;
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+ vdev_name = "stream_hdmirx";
|
||
|
+ strscpy(vdev->name, vdev_name, 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 hdmirx_output_fmt *fmt = stream->out_fmt;
|
||
|
+ u32 i;
|
||
|
+
|
||
|
+ /* Dequeue a filled buffer */
|
||
|
+ for (i = 0; i < fmt->mplanes; 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 = "rst_a";
|
||
|
+ hdmirx_dev->resets[HDMIRX_RST_P].id = "rst_p";
|
||
|
+ hdmirx_dev->resets[HDMIRX_RST_REF].id = "rst_ref";
|
||
|
+ hdmirx_dev->resets[HDMIRX_RST_BIU].id = "rst_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, "hdmirx-5v-detection", GPIOD_IN);
|
||
|
+
|
||
|
+ if (IS_ERR(hdmirx_dev->detect_5v_gpio)) {
|
||
|
+ dev_err(dev, "failed to get hdmirx 5v 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_edid_init_config(struct snps_hdmirx_dev *hdmirx_dev)
|
||
|
+{
|
||
|
+ int ret;
|
||
|
+ struct v4l2_edid def_edid;
|
||
|
+
|
||
|
+ /* disable hpd and write edid */
|
||
|
+ def_edid.pad = 0;
|
||
|
+ def_edid.start_block = 0;
|
||
|
+ def_edid.blocks = EDID_NUM_BLOCKS_MAX;
|
||
|
+ if (hdmirx_dev->edid_version == HDMIRX_EDID_600M)
|
||
|
+ def_edid.edid = edid_init_data_600M;
|
||
|
+ else
|
||
|
+ def_edid.edid = edid_init_data_340M;
|
||
|
+ ret = hdmirx_write_edid(hdmirx_dev, &def_edid, false);
|
||
|
+ 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);
|
||
|
+
|
||
|
+ hdmirx_edid_init_config(hdmirx_dev);
|
||
|
+
|
||
|
+ 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;
|
||
|
+ cec_data.edid = edid_init_data_340M;
|
||
|
+
|
||
|
+ 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);
|
||
|
+ hdmirx_dev->edid_version = HDMIRX_EDID_340M;
|
||
|
+
|
||
|
+ 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);
|
||
|
+ 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_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 int 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);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+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 v2");
|
||
|
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..8554cbc4ccde
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
|
||
|
@@ -0,0 +1,289 @@
|
||
|
+// 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;
|
||
|
+}
|
||
|
+
|
||
|
+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;
|
||
|
+ if (msg->len > 16)
|
||
|
+ msg_len = 16;
|
||
|
+ if (msg_len <= 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ 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 = msg_len / 4 + 1;
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+
|
||
|
+ 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;
|
||
|
+
|
||
|
+ if (!data)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * 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;
|
||
|
+ cec->edid = (struct edid *)data->edid;
|
||
|
+
|
||
|
+ 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, "rk-hdmirx",
|
||
|
+ CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
|
||
|
+ CEC_CAP_RC | CEC_CAP_PASSTHROUGH,
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+
|
||
|
+ cec_s_phys_addr_from_edid(cec->adap, cec->edid);
|
||
|
+
|
||
|
+ 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)
|
||
|
+{
|
||
|
+ if (!cec)
|
||
|
+ return;
|
||
|
+
|
||
|
+ 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..ae43f74d471d
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
|
||
|
@@ -0,0 +1,46 @@
|
||
|
+/* 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;
|
||
|
+ u8 *edid;
|
||
|
+};
|
||
|
+
|
||
|
+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 edid *edid;
|
||
|
+};
|
||
|
+
|
||
|
+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.44.1
|
||
|
|
||
|
|
||
|
From d3f4d04e3feba524c6e2fa0a2f8e25739a936396 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Wed, 15 May 2024 18:30:14 +0200
|
||
|
Subject: [PATCH 25/54] usb: typec: tcpm: print error on hard reset
|
||
|
|
||
|
A USB-C hard reset involves removing the voltage from VBUS for some
|
||
|
time. So basically it has the same effect as removing the USB-C plug
|
||
|
for a short moment. If the machine is powered from the USB-C port and
|
||
|
does not have a fallback supply (e.g. a battery), this will result in
|
||
|
a full machine reset due to power loss.
|
||
|
|
||
|
Ideally we want to avoid triggering a hard reset on these boards. A
|
||
|
non-working USB-C port is probably better than unplanned reboots. But
|
||
|
boards with a backup supply should do the hard reset to get everything
|
||
|
working again.
|
||
|
|
||
|
In theory it would be enough to check the self_powered property, but
|
||
|
it seems the property might not be configured consistently enough in
|
||
|
system firmwares.
|
||
|
|
||
|
So let's start with just printing an error message when a hard reset is
|
||
|
triggered on systems we expect to be affected. This at least makes
|
||
|
debugging issues on affected systems easier without impacting unaffected
|
||
|
systems too much.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/usb/typec/tcpm/tcpm.c | 2 ++
|
||
|
1 file changed, 2 insertions(+)
|
||
|
|
||
|
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||
|
index 8a1af08f71b6..375bc84d14a2 100644
|
||
|
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||
|
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||
|
@@ -5185,6 +5185,8 @@ static void run_state_machine(struct tcpm_port *port)
|
||
|
case HARD_RESET_SEND:
|
||
|
if (port->ams != NONE_AMS)
|
||
|
tcpm_ams_finish(port);
|
||
|
+ if (!port->self_powered && port->port_type == TYPEC_PORT_SNK)
|
||
|
+ dev_err(port->dev, "Initiating hard-reset, which might result in machine power-loss.\n");
|
||
|
/*
|
||
|
* State machine will be directed to HARD_RESET_START,
|
||
|
* thus set upcoming_state to INVALID_STATE.
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From e394b2e700e3fe42e9240f1e1640b2e9c715f179 Mon Sep 17 00:00:00 2001
|
||
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
Date: Tue, 14 May 2024 17:29:32 +0200
|
||
|
Subject: [PATCH 26/54] usb: typec: tcpm: avoid resets for missing source
|
||
|
capability messages
|
||
|
|
||
|
When the Linux Type-C controller drivers probe, they requests a soft
|
||
|
reset, which should result in the source restarting to send Source
|
||
|
Capability messages again independently of the previous state.
|
||
|
Unfortunately some USB PD sources do not follow the specification and
|
||
|
do not send them after a soft reset when they already negotiated a
|
||
|
specific contract before. The current way (and what is described in the
|
||
|
specificiation) to resolve this problem is triggering a hard reset.
|
||
|
|
||
|
But a hard reset is fatal on batteryless platforms powered via USB-C PD,
|
||
|
since that removes VBUS for some time. Since this is triggered at boot
|
||
|
time, the system will be stuck in a boot loop. Examples for platforms
|
||
|
affected by this are the Radxa Rock 5B or the Libre Computer Renegade
|
||
|
Elite ROC-RK3399-PC.
|
||
|
|
||
|
Instead of directly trying a hard reset when no Source Capability
|
||
|
message is send by the USB-PD source automatically, this changes the
|
||
|
state machine to try explicitly asking for the capabilities by sending
|
||
|
a Get Source Capability control message.
|
||
|
|
||
|
For me this solves issues with 2 different USB-PD sources - a RAVPower
|
||
|
powerbank and a Lemorele USB-C dock. Every other PD source I own
|
||
|
follows the specification and automatically sends the Source Capability
|
||
|
message after a soft reset, which works with or without this change.
|
||
|
|
||
|
I decided against making this extra step limited to devices not having
|
||
|
the self_powered flag set, since I don't see any huge drawbacks in this
|
||
|
approach and it keeps the logic simpler. The worst case scenario would
|
||
|
be a power source, which is really stuck. In that case the hard reset
|
||
|
is delayed by another 310ms.
|
||
|
|
||
|
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||
|
---
|
||
|
drivers/usb/typec/tcpm/tcpm.c | 27 +++++++++++++++++++++++++--
|
||
|
1 file changed, 25 insertions(+), 2 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
||
|
index 375bc84d14a2..bac6866617c8 100644
|
||
|
--- a/drivers/usb/typec/tcpm/tcpm.c
|
||
|
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
||
|
@@ -57,6 +57,7 @@
|
||
|
S(SNK_DISCOVERY_DEBOUNCE), \
|
||
|
S(SNK_DISCOVERY_DEBOUNCE_DONE), \
|
||
|
S(SNK_WAIT_CAPABILITIES), \
|
||
|
+ S(SNK_WAIT_CAPABILITIES_TIMEOUT), \
|
||
|
S(SNK_NEGOTIATE_CAPABILITIES), \
|
||
|
S(SNK_NEGOTIATE_PPS_CAPABILITIES), \
|
||
|
S(SNK_TRANSITION_SINK), \
|
||
|
@@ -3108,7 +3109,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
|
||
|
PD_MSG_CTRL_REJECT :
|
||
|
PD_MSG_CTRL_NOT_SUPP,
|
||
|
NONE_AMS);
|
||
|
- } else if (port->state == SNK_WAIT_CAPABILITIES) {
|
||
|
+ } else if (port->state == SNK_WAIT_CAPABILITIES ||
|
||
|
+ port->state == SNK_WAIT_CAPABILITIES_TIMEOUT) {
|
||
|
/*
|
||
|
* This message may be received even if VBUS is not
|
||
|
* present. This is quite unexpected; see USB PD
|
||
|
@@ -5039,10 +5041,31 @@ static void run_state_machine(struct tcpm_port *port)
|
||
|
tcpm_set_state(port, SNK_SOFT_RESET,
|
||
|
PD_T_SINK_WAIT_CAP);
|
||
|
} else {
|
||
|
- tcpm_set_state(port, hard_reset_state(port),
|
||
|
+ tcpm_set_state(port, SNK_WAIT_CAPABILITIES_TIMEOUT,
|
||
|
PD_T_SINK_WAIT_CAP);
|
||
|
}
|
||
|
break;
|
||
|
+ case SNK_WAIT_CAPABILITIES_TIMEOUT:
|
||
|
+ /*
|
||
|
+ * There are some USB PD sources in the field, which do not
|
||
|
+ * properly implement the specification and fail to start
|
||
|
+ * sending Source Capability messages after a soft reset. The
|
||
|
+ * specification suggests to do a hard reset when no Source
|
||
|
+ * capability message is received within PD_T_SINK_WAIT_CAP,
|
||
|
+ * but that might effectively kil the machine's power source.
|
||
|
+ *
|
||
|
+ * This slightly diverges from the specification and tries to
|
||
|
+ * recover from this by explicitly asking for the capabilities
|
||
|
+ * using the Get_Source_Cap control message before falling back
|
||
|
+ * to a hard reset. The control message should also be supported
|
||
|
+ * and handled by all USB PD source and dual role devices
|
||
|
+ * according to the specification.
|
||
|
+ */
|
||
|
+ if (tcpm_pd_send_control(port, PD_CTRL_GET_SOURCE_CAP, TCPC_TX_SOP))
|
||
|
+ tcpm_set_state_cond(port, hard_reset_state(port), 0);
|
||
|
+ else
|
||
|
+ tcpm_set_state(port, hard_reset_state(port), PD_T_SINK_WAIT_CAP);
|
||
|
+ break;
|
||
|
case SNK_NEGOTIATE_CAPABILITIES:
|
||
|
port->pd_capable = true;
|
||
|
tcpm_set_partner_usb_comm_capable(port,
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From d39959cd76c8f219df1de5ba951dec83cdb5c288 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 5 Feb 2024 01:38:48 +0200
|
||
|
Subject: [PATCH 27/54] [WIP] phy: phy-rockchip-samsung-hdptx: Add FRL & EARC
|
||
|
support
|
||
|
|
||
|
For upstreaming, this requires extending the standard PHY API to support
|
||
|
HDMI configuration options [1].
|
||
|
|
||
|
Currently, the bus_width PHY attribute is used to pass clock rate and
|
||
|
flags for 10-bit color depth, FRL and EARC. This is done by the HDMI
|
||
|
bridge driver via phy_set_bus_width().
|
||
|
|
||
|
[1]: https://lore.kernel.org/all/59d5595a24bbcca897e814440179fa2caf3dff38.1707040881.git.Sandor.yu@nxp.com/
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../phy/rockchip/phy-rockchip-samsung-hdptx.c | 434 +++++++++++++++++-
|
||
|
1 file changed, 431 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
index 946c01210ac8..44acea3f86af 100644
|
||
|
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
@@ -190,6 +190,12 @@
|
||
|
#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3)
|
||
|
#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2)
|
||
|
|
||
|
+#define HDMI20_MAX_RATE 600000000
|
||
|
+#define DATA_RATE_MASK 0xFFFFFFF
|
||
|
+#define COLOR_DEPTH_MASK BIT(31)
|
||
|
+#define HDMI_MODE_MASK BIT(30)
|
||
|
+#define HDMI_EARC_MASK BIT(29)
|
||
|
+
|
||
|
struct lcpll_config {
|
||
|
u32 bit_rate;
|
||
|
u8 lcvco_mode_en;
|
||
|
@@ -272,6 +278,25 @@ struct rk_hdptx_phy {
|
||
|
struct clk_bulk_data *clks;
|
||
|
int nr_clks;
|
||
|
struct reset_control_bulk_data rsts[RST_MAX];
|
||
|
+ bool earc_en;
|
||
|
+};
|
||
|
+
|
||
|
+static const struct lcpll_config lcpll_cfg[] = {
|
||
|
+ { 48000000, 1, 0, 0, 0x7d, 0x7d, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2,
|
||
|
+ 0, 0x13, 0x18, 1, 0, 0x20, 0x0c, 1, 0, },
|
||
|
+ { 40000000, 1, 1, 0, 0x68, 0x68, 1, 1, 0, 0, 0, 1, 1, 1, 1, 9, 0, 1, 1,
|
||
|
+ 0, 2, 3, 1, 0, 0x20, 0x0c, 1, 0, },
|
||
|
+ { 32000000, 1, 1, 1, 0x6b, 0x6b, 1, 1, 0, 1, 2, 1, 1, 1, 1, 9, 1, 2, 1,
|
||
|
+ 0, 0x0d, 0x18, 1, 0, 0x20, 0x0c, 1, 1, },
|
||
|
+};
|
||
|
+
|
||
|
+static const struct ropll_config ropll_frl_cfg[] = {
|
||
|
+ { 24000000, 0x19, 0x19, 1, 1, 0, 1, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
|
||
|
+ 0, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
|
||
|
+ { 18000000, 0x7d, 0x7d, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
|
||
|
+ 0, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
|
||
|
+ { 9000000, 0x7d, 0x7d, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
|
||
|
+ 0, 0, 0x20, 0x0c, 1, 0x0e, 0, 0, },
|
||
|
};
|
||
|
|
||
|
static const struct ropll_config ropll_tmds_cfg[] = {
|
||
|
@@ -449,6 +474,73 @@ static const struct reg_sequence rk_hdtpx_tmds_cmn_init_seq[] = {
|
||
|
REG_SEQ0(CMN_REG(009b), 0x00),
|
||
|
};
|
||
|
|
||
|
+static const struct reg_sequence rk_hdtpx_frl_cmn_init_seq[] = {
|
||
|
+ REG_SEQ0(CMN_REG(0011), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0017), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0026), 0x53),
|
||
|
+ REG_SEQ0(CMN_REG(0030), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0031), 0x20),
|
||
|
+ REG_SEQ0(CMN_REG(0032), 0x30),
|
||
|
+ REG_SEQ0(CMN_REG(0033), 0x0b),
|
||
|
+ REG_SEQ0(CMN_REG(0034), 0x23),
|
||
|
+ REG_SEQ0(CMN_REG(0042), 0xb8),
|
||
|
+ REG_SEQ0(CMN_REG(004e), 0x14),
|
||
|
+ REG_SEQ0(CMN_REG(0074), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0081), 0x09),
|
||
|
+ REG_SEQ0(CMN_REG(0086), 0x01),
|
||
|
+ REG_SEQ0(CMN_REG(0087), 0x0c),
|
||
|
+ REG_SEQ0(CMN_REG(009b), 0x10),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct reg_sequence rk_hdtpx_frl_ropll_cmn_init_seq[] = {
|
||
|
+ REG_SEQ0(CMN_REG(0008), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(001e), 0x14),
|
||
|
+ REG_SEQ0(CMN_REG(0020), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0021), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0022), 0x11),
|
||
|
+ REG_SEQ0(CMN_REG(0023), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0025), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0027), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0028), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(002a), 0x01),
|
||
|
+ REG_SEQ0(CMN_REG(002b), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(002c), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(002d), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(002e), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(002f), 0x04),
|
||
|
+ REG_SEQ0(CMN_REG(003d), 0x40),
|
||
|
+ REG_SEQ0(CMN_REG(005c), 0x25),
|
||
|
+ REG_SEQ0(CMN_REG(0089), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0094), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0097), 0x02),
|
||
|
+ REG_SEQ0(CMN_REG(0099), 0x04),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct reg_sequence rk_hdtpx_frl_lcpll_cmn_init_seq[] = {
|
||
|
+ REG_SEQ0(CMN_REG(0025), 0x10),
|
||
|
+ REG_SEQ0(CMN_REG(0027), 0x01),
|
||
|
+ REG_SEQ0(CMN_REG(0028), 0x0d),
|
||
|
+ REG_SEQ0(CMN_REG(002e), 0x02),
|
||
|
+ REG_SEQ0(CMN_REG(002f), 0x0d),
|
||
|
+ REG_SEQ0(CMN_REG(003d), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0051), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0055), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0059), 0x11),
|
||
|
+ REG_SEQ0(CMN_REG(005a), 0x03),
|
||
|
+ REG_SEQ0(CMN_REG(005c), 0x05),
|
||
|
+ REG_SEQ0(CMN_REG(005e), 0x07),
|
||
|
+ REG_SEQ0(CMN_REG(0060), 0x01),
|
||
|
+ REG_SEQ0(CMN_REG(0064), 0x07),
|
||
|
+ REG_SEQ0(CMN_REG(0065), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0069), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(006c), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0070), 0x01),
|
||
|
+ REG_SEQ0(CMN_REG(0089), 0x02),
|
||
|
+ REG_SEQ0(CMN_REG(0095), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0097), 0x00),
|
||
|
+ REG_SEQ0(CMN_REG(0099), 0x00),
|
||
|
+};
|
||
|
+
|
||
|
static const struct reg_sequence rk_hdtpx_common_sb_init_seq[] = {
|
||
|
REG_SEQ0(SB_REG(0114), 0x00),
|
||
|
REG_SEQ0(SB_REG(0115), 0x00),
|
||
|
@@ -472,6 +564,17 @@ static const struct reg_sequence rk_hdtpx_tmds_lntop_lowbr_seq[] = {
|
||
|
REG_SEQ0(LNTOP_REG(0205), 0x1f),
|
||
|
};
|
||
|
|
||
|
+static const struct reg_sequence rk_hdtpx_frl_lntop_init_seq[] = {
|
||
|
+ REG_SEQ0(LNTOP_REG(0200), 0x04),
|
||
|
+ REG_SEQ0(LNTOP_REG(0201), 0x00),
|
||
|
+ REG_SEQ0(LNTOP_REG(0202), 0x00),
|
||
|
+ REG_SEQ0(LNTOP_REG(0203), 0xf0),
|
||
|
+ REG_SEQ0(LNTOP_REG(0204), 0xff),
|
||
|
+ REG_SEQ0(LNTOP_REG(0205), 0xff),
|
||
|
+ REG_SEQ0(LNTOP_REG(0206), 0x05),
|
||
|
+ REG_SEQ0(LNTOP_REG(0207), 0x0f),
|
||
|
+};
|
||
|
+
|
||
|
static const struct reg_sequence rk_hdtpx_common_lane_init_seq[] = {
|
||
|
REG_SEQ0(LANE_REG(0303), 0x0c),
|
||
|
REG_SEQ0(LANE_REG(0307), 0x20),
|
||
|
@@ -550,6 +653,40 @@ static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = {
|
||
|
REG_SEQ0(LANE_REG(0606), 0x1c),
|
||
|
};
|
||
|
|
||
|
+static const struct reg_sequence rk_hdtpx_frl_ropll_lane_init_seq[] = {
|
||
|
+ REG_SEQ0(LANE_REG(0312), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0412), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0512), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0612), 0x3c),
|
||
|
+};
|
||
|
+
|
||
|
+static const struct reg_sequence rk_hdtpx_frl_lcpll_lane_init_seq[] = {
|
||
|
+ REG_SEQ0(LANE_REG(0312), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0412), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0512), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0612), 0x3c),
|
||
|
+ REG_SEQ0(LANE_REG(0303), 0x2f),
|
||
|
+ REG_SEQ0(LANE_REG(0403), 0x2f),
|
||
|
+ REG_SEQ0(LANE_REG(0503), 0x2f),
|
||
|
+ REG_SEQ0(LANE_REG(0603), 0x2f),
|
||
|
+ REG_SEQ0(LANE_REG(0305), 0x03),
|
||
|
+ REG_SEQ0(LANE_REG(0405), 0x03),
|
||
|
+ REG_SEQ0(LANE_REG(0505), 0x03),
|
||
|
+ REG_SEQ0(LANE_REG(0605), 0x03),
|
||
|
+ REG_SEQ0(LANE_REG(0306), 0xfc),
|
||
|
+ REG_SEQ0(LANE_REG(0406), 0xfc),
|
||
|
+ REG_SEQ0(LANE_REG(0506), 0xfc),
|
||
|
+ REG_SEQ0(LANE_REG(0606), 0xfc),
|
||
|
+ REG_SEQ0(LANE_REG(0305), 0x4f),
|
||
|
+ REG_SEQ0(LANE_REG(0405), 0x4f),
|
||
|
+ REG_SEQ0(LANE_REG(0505), 0x4f),
|
||
|
+ REG_SEQ0(LANE_REG(0605), 0x4f),
|
||
|
+ REG_SEQ0(LANE_REG(0304), 0x14),
|
||
|
+ REG_SEQ0(LANE_REG(0404), 0x14),
|
||
|
+ REG_SEQ0(LANE_REG(0504), 0x14),
|
||
|
+ REG_SEQ0(LANE_REG(0604), 0x14),
|
||
|
+};
|
||
|
+
|
||
|
static bool rk_hdptx_phy_is_rw_reg(struct device *dev, unsigned int reg)
|
||
|
{
|
||
|
switch (reg) {
|
||
|
@@ -651,6 +788,47 @@ static int rk_hdptx_post_enable_pll(struct rk_hdptx_phy *hdptx)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int rk_hdptx_post_power_up(struct rk_hdptx_phy *hdptx)
|
||
|
+{
|
||
|
+ u32 val;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 |
|
||
|
+ HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN;
|
||
|
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
|
||
|
+
|
||
|
+ usleep_range(10, 15);
|
||
|
+ reset_control_deassert(hdptx->rsts[RST_INIT].rstc);
|
||
|
+
|
||
|
+ usleep_range(10, 15);
|
||
|
+ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN;
|
||
|
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
|
||
|
+
|
||
|
+ usleep_range(10, 15);
|
||
|
+ reset_control_deassert(hdptx->rsts[RST_CMN].rstc);
|
||
|
+
|
||
|
+ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS, val,
|
||
|
+ val & HDPTX_O_PLL_LOCK_DONE, 20, 400);
|
||
|
+ if (ret) {
|
||
|
+ dev_err(hdptx->dev, "Failed to get PHY PLL lock: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ usleep_range(20, 30);
|
||
|
+ reset_control_deassert(hdptx->rsts[RST_LANE].rstc);
|
||
|
+
|
||
|
+ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS, val,
|
||
|
+ val & HDPTX_O_PHY_RDY, 100, 5000);
|
||
|
+ if (ret) {
|
||
|
+ dev_err(hdptx->dev, "Failed to get PHY ready: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ dev_dbg(hdptx->dev, "PHY ready\n");
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx)
|
||
|
{
|
||
|
u32 val;
|
||
|
@@ -680,6 +858,99 @@ static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx)
|
||
|
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
|
||
|
}
|
||
|
|
||
|
+static void rk_hdptx_earc_config(struct rk_hdptx_phy *hdptx)
|
||
|
+{
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0113), SB_RX_RCAL_OPT_CODE_MASK,
|
||
|
+ FIELD_PREP(SB_RX_RCAL_OPT_CODE_MASK, 1));
|
||
|
+ regmap_write(hdptx->regmap, SB_REG(011c), 0x04);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(011b), SB_AFC_TOL_MASK,
|
||
|
+ FIELD_PREP(SB_AFC_TOL_MASK, 3));
|
||
|
+ regmap_write(hdptx->regmap, SB_REG(0109), 0x05);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0120),
|
||
|
+ SB_EARC_EN_MASK | SB_EARC_AFC_EN_MASK,
|
||
|
+ FIELD_PREP(SB_EARC_EN_MASK, 1) |
|
||
|
+ FIELD_PREP(SB_EARC_AFC_EN_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(011b), SB_EARC_SIG_DET_BYPASS_MASK,
|
||
|
+ FIELD_PREP(SB_EARC_SIG_DET_BYPASS_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(011f),
|
||
|
+ SB_PWM_AFC_CTRL_MASK | SB_RCAL_RSTN_MASK,
|
||
|
+ FIELD_PREP(SB_PWM_AFC_CTRL_MASK, 0xc) |
|
||
|
+ FIELD_PREP(SB_RCAL_RSTN_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0115), SB_READY_DELAY_TIME_MASK,
|
||
|
+ FIELD_PREP(SB_READY_DELAY_TIME_MASK, 2));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0113), SB_RX_RTERM_CTRL_MASK,
|
||
|
+ FIELD_PREP(SB_RX_RTERM_CTRL_MASK, 3));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0102), ANA_SB_RXTERM_OFFSP_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_RXTERM_OFFSP_MASK, 3));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0103), ANA_SB_RXTERM_OFFSN_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_RXTERM_OFFSN_MASK, 3));
|
||
|
+
|
||
|
+ regmap_write(hdptx->regmap, SB_REG(011a), 0x03);
|
||
|
+ regmap_write(hdptx->regmap, SB_REG(0118), 0x0a);
|
||
|
+ regmap_write(hdptx->regmap, SB_REG(011e), 0x6a);
|
||
|
+ regmap_write(hdptx->regmap, SB_REG(011d), 0x67);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0117), FAST_PULSE_TIME_MASK,
|
||
|
+ FIELD_PREP(FAST_PULSE_TIME_MASK, 4));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0114),
|
||
|
+ SB_TG_SB_EN_DELAY_TIME_MASK | SB_TG_RXTERM_EN_DELAY_TIME_MASK,
|
||
|
+ FIELD_PREP(SB_TG_SB_EN_DELAY_TIME_MASK, 2) |
|
||
|
+ FIELD_PREP(SB_TG_RXTERM_EN_DELAY_TIME_MASK, 2));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0105), ANA_SB_TX_HLVL_PROG_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_TX_HLVL_PROG_MASK, 7));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0106), ANA_SB_TX_LLVL_PROG_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_TX_LLVL_PROG_MASK, 7));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), ANA_SB_VREG_GAIN_CTRL_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_VREG_GAIN_CTRL_MASK, 0));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0110), ANA_SB_VREG_REF_SEL_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_VREG_REF_SEL_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0115), SB_TG_OSC_EN_DELAY_TIME_MASK,
|
||
|
+ FIELD_PREP(SB_TG_OSC_EN_DELAY_TIME_MASK, 2));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0116), AFC_RSTN_DELAY_TIME_MASK,
|
||
|
+ FIELD_PREP(AFC_RSTN_DELAY_TIME_MASK, 2));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0109), ANA_SB_DMRX_AFC_DIV_RATIO_MASK,
|
||
|
+ FIELD_PREP(ANA_SB_DMRX_AFC_DIV_RATIO_MASK, 5));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0103), OVRD_SB_RX_RESCAL_DONE_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_RX_RESCAL_DONE_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), OVRD_SB_EN_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_EN_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0102), OVRD_SB_RXTERM_EN_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_RXTERM_EN_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0105), OVRD_SB_EARC_CMDC_EN_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_EARC_CMDC_EN_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(010f),
|
||
|
+ OVRD_SB_VREG_EN_MASK | OVRD_SB_VREG_LPF_BYPASS_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_VREG_EN_MASK, 1) |
|
||
|
+ FIELD_PREP(OVRD_SB_VREG_LPF_BYPASS_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0123), OVRD_SB_READY_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_READY_MASK, 1));
|
||
|
+
|
||
|
+ usleep_range(1000, 1100);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0103), SB_RX_RESCAL_DONE_MASK,
|
||
|
+ FIELD_PREP(SB_RX_RESCAL_DONE_MASK, 1));
|
||
|
+ usleep_range(50, 60);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), SB_EN_MASK,
|
||
|
+ FIELD_PREP(SB_EN_MASK, 1));
|
||
|
+ usleep_range(50, 60);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0102), SB_RXTERM_EN_MASK,
|
||
|
+ FIELD_PREP(SB_RXTERM_EN_MASK, 1));
|
||
|
+ usleep_range(50, 60);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0105), SB_EARC_CMDC_EN_MASK,
|
||
|
+ FIELD_PREP(SB_EARC_CMDC_EN_MASK, 1));
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), SB_VREG_EN_MASK,
|
||
|
+ FIELD_PREP(SB_VREG_EN_MASK, 1));
|
||
|
+ usleep_range(50, 60);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), OVRD_SB_VREG_LPF_BYPASS_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_VREG_LPF_BYPASS_MASK, 1));
|
||
|
+ usleep_range(250, 300);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), OVRD_SB_VREG_LPF_BYPASS_MASK,
|
||
|
+ FIELD_PREP(OVRD_SB_VREG_LPF_BYPASS_MASK, 0));
|
||
|
+ usleep_range(100, 120);
|
||
|
+ regmap_update_bits(hdptx->regmap, SB_REG(0123), SB_READY_MASK,
|
||
|
+ FIELD_PREP(SB_READY_MASK, 1));
|
||
|
+}
|
||
|
+
|
||
|
static bool rk_hdptx_phy_clk_pll_calc(unsigned int data_rate,
|
||
|
struct ropll_config *cfg)
|
||
|
{
|
||
|
@@ -755,9 +1026,13 @@ static bool rk_hdptx_phy_clk_pll_calc(unsigned int data_rate,
|
||
|
static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
|
||
|
unsigned int rate)
|
||
|
{
|
||
|
+ int i, bus_width = phy_get_bus_width(hdptx->phy);
|
||
|
+ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0;
|
||
|
const struct ropll_config *cfg = NULL;
|
||
|
struct ropll_config rc = {0};
|
||
|
- int i;
|
||
|
+
|
||
|
+ if (color_depth)
|
||
|
+ rate = rate * 10 / 8;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++)
|
||
|
if (rate == ropll_tmds_cfg[i].bit_rate) {
|
||
|
@@ -813,6 +1088,9 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
|
||
|
regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_POSTDIV_SEL_MASK,
|
||
|
FIELD_PREP(PLL_PCG_POSTDIV_SEL_MASK, cfg->pms_sdiv));
|
||
|
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_SEL_MASK,
|
||
|
+ FIELD_PREP(PLL_PCG_CLK_SEL_MASK, color_depth));
|
||
|
+
|
||
|
regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_EN,
|
||
|
PLL_PCG_CLK_EN);
|
||
|
|
||
|
@@ -853,9 +1131,146 @@ static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx,
|
||
|
rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_lane_init_seq);
|
||
|
rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_tmds_lane_init_seq);
|
||
|
|
||
|
+ if (hdptx->earc_en)
|
||
|
+ rk_hdptx_earc_config(hdptx);
|
||
|
+
|
||
|
return rk_hdptx_post_enable_lane(hdptx);
|
||
|
}
|
||
|
|
||
|
+static int rk_hdptx_ropll_frl_mode_config(struct rk_hdptx_phy *hdptx,
|
||
|
+ u32 bus_width)
|
||
|
+{
|
||
|
+ u32 bit_rate = bus_width & DATA_RATE_MASK;
|
||
|
+ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0;
|
||
|
+ const struct ropll_config *cfg = NULL;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(ropll_frl_cfg); i++)
|
||
|
+ if (bit_rate == ropll_frl_cfg[i].bit_rate) {
|
||
|
+ cfg = &ropll_frl_cfg[i];
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!cfg) {
|
||
|
+ dev_err(hdptx->dev, "%s cannot find pll cfg\n", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ rk_hdptx_pre_power_up(hdptx);
|
||
|
+
|
||
|
+ reset_control_assert(hdptx->rsts[RST_ROPLL].rstc);
|
||
|
+ usleep_range(10, 20);
|
||
|
+ reset_control_deassert(hdptx->rsts[RST_ROPLL].rstc);
|
||
|
+
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_cmn_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_cmn_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_ropll_cmn_init_seq);
|
||
|
+
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0051), cfg->pms_mdiv);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0055), cfg->pms_mdiv_afc);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0059),
|
||
|
+ (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(005a), cfg->pms_sdiv << 4);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDM_EN_MASK,
|
||
|
+ FIELD_PREP(ROPLL_SDM_EN_MASK, cfg->sdm_en));
|
||
|
+ if (!cfg->sdm_en)
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e), 0xf, 0);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0064), ROPLL_SDM_NUM_SIGN_RBR_MASK,
|
||
|
+ FIELD_PREP(ROPLL_SDM_NUM_SIGN_RBR_MASK, cfg->sdm_num_sign));
|
||
|
+
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0060), cfg->sdm_deno);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0065), cfg->sdm_num);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0069), ROPLL_SDC_N_RBR_MASK,
|
||
|
+ FIELD_PREP(ROPLL_SDC_N_RBR_MASK, cfg->sdc_n));
|
||
|
+
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(006c), cfg->sdc_num);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0070), cfg->sdc_deno);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_POSTDIV_SEL_MASK,
|
||
|
+ FIELD_PREP(PLL_PCG_POSTDIV_SEL_MASK, cfg->pms_sdiv));
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_SEL_MASK,
|
||
|
+ FIELD_PREP(PLL_PCG_CLK_SEL_MASK, color_depth));
|
||
|
+
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_sb_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_lntop_init_seq);
|
||
|
+
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_lane_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_ropll_lane_init_seq);
|
||
|
+
|
||
|
+ if (hdptx->earc_en)
|
||
|
+ rk_hdptx_earc_config(hdptx);
|
||
|
+
|
||
|
+ return rk_hdptx_post_power_up(hdptx);
|
||
|
+}
|
||
|
+
|
||
|
+static int rk_hdptx_lcpll_frl_mode_config(struct rk_hdptx_phy *hdptx,
|
||
|
+ u32 bus_width)
|
||
|
+{
|
||
|
+ u32 bit_rate = bus_width & DATA_RATE_MASK;
|
||
|
+ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0;
|
||
|
+ const struct lcpll_config *cfg = NULL;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(lcpll_cfg); i++)
|
||
|
+ if (bit_rate == lcpll_cfg[i].bit_rate) {
|
||
|
+ cfg = &lcpll_cfg[i];
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!cfg) {
|
||
|
+ dev_err(hdptx->dev, "%s cannot find pll cfg\n", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ rk_hdptx_pre_power_up(hdptx);
|
||
|
+
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_cmn_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_cmn_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_lcpll_cmn_init_seq);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0008),
|
||
|
+ LCPLL_EN_MASK | LCPLL_LCVCO_MODE_EN_MASK,
|
||
|
+ FIELD_PREP(LCPLL_EN_MASK, 1) |
|
||
|
+ FIELD_PREP(LCPLL_LCVCO_MODE_EN_MASK, cfg->lcvco_mode_en));
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(001e),
|
||
|
+ LCPLL_PI_EN_MASK | LCPLL_100M_CLK_EN_MASK,
|
||
|
+ FIELD_PREP(LCPLL_PI_EN_MASK, cfg->pi_en) |
|
||
|
+ FIELD_PREP(LCPLL_100M_CLK_EN_MASK, cfg->clk_en_100m));
|
||
|
+
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0020), cfg->pms_mdiv);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0021), cfg->pms_mdiv_afc);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0022),
|
||
|
+ (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(0023),
|
||
|
+ (cfg->pms_sdiv << 4) | cfg->pms_sdiv);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(002a), cfg->sdm_deno);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(002b), cfg->sdm_num_sign);
|
||
|
+ regmap_write(hdptx->regmap, CMN_REG(002c), cfg->sdm_num);
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(002d), LCPLL_SDC_N_MASK,
|
||
|
+ FIELD_PREP(LCPLL_SDC_N_MASK, cfg->sdc_n));
|
||
|
+
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_POSTDIV_SEL_MASK,
|
||
|
+ FIELD_PREP(PLL_PCG_POSTDIV_SEL_MASK, cfg->pms_sdiv));
|
||
|
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_SEL_MASK,
|
||
|
+ FIELD_PREP(PLL_PCG_CLK_SEL_MASK, color_depth));
|
||
|
+
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_sb_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_lntop_init_seq);
|
||
|
+
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_common_lane_init_seq);
|
||
|
+ rk_hdptx_multi_reg_write(hdptx, rk_hdtpx_frl_lcpll_lane_init_seq);
|
||
|
+
|
||
|
+ if (hdptx->earc_en)
|
||
|
+ rk_hdptx_earc_config(hdptx);
|
||
|
+
|
||
|
+ return rk_hdptx_post_power_up(hdptx);
|
||
|
+}
|
||
|
+
|
||
|
static int rk_hdptx_phy_power_on(struct phy *phy)
|
||
|
{
|
||
|
struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
|
||
|
@@ -865,7 +1280,7 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
|
||
|
* from the HDMI bridge driver until phy_configure_opts_hdmi
|
||
|
* becomes available in the PHY API.
|
||
|
*/
|
||
|
- unsigned int rate = bus_width & 0xfffffff;
|
||
|
+ unsigned int rate = bus_width & DATA_RATE_MASK;
|
||
|
|
||
|
dev_dbg(hdptx->dev, "%s bus_width=%x rate=%u\n",
|
||
|
__func__, bus_width, rate);
|
||
|
@@ -876,7 +1291,20 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
- ret = rk_hdptx_ropll_tmds_mode_config(hdptx, rate);
|
||
|
+ if (bus_width & HDMI_EARC_MASK)
|
||
|
+ hdptx->earc_en = true;
|
||
|
+ else
|
||
|
+ hdptx->earc_en = false;
|
||
|
+
|
||
|
+ if (bus_width & HDMI_MODE_MASK) {
|
||
|
+ if (rate > 24000000)
|
||
|
+ ret = rk_hdptx_lcpll_frl_mode_config(hdptx, bus_width);
|
||
|
+ else
|
||
|
+ ret = rk_hdptx_ropll_frl_mode_config(hdptx, bus_width);
|
||
|
+ } else {
|
||
|
+ ret = rk_hdptx_ropll_tmds_mode_config(hdptx, rate);
|
||
|
+ }
|
||
|
+
|
||
|
if (ret)
|
||
|
pm_runtime_put(hdptx->dev);
|
||
|
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From f3967b5c3262f3a19e84a3246e737ac39ec131e3 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 19 Feb 2024 21:53:24 +0200
|
||
|
Subject: [PATCH 28/54] [WIP] dt-bindings: phy: rockchip,rk3588-hdptx-phy: Add
|
||
|
#clock-cells
|
||
|
|
||
|
The PHY can be used as a clock provider on RK3588, hence add the missing
|
||
|
'#clock-cells' property.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../devicetree/bindings/phy/rockchip,rk3588-hdptx-phy.yaml | 3 +++
|
||
|
1 file changed, 3 insertions(+)
|
||
|
|
||
|
diff --git a/Documentation/devicetree/bindings/phy/rockchip,rk3588-hdptx-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,rk3588-hdptx-phy.yaml
|
||
|
index 54e822c715f3..84fe59dbcf48 100644
|
||
|
--- a/Documentation/devicetree/bindings/phy/rockchip,rk3588-hdptx-phy.yaml
|
||
|
+++ b/Documentation/devicetree/bindings/phy/rockchip,rk3588-hdptx-phy.yaml
|
||
|
@@ -27,6 +27,9 @@ properties:
|
||
|
- const: ref
|
||
|
- const: apb
|
||
|
|
||
|
+ "#clock-cells":
|
||
|
+ const: 0
|
||
|
+
|
||
|
"#phy-cells":
|
||
|
const: 0
|
||
|
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 3f89224fd82432d536350eb5291f906a7cc637e0 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 16 Jan 2024 19:27:40 +0200
|
||
|
Subject: [PATCH 29/54] [WIP] phy: phy-rockchip-samsung-hdptx: Add clock
|
||
|
provider
|
||
|
|
||
|
The HDMI PHY PLL can be used as an alternative dclk source to SoC CRU.
|
||
|
It provides more accurate clock rates required to properly support
|
||
|
various display modes, e.g. those relying on non-integer refresh rates.
|
||
|
|
||
|
Also note this only works for HDMI 2.0 or bellow, e.g. cannot be used to
|
||
|
support HDMI 2.1 4K@120Hz mode.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../phy/rockchip/phy-rockchip-samsung-hdptx.c | 148 +++++++++++++++++-
|
||
|
1 file changed, 143 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
index 44acea3f86af..a3ac4f3835bc 100644
|
||
|
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
@@ -8,6 +8,7 @@
|
||
|
*/
|
||
|
#include <linux/bitfield.h>
|
||
|
#include <linux/clk.h>
|
||
|
+#include <linux/clk-provider.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/mfd/syscon.h>
|
||
|
#include <linux/module.h>
|
||
|
@@ -279,6 +280,12 @@ struct rk_hdptx_phy {
|
||
|
int nr_clks;
|
||
|
struct reset_control_bulk_data rsts[RST_MAX];
|
||
|
bool earc_en;
|
||
|
+
|
||
|
+ /* clk provider */
|
||
|
+ struct clk_hw hw;
|
||
|
+ unsigned long rate;
|
||
|
+ int id;
|
||
|
+ int count;
|
||
|
};
|
||
|
|
||
|
static const struct lcpll_config lcpll_cfg[] = {
|
||
|
@@ -1031,6 +1038,8 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
|
||
|
const struct ropll_config *cfg = NULL;
|
||
|
struct ropll_config rc = {0};
|
||
|
|
||
|
+ hdptx->rate = rate * 100;
|
||
|
+
|
||
|
if (color_depth)
|
||
|
rate = rate * 10 / 8;
|
||
|
|
||
|
@@ -1315,11 +1324,13 @@ static int rk_hdptx_phy_power_off(struct phy *phy)
|
||
|
{
|
||
|
struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
|
||
|
u32 val;
|
||
|
- int ret;
|
||
|
+ int ret = 0;
|
||
|
|
||
|
- ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &val);
|
||
|
- if (ret == 0 && (val & HDPTX_O_PLL_LOCK_DONE))
|
||
|
- rk_hdptx_phy_disable(hdptx);
|
||
|
+ if (hdptx->count == 0) {
|
||
|
+ ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &val);
|
||
|
+ if (ret == 0 && (val & HDPTX_O_PLL_LOCK_DONE))
|
||
|
+ rk_hdptx_phy_disable(hdptx);
|
||
|
+ }
|
||
|
|
||
|
pm_runtime_put(hdptx->dev);
|
||
|
|
||
|
@@ -1332,6 +1343,129 @@ static const struct phy_ops rk_hdptx_phy_ops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
};
|
||
|
|
||
|
+static struct rk_hdptx_phy *to_rk_hdptx_phy(struct clk_hw *hw)
|
||
|
+{
|
||
|
+ return container_of(hw, struct rk_hdptx_phy, hw);
|
||
|
+}
|
||
|
+
|
||
|
+static int rk_hdptx_phy_clk_prepare(struct clk_hw *hw)
|
||
|
+{
|
||
|
+ struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ ret = pm_runtime_resume_and_get(hdptx->dev);
|
||
|
+ if (ret) {
|
||
|
+ dev_err(hdptx->dev, "Failed to resume phy clk: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!hdptx->count && hdptx->rate) {
|
||
|
+ ret = rk_hdptx_ropll_tmds_cmn_config(hdptx, hdptx->rate / 100);
|
||
|
+ if (ret < 0) {
|
||
|
+ dev_err(hdptx->dev, "Failed to init PHY PLL: %d\n", ret);
|
||
|
+ pm_runtime_put(hdptx->dev);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ hdptx->count++;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void rk_hdptx_phy_clk_unprepare(struct clk_hw *hw)
|
||
|
+{
|
||
|
+ struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
|
||
|
+
|
||
|
+ if (hdptx->count == 1) {
|
||
|
+ u32 val;
|
||
|
+ int ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &val);
|
||
|
+ if (ret == 0 && (val & HDPTX_O_PLL_LOCK_DONE))
|
||
|
+ rk_hdptx_phy_disable(hdptx);
|
||
|
+ }
|
||
|
+
|
||
|
+ hdptx->count--;
|
||
|
+ pm_runtime_put(hdptx->dev);
|
||
|
+}
|
||
|
+
|
||
|
+static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
|
||
|
+ unsigned long parent_rate)
|
||
|
+{
|
||
|
+ struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
|
||
|
+
|
||
|
+ return hdptx->rate;
|
||
|
+}
|
||
|
+
|
||
|
+static long rk_hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||
|
+ unsigned long *parent_rate)
|
||
|
+{
|
||
|
+ const struct ropll_config *cfg = NULL;
|
||
|
+ u32 bit_rate = rate / 100;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (rate > HDMI20_MAX_RATE)
|
||
|
+ return rate;
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(ropll_tmds_cfg); i++)
|
||
|
+ if (bit_rate == ropll_tmds_cfg[i].bit_rate) {
|
||
|
+ cfg = &ropll_tmds_cfg[i];
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!cfg && !rk_hdptx_phy_clk_pll_calc(bit_rate, NULL))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ return rate;
|
||
|
+}
|
||
|
+
|
||
|
+static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||
|
+ unsigned long parent_rate)
|
||
|
+{
|
||
|
+ struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
|
||
|
+ u32 val;
|
||
|
+ int ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &val);
|
||
|
+ if (ret == 0 && (val & HDPTX_O_PLL_LOCK_DONE))
|
||
|
+ rk_hdptx_phy_disable(hdptx);
|
||
|
+
|
||
|
+ return rk_hdptx_ropll_tmds_cmn_config(hdptx, rate / 100);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct clk_ops hdptx_phy_clk_ops = {
|
||
|
+ .prepare = rk_hdptx_phy_clk_prepare,
|
||
|
+ .unprepare = rk_hdptx_phy_clk_unprepare,
|
||
|
+ .recalc_rate = rk_hdptx_phy_clk_recalc_rate,
|
||
|
+ .round_rate = rk_hdptx_phy_clk_round_rate,
|
||
|
+ .set_rate = rk_hdptx_phy_clk_set_rate,
|
||
|
+};
|
||
|
+
|
||
|
+static int rk_hdptx_phy_clk_register(struct rk_hdptx_phy *hdptx)
|
||
|
+{
|
||
|
+ struct device *dev = hdptx->dev;
|
||
|
+ const char *name, *pname;
|
||
|
+ struct clk *refclk;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ refclk = devm_clk_get(dev, "ref");
|
||
|
+ if (IS_ERR(refclk))
|
||
|
+ return dev_err_probe(dev, PTR_ERR(refclk),
|
||
|
+ "Failed to get ref clock\n");
|
||
|
+
|
||
|
+ pname = __clk_get_name(refclk);
|
||
|
+ name = hdptx->id ? "clk_hdmiphy_pixel1" : "clk_hdmiphy_pixel0";
|
||
|
+ hdptx->hw.init = CLK_HW_INIT(name, pname, &hdptx_phy_clk_ops,
|
||
|
+ CLK_GET_RATE_NOCACHE);
|
||
|
+
|
||
|
+ ret = devm_clk_hw_register(dev, &hdptx->hw);
|
||
|
+ if (ret)
|
||
|
+ return dev_err_probe(dev, ret, "Failed to register clock\n");
|
||
|
+
|
||
|
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &hdptx->hw);
|
||
|
+ if (ret)
|
||
|
+ return dev_err_probe(dev, ret,
|
||
|
+ "Failed to register clk provider\n");
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int rk_hdptx_phy_runtime_suspend(struct device *dev)
|
||
|
{
|
||
|
struct rk_hdptx_phy *hdptx = dev_get_drvdata(dev);
|
||
|
@@ -1367,6 +1501,10 @@ static int rk_hdptx_phy_probe(struct platform_device *pdev)
|
||
|
|
||
|
hdptx->dev = dev;
|
||
|
|
||
|
+ hdptx->id = of_alias_get_id(dev->of_node, "hdptxphy");
|
||
|
+ if (hdptx->id < 0)
|
||
|
+ hdptx->id = 0;
|
||
|
+
|
||
|
regs = devm_platform_ioremap_resource(pdev, 0);
|
||
|
if (IS_ERR(regs))
|
||
|
return dev_err_probe(dev, PTR_ERR(regs),
|
||
|
@@ -1426,7 +1564,7 @@ static int rk_hdptx_phy_probe(struct platform_device *pdev)
|
||
|
reset_control_deassert(hdptx->rsts[RST_CMN].rstc);
|
||
|
reset_control_deassert(hdptx->rsts[RST_INIT].rstc);
|
||
|
|
||
|
- return 0;
|
||
|
+ return rk_hdptx_phy_clk_register(hdptx);
|
||
|
}
|
||
|
|
||
|
static const struct dev_pm_ops rk_hdptx_phy_pm_ops = {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From c7dbc888875e205bbfbf81d8174b6412642c429f Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Wed, 27 Mar 2024 20:36:15 +0200
|
||
|
Subject: [PATCH 30/54] [WIP] dt-bindings: display: rockchip-drm: Add optional
|
||
|
clocks property
|
||
|
|
||
|
Allow using the clock provided by HDMI0 PHY PLL to improve HDMI output
|
||
|
support on RK3588 SoC.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../bindings/display/rockchip/rockchip-drm.yaml | 8 ++++++++
|
||
|
1 file changed, 8 insertions(+)
|
||
|
|
||
|
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-drm.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-drm.yaml
|
||
|
index a8d18a37cb23..9d000760dd6e 100644
|
||
|
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip-drm.yaml
|
||
|
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-drm.yaml
|
||
|
@@ -28,6 +28,14 @@ properties:
|
||
|
of vop devices. vop definitions as defined in
|
||
|
Documentation/devicetree/bindings/display/rockchip/rockchip-vop.yaml
|
||
|
|
||
|
+ clocks:
|
||
|
+ maxItems: 1
|
||
|
+ description: Optional clock provided by HDMI0 PLL
|
||
|
+
|
||
|
+ clock-names:
|
||
|
+ items:
|
||
|
+ - const: hdmi0_phy_pll
|
||
|
+
|
||
|
required:
|
||
|
- compatible
|
||
|
- ports
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 05c5f1c19d33c6c5404a68adb6d264387133d352 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Fri, 3 Nov 2023 19:58:02 +0200
|
||
|
Subject: [PATCH 31/54] [WIP] drm/rockchip: vop2: Improve display modes
|
||
|
handling on rk3588
|
||
|
|
||
|
The initial vop2 support for rk3588 in mainline is not able to handle
|
||
|
all display modes supported by connected displays, e.g.
|
||
|
2560x1440-75.00Hz, 2048x1152-60.00Hz, 1024x768-60.00Hz.
|
||
|
|
||
|
Additionally, it doesn't cope with non-integer refresh rates like 59.94,
|
||
|
29.97, 23.98, etc.
|
||
|
|
||
|
Improve HDMI0 clocking in order to support the additional display modes.
|
||
|
|
||
|
Fixes: 5a028e8f062f ("drm/rockchip: vop2: Add support for rk3588")
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 553 ++++++++++++++++++-
|
||
|
1 file changed, 552 insertions(+), 1 deletion(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
||
|
index 62ebbdb16253..86d2c15af39f 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
||
|
@@ -5,6 +5,8 @@
|
||
|
*/
|
||
|
#include <linux/bitfield.h>
|
||
|
#include <linux/clk.h>
|
||
|
+#include <linux/clk-provider.h>
|
||
|
+#include <linux/clkdev.h>
|
||
|
#include <linux/component.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/iopoll.h>
|
||
|
@@ -212,6 +214,10 @@ struct vop2 {
|
||
|
struct clk *hclk;
|
||
|
struct clk *aclk;
|
||
|
struct clk *pclk;
|
||
|
+ // [CC:] hack to support additional display modes
|
||
|
+ struct clk *hdmi0_phy_pll;
|
||
|
+ /* list_head of internal clk */
|
||
|
+ struct list_head clk_list_head;
|
||
|
|
||
|
/* optional internal rgb encoder */
|
||
|
struct rockchip_rgb *rgb;
|
||
|
@@ -220,6 +226,19 @@ struct vop2 {
|
||
|
struct vop2_win win[];
|
||
|
};
|
||
|
|
||
|
+struct vop2_clk {
|
||
|
+ struct vop2 *vop2;
|
||
|
+ struct list_head list;
|
||
|
+ unsigned long rate;
|
||
|
+ struct clk_hw hw;
|
||
|
+ struct clk_divider div;
|
||
|
+ int div_val;
|
||
|
+ u8 parent_index;
|
||
|
+};
|
||
|
+
|
||
|
+#define to_vop2_clk(_hw) container_of(_hw, struct vop2_clk, hw)
|
||
|
+#define VOP2_MAX_DCLK_RATE 600000 /* kHz */
|
||
|
+
|
||
|
#define vop2_output_if_is_hdmi(x) ((x) == ROCKCHIP_VOP2_EP_HDMI0 || \
|
||
|
(x) == ROCKCHIP_VOP2_EP_HDMI1)
|
||
|
|
||
|
@@ -1476,9 +1495,30 @@ static bool vop2_crtc_mode_fixup(struct drm_crtc *crtc,
|
||
|
const struct drm_display_mode *mode,
|
||
|
struct drm_display_mode *adj_mode)
|
||
|
{
|
||
|
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
|
||
|
+ struct drm_connector *connector;
|
||
|
+ struct drm_connector_list_iter conn_iter;
|
||
|
+ struct drm_crtc_state *new_crtc_state = container_of(mode, struct drm_crtc_state, mode);
|
||
|
drm_mode_set_crtcinfo(adj_mode, CRTC_INTERLACE_HALVE_V |
|
||
|
CRTC_STEREO_DOUBLE);
|
||
|
|
||
|
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||
|
+ adj_mode->crtc_clock *= 2;
|
||
|
+
|
||
|
+ drm_connector_list_iter_begin(crtc->dev, &conn_iter);
|
||
|
+ drm_for_each_connector_iter(connector, &conn_iter) {
|
||
|
+ if ((new_crtc_state->connector_mask & drm_connector_mask(connector)) &&
|
||
|
+ ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
|
||
|
+ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA))) {
|
||
|
+ drm_connector_list_iter_end(&conn_iter);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ drm_connector_list_iter_end(&conn_iter);
|
||
|
+
|
||
|
+ if (adj_mode->crtc_clock <= VOP2_MAX_DCLK_RATE)
|
||
|
+ adj_mode->crtc_clock = DIV_ROUND_UP(clk_round_rate(vp->dclk,
|
||
|
+ adj_mode->crtc_clock * 1000), 1000);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
@@ -1663,6 +1703,31 @@ static unsigned long rk3588_calc_dclk(unsigned long child_clk, unsigned long max
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name);
|
||
|
+
|
||
|
+static int vop2_cru_set_rate(struct vop2_clk *if_pixclk, struct vop2_clk *if_dclk)
|
||
|
+{
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+ if (if_pixclk) {
|
||
|
+ ret = clk_set_rate(if_pixclk->hw.clk, if_pixclk->rate);
|
||
|
+ if (ret < 0) {
|
||
|
+ DRM_DEV_ERROR(if_pixclk->vop2->dev, "set %s to %ld failed: %d\n",
|
||
|
+ clk_hw_get_name(&if_pixclk->hw), if_pixclk->rate, ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (if_dclk) {
|
||
|
+ ret = clk_set_rate(if_dclk->hw.clk, if_dclk->rate);
|
||
|
+ if (ret < 0)
|
||
|
+ DRM_DEV_ERROR(if_dclk->vop2->dev, "set %s to %ld failed %d\n",
|
||
|
+ clk_hw_get_name(&if_dclk->hw), if_dclk->rate, ret);
|
||
|
+ }
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
/*
|
||
|
* 4 pixclk/cycle on rk3588
|
||
|
* RGB/eDP/HDMI: if_pixclk >= dclk_core
|
||
|
@@ -1686,6 +1751,72 @@ static unsigned long rk3588_calc_cru_cfg(struct vop2_video_port *vp, int id,
|
||
|
int K = 1;
|
||
|
|
||
|
if (vop2_output_if_is_hdmi(id)) {
|
||
|
+ if (vop2->data->soc_id == 3588 && id == ROCKCHIP_VOP2_EP_HDMI0 &&
|
||
|
+ vop2->hdmi0_phy_pll) {
|
||
|
+ const char *clk_src_name = "hdmi_edp0_clk_src";
|
||
|
+ const char *clk_parent_name = "dclk";
|
||
|
+ const char *pixclk_name = "hdmi_edp0_pixclk";
|
||
|
+ const char *dclk_name = "hdmi_edp0_dclk";
|
||
|
+ struct vop2_clk *if_clk_src, *if_clk_parent, *if_pixclk, *if_dclk, *dclk, *dclk_core, *dclk_out;
|
||
|
+ char clk_name[32];
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ if_clk_src = vop2_clk_get(vop2, clk_src_name);
|
||
|
+ snprintf(clk_name, sizeof(clk_name), "%s%d", clk_parent_name, vp->id);
|
||
|
+ if_clk_parent = vop2_clk_get(vop2, clk_name);
|
||
|
+ if_pixclk = vop2_clk_get(vop2, pixclk_name);
|
||
|
+ if_dclk = vop2_clk_get(vop2, dclk_name);
|
||
|
+ if (!if_pixclk || !if_clk_parent) {
|
||
|
+ DRM_DEV_ERROR(vop2->dev, "failed to get connector interface clk\n");
|
||
|
+ return -ENODEV;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = clk_set_parent(if_clk_src->hw.clk, if_clk_parent->hw.clk);
|
||
|
+ if (ret < 0) {
|
||
|
+ DRM_DEV_ERROR(vop2->dev, "failed to set parent(%s) for %s: %d\n",
|
||
|
+ __clk_get_name(if_clk_parent->hw.clk),
|
||
|
+ __clk_get_name(if_clk_src->hw.clk), ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (output_mode == ROCKCHIP_OUT_MODE_YUV420)
|
||
|
+ K = 2;
|
||
|
+
|
||
|
+ if_pixclk->rate = (dclk_core_rate << 1) / K;
|
||
|
+ if_dclk->rate = dclk_core_rate / K;
|
||
|
+
|
||
|
+ snprintf(clk_name, sizeof(clk_name), "dclk_core%d", vp->id);
|
||
|
+ dclk_core = vop2_clk_get(vop2, clk_name);
|
||
|
+
|
||
|
+ snprintf(clk_name, sizeof(clk_name), "dclk_out%d", vp->id);
|
||
|
+ dclk_out = vop2_clk_get(vop2, clk_name);
|
||
|
+
|
||
|
+ snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id);
|
||
|
+ dclk = vop2_clk_get(vop2, clk_name);
|
||
|
+ if (v_pixclk <= (VOP2_MAX_DCLK_RATE * 1000)) {
|
||
|
+ if (output_mode == ROCKCHIP_OUT_MODE_YUV420)
|
||
|
+ v_pixclk = v_pixclk >> 1;
|
||
|
+ } else {
|
||
|
+ v_pixclk = v_pixclk >> 2;
|
||
|
+ }
|
||
|
+ clk_set_rate(dclk->hw.clk, v_pixclk);
|
||
|
+
|
||
|
+ if (dclk_core_rate > if_pixclk->rate) {
|
||
|
+ clk_set_rate(dclk_core->hw.clk, dclk_core_rate);
|
||
|
+ ret = vop2_cru_set_rate(if_pixclk, if_dclk);
|
||
|
+ } else {
|
||
|
+ ret = vop2_cru_set_rate(if_pixclk, if_dclk);
|
||
|
+ clk_set_rate(dclk_core->hw.clk, dclk_core_rate);
|
||
|
+ }
|
||
|
+
|
||
|
+ *dclk_core_div = dclk_core->div_val;
|
||
|
+ *dclk_out_div = dclk_out->div_val;
|
||
|
+ *if_pixclk_div = if_pixclk->div_val;
|
||
|
+ *if_dclk_div = if_dclk->div_val;
|
||
|
+
|
||
|
+ return dclk->rate;
|
||
|
+ }
|
||
|
+
|
||
|
/*
|
||
|
* K = 2: dclk_core = if_pixclk_rate > if_dclk_rate
|
||
|
* K = 1: dclk_core = hdmie_edp_dclk > if_pixclk_rate
|
||
|
@@ -1917,6 +2048,22 @@ static int us_to_vertical_line(struct drm_display_mode *mode, int us)
|
||
|
return us * mode->clock / mode->htotal / 1000;
|
||
|
}
|
||
|
|
||
|
+// [CC:] rework virtual clock
|
||
|
+static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name)
|
||
|
+{
|
||
|
+ struct vop2_clk *clk, *n;
|
||
|
+
|
||
|
+ if (!name)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) {
|
||
|
+ if (!strcmp(clk_hw_get_name(&clk->hw), name))
|
||
|
+ return clk;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
||
|
struct drm_atomic_state *state)
|
||
|
{
|
||
|
@@ -1944,6 +2091,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
||
|
u32 val, polflags;
|
||
|
int ret;
|
||
|
struct drm_encoder *encoder;
|
||
|
+ char clk_name[32];
|
||
|
+ struct vop2_clk *dclk;
|
||
|
|
||
|
drm_dbg(vop2->drm, "Update mode to %dx%d%s%d, type: %d for vp%d\n",
|
||
|
hdisplay, vdisplay, mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "p",
|
||
|
@@ -2044,11 +2193,38 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
||
|
|
||
|
if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
|
||
|
dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
|
||
|
- clock *= 2;
|
||
|
+ // [CC:] done via mode_fixup
|
||
|
+ // clock *= 2;
|
||
|
}
|
||
|
|
||
|
vop2_vp_write(vp, RK3568_VP_MIPI_CTRL, 0);
|
||
|
|
||
|
+ snprintf(clk_name, sizeof(clk_name), "dclk%d", vp->id);
|
||
|
+ dclk = vop2_clk_get(vop2, clk_name);
|
||
|
+ if (dclk) {
|
||
|
+ /*
|
||
|
+ * use HDMI_PHY_PLL as dclk source under 4K@60 if it is available,
|
||
|
+ * otherwise use system cru as dclk source.
|
||
|
+ */
|
||
|
+ drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
|
||
|
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||
|
+
|
||
|
+ // [CC:] Using PHY PLL to handle all display modes
|
||
|
+ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
|
||
|
+ clk_get_rate(vop2->hdmi0_phy_pll);
|
||
|
+
|
||
|
+ if (mode->crtc_clock <= VOP2_MAX_DCLK_RATE) {
|
||
|
+ ret = clk_set_parent(vp->dclk, vop2->hdmi0_phy_pll);
|
||
|
+ if (ret < 0)
|
||
|
+ DRM_WARN("failed to set clock parent for %s\n",
|
||
|
+ __clk_get_name(vp->dclk));
|
||
|
+ }
|
||
|
+
|
||
|
+ clock = dclk->rate;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
clk_set_rate(vp->dclk, clock);
|
||
|
|
||
|
vop2_post_config(crtc);
|
||
|
@@ -2504,7 +2680,43 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
|
||
|
spin_unlock_irq(&crtc->dev->event_lock);
|
||
|
}
|
||
|
|
||
|
+static enum drm_mode_status
|
||
|
+vop2_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
|
||
|
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
|
||
|
+ struct vop2 *vop2 = vp->vop2;
|
||
|
+ const struct vop2_data *vop2_data = vop2->data;
|
||
|
+ const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id];
|
||
|
+ int request_clock = mode->clock;
|
||
|
+ int clock;
|
||
|
+
|
||
|
+ if (mode->hdisplay > vp_data->max_output.width)
|
||
|
+ return MODE_BAD_HVALUE;
|
||
|
+
|
||
|
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||
|
+ request_clock *= 2;
|
||
|
+
|
||
|
+ if (request_clock <= VOP2_MAX_DCLK_RATE) {
|
||
|
+ clock = request_clock;
|
||
|
+ } else {
|
||
|
+ request_clock = request_clock >> 2;
|
||
|
+ clock = clk_round_rate(vp->dclk, request_clock * 1000) / 1000;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Hdmi or DisplayPort request a Accurate clock.
|
||
|
+ */
|
||
|
+ if (vcstate->output_type == DRM_MODE_CONNECTOR_HDMIA ||
|
||
|
+ vcstate->output_type == DRM_MODE_CONNECTOR_DisplayPort)
|
||
|
+ if (clock != request_clock)
|
||
|
+ return MODE_CLOCK_RANGE;
|
||
|
+
|
||
|
+ return MODE_OK;
|
||
|
+}
|
||
|
+
|
||
|
static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = {
|
||
|
+ .mode_valid = vop2_crtc_mode_valid,
|
||
|
.mode_fixup = vop2_crtc_mode_fixup,
|
||
|
.atomic_check = vop2_crtc_atomic_check,
|
||
|
.atomic_begin = vop2_crtc_atomic_begin,
|
||
|
@@ -3074,6 +3286,336 @@ static const struct regmap_config vop2_regmap_config = {
|
||
|
.cache_type = REGCACHE_MAPLE,
|
||
|
};
|
||
|
|
||
|
+/*
|
||
|
+ * BEGIN virtual clock
|
||
|
+ */
|
||
|
+#define PLL_RATE_MIN 30000000
|
||
|
+
|
||
|
+#define cru_dbg(format, ...) do { \
|
||
|
+ if (cru_debug) \
|
||
|
+ pr_info("%s: " format, __func__, ## __VA_ARGS__); \
|
||
|
+ } while (0)
|
||
|
+
|
||
|
+#define PNAME(x) static const char *const x[]
|
||
|
+
|
||
|
+enum vop_clk_branch_type {
|
||
|
+ branch_mux,
|
||
|
+ branch_divider,
|
||
|
+ branch_factor,
|
||
|
+ branch_virtual,
|
||
|
+};
|
||
|
+
|
||
|
+#define VIR(cname) \
|
||
|
+ { \
|
||
|
+ .branch_type = branch_virtual, \
|
||
|
+ .name = cname, \
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+#define MUX(cname, pnames, f) \
|
||
|
+ { \
|
||
|
+ .branch_type = branch_mux, \
|
||
|
+ .name = cname, \
|
||
|
+ .parent_names = pnames, \
|
||
|
+ .num_parents = ARRAY_SIZE(pnames), \
|
||
|
+ .flags = f, \
|
||
|
+ }
|
||
|
+
|
||
|
+#define FACTOR(cname, pname, f) \
|
||
|
+ { \
|
||
|
+ .branch_type = branch_factor, \
|
||
|
+ .name = cname, \
|
||
|
+ .parent_names = (const char *[]){ pname }, \
|
||
|
+ .num_parents = 1, \
|
||
|
+ .flags = f, \
|
||
|
+ }
|
||
|
+
|
||
|
+#define DIV(cname, pname, f, w) \
|
||
|
+ { \
|
||
|
+ .branch_type = branch_divider, \
|
||
|
+ .name = cname, \
|
||
|
+ .parent_names = (const char *[]){ pname }, \
|
||
|
+ .num_parents = 1, \
|
||
|
+ .flags = f, \
|
||
|
+ .div_width = w, \
|
||
|
+ }
|
||
|
+
|
||
|
+struct vop2_clk_branch {
|
||
|
+ enum vop_clk_branch_type branch_type;
|
||
|
+ const char *name;
|
||
|
+ const char *const *parent_names;
|
||
|
+ u8 num_parents;
|
||
|
+ unsigned long flags;
|
||
|
+ u8 div_shift;
|
||
|
+ u8 div_width;
|
||
|
+ u8 div_flags;
|
||
|
+};
|
||
|
+
|
||
|
+PNAME(mux_port0_dclk_src_p) = { "dclk0", "dclk1" };
|
||
|
+PNAME(mux_port2_dclk_src_p) = { "dclk2", "dclk1" };
|
||
|
+PNAME(mux_dp_pixclk_p) = { "dclk_out0", "dclk_out1", "dclk_out2" };
|
||
|
+PNAME(mux_hdmi_edp_clk_src_p) = { "dclk0", "dclk1", "dclk2" };
|
||
|
+PNAME(mux_mipi_clk_src_p) = { "dclk_out1", "dclk_out2", "dclk_out3" };
|
||
|
+PNAME(mux_dsc_8k_clk_src_p) = { "dclk0", "dclk1", "dclk2", "dclk3" };
|
||
|
+PNAME(mux_dsc_4k_clk_src_p) = { "dclk0", "dclk1", "dclk2", "dclk3" };
|
||
|
+
|
||
|
+/*
|
||
|
+ * We only use this clk driver calculate the div
|
||
|
+ * of dclk_core/dclk_out/if_pixclk/if_dclk and
|
||
|
+ * the rate of the dclk from the soc.
|
||
|
+ *
|
||
|
+ * We don't touch the cru in the vop here, as
|
||
|
+ * these registers has special read andy write
|
||
|
+ * limits.
|
||
|
+ */
|
||
|
+static struct vop2_clk_branch rk3588_vop_clk_branches[] = {
|
||
|
+ VIR("dclk0"),
|
||
|
+ VIR("dclk1"),
|
||
|
+ VIR("dclk2"),
|
||
|
+ VIR("dclk3"),
|
||
|
+
|
||
|
+ MUX("port0_dclk_src", mux_port0_dclk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("dclk_core0", "port0_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+ DIV("dclk_out0", "port0_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+
|
||
|
+ FACTOR("port1_dclk_src", "dclk1", CLK_SET_RATE_PARENT),
|
||
|
+ DIV("dclk_core1", "port1_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+ DIV("dclk_out1", "port1_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+
|
||
|
+ MUX("port2_dclk_src", mux_port2_dclk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("dclk_core2", "port2_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+ DIV("dclk_out2", "port2_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+
|
||
|
+ FACTOR("port3_dclk_src", "dclk3", CLK_SET_RATE_PARENT),
|
||
|
+ DIV("dclk_core3", "port3_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+ DIV("dclk_out3", "port3_dclk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+
|
||
|
+ MUX("dp0_pixclk", mux_dp_pixclk_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ MUX("dp1_pixclk", mux_dp_pixclk_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+
|
||
|
+ MUX("hdmi_edp0_clk_src", mux_hdmi_edp_clk_src_p,
|
||
|
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("hdmi_edp0_dclk", "hdmi_edp0_clk_src", 0, 2),
|
||
|
+ DIV("hdmi_edp0_pixclk", "hdmi_edp0_clk_src", CLK_SET_RATE_PARENT, 1),
|
||
|
+
|
||
|
+ MUX("hdmi_edp1_clk_src", mux_hdmi_edp_clk_src_p,
|
||
|
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("hdmi_edp1_dclk", "hdmi_edp1_clk_src", 0, 2),
|
||
|
+ DIV("hdmi_edp1_pixclk", "hdmi_edp1_clk_src", CLK_SET_RATE_PARENT, 1),
|
||
|
+
|
||
|
+ MUX("mipi0_clk_src", mux_mipi_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("mipi0_pixclk", "mipi0_clk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+
|
||
|
+ MUX("mipi1_clk_src", mux_mipi_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("mipi1_pixclk", "mipi1_clk_src", CLK_SET_RATE_PARENT, 2),
|
||
|
+
|
||
|
+ FACTOR("rgb_pixclk", "port3_dclk_src", CLK_SET_RATE_PARENT),
|
||
|
+
|
||
|
+ MUX("dsc_8k_txp_clk_src", mux_dsc_8k_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("dsc_8k_txp_clk", "dsc_8k_txp_clk_src", 0, 2),
|
||
|
+ DIV("dsc_8k_pxl_clk", "dsc_8k_txp_clk_src", 0, 2),
|
||
|
+ DIV("dsc_8k_cds_clk", "dsc_8k_txp_clk_src", 0, 2),
|
||
|
+
|
||
|
+ MUX("dsc_4k_txp_clk_src", mux_dsc_4k_clk_src_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT),
|
||
|
+ DIV("dsc_4k_txp_clk", "dsc_4k_txp_clk_src", 0, 2),
|
||
|
+ DIV("dsc_4k_pxl_clk", "dsc_4k_txp_clk_src", 0, 2),
|
||
|
+ DIV("dsc_4k_cds_clk", "dsc_4k_txp_clk_src", 0, 2),
|
||
|
+};
|
||
|
+
|
||
|
+static unsigned long clk_virtual_recalc_rate(struct clk_hw *hw,
|
||
|
+ unsigned long parent_rate)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+
|
||
|
+ return (unsigned long)vop2_clk->rate;
|
||
|
+}
|
||
|
+
|
||
|
+static long clk_virtual_round_rate(struct clk_hw *hw, unsigned long rate,
|
||
|
+ unsigned long *prate)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+
|
||
|
+ vop2_clk->rate = rate;
|
||
|
+
|
||
|
+ return rate;
|
||
|
+}
|
||
|
+
|
||
|
+static int clk_virtual_set_rate(struct clk_hw *hw, unsigned long rate,
|
||
|
+ unsigned long parent_rate)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+const struct clk_ops clk_virtual_ops = {
|
||
|
+ .round_rate = clk_virtual_round_rate,
|
||
|
+ .set_rate = clk_virtual_set_rate,
|
||
|
+ .recalc_rate = clk_virtual_recalc_rate,
|
||
|
+};
|
||
|
+
|
||
|
+static u8 vop2_mux_get_parent(struct clk_hw *hw)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+
|
||
|
+ // cru_dbg("%s index: %d\n", clk_hw_get_name(hw), vop2_clk->parent_index);
|
||
|
+ return vop2_clk->parent_index;
|
||
|
+}
|
||
|
+
|
||
|
+static int vop2_mux_set_parent(struct clk_hw *hw, u8 index)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+
|
||
|
+ vop2_clk->parent_index = index;
|
||
|
+
|
||
|
+ // cru_dbg("%s index: %d\n", clk_hw_get_name(hw), index);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int vop2_clk_mux_determine_rate(struct clk_hw *hw,
|
||
|
+ struct clk_rate_request *req)
|
||
|
+{
|
||
|
+ // cru_dbg("%s %ld(min: %ld max: %ld)\n",
|
||
|
+ // clk_hw_get_name(hw), req->rate, req->min_rate, req->max_rate);
|
||
|
+ return __clk_mux_determine_rate(hw, req);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct clk_ops vop2_mux_clk_ops = {
|
||
|
+ .get_parent = vop2_mux_get_parent,
|
||
|
+ .set_parent = vop2_mux_set_parent,
|
||
|
+ .determine_rate = vop2_clk_mux_determine_rate,
|
||
|
+};
|
||
|
+
|
||
|
+#define div_mask(width) ((1 << (width)) - 1)
|
||
|
+
|
||
|
+static int vop2_div_get_val(unsigned long rate, unsigned long parent_rate)
|
||
|
+{
|
||
|
+ unsigned int div, value;
|
||
|
+
|
||
|
+ div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||
|
+
|
||
|
+ value = ilog2(div);
|
||
|
+
|
||
|
+ return value;
|
||
|
+}
|
||
|
+
|
||
|
+static unsigned long vop2_clk_div_recalc_rate(struct clk_hw *hw,
|
||
|
+ unsigned long parent_rate)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+ unsigned long rate;
|
||
|
+ unsigned int div;
|
||
|
+
|
||
|
+ div = 1 << vop2_clk->div_val;
|
||
|
+ rate = parent_rate / div;
|
||
|
+
|
||
|
+ // cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, parent_rate);
|
||
|
+ return rate;
|
||
|
+}
|
||
|
+
|
||
|
+static long vop2_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||
|
+ unsigned long *prate)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+
|
||
|
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
|
||
|
+ if (*prate < rate)
|
||
|
+ *prate = rate;
|
||
|
+ if ((*prate >> vop2_clk->div.width) > rate)
|
||
|
+ *prate = rate;
|
||
|
+
|
||
|
+ if ((*prate % rate))
|
||
|
+ *prate = rate;
|
||
|
+
|
||
|
+ /* SOC PLL can't output a too low pll freq */
|
||
|
+ if (*prate < PLL_RATE_MIN)
|
||
|
+ *prate = rate << vop2_clk->div.width;
|
||
|
+ }
|
||
|
+
|
||
|
+ // cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, *prate);
|
||
|
+ return rate;
|
||
|
+}
|
||
|
+
|
||
|
+static int vop2_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
|
||
|
+{
|
||
|
+ struct vop2_clk *vop2_clk = to_vop2_clk(hw);
|
||
|
+ int div_val;
|
||
|
+
|
||
|
+ div_val = vop2_div_get_val(rate, parent_rate);
|
||
|
+ vop2_clk->div_val = div_val;
|
||
|
+
|
||
|
+ // cru_dbg("%s prate: %ld rate: %ld div_val: %d\n",
|
||
|
+ // clk_hw_get_name(hw), parent_rate, rate, div_val);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct clk_ops vop2_div_clk_ops = {
|
||
|
+ .recalc_rate = vop2_clk_div_recalc_rate,
|
||
|
+ .round_rate = vop2_clk_div_round_rate,
|
||
|
+ .set_rate = vop2_clk_div_set_rate,
|
||
|
+};
|
||
|
+
|
||
|
+static struct clk *vop2_clk_register(struct vop2 *vop2, struct vop2_clk_branch *branch)
|
||
|
+{
|
||
|
+ struct clk_init_data init = {};
|
||
|
+ struct vop2_clk *vop2_clk;
|
||
|
+ struct clk *clk;
|
||
|
+
|
||
|
+ vop2_clk = devm_kzalloc(vop2->dev, sizeof(*vop2_clk), GFP_KERNEL);
|
||
|
+ if (!vop2_clk)
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+
|
||
|
+ vop2_clk->vop2 = vop2;
|
||
|
+ vop2_clk->hw.init = &init;
|
||
|
+ vop2_clk->div.shift = branch->div_shift;
|
||
|
+ vop2_clk->div.width = branch->div_width;
|
||
|
+
|
||
|
+ init.name = branch->name;
|
||
|
+ init.flags = branch->flags;
|
||
|
+ init.num_parents = branch->num_parents;
|
||
|
+ init.parent_names = branch->parent_names;
|
||
|
+ if (branch->branch_type == branch_divider) {
|
||
|
+ init.ops = &vop2_div_clk_ops;
|
||
|
+ } else if (branch->branch_type == branch_virtual) {
|
||
|
+ init.ops = &clk_virtual_ops;
|
||
|
+ init.num_parents = 0;
|
||
|
+ init.parent_names = NULL;
|
||
|
+ } else {
|
||
|
+ init.ops = &vop2_mux_clk_ops;
|
||
|
+ }
|
||
|
+
|
||
|
+ clk = devm_clk_register(vop2->dev, &vop2_clk->hw);
|
||
|
+ if (!IS_ERR(clk))
|
||
|
+ list_add_tail(&vop2_clk->list, &vop2->clk_list_head);
|
||
|
+ else
|
||
|
+ DRM_DEV_ERROR(vop2->dev, "Register %s failed\n", branch->name);
|
||
|
+
|
||
|
+ return clk;
|
||
|
+}
|
||
|
+
|
||
|
+static int vop2_clk_init(struct vop2 *vop2)
|
||
|
+{
|
||
|
+ struct vop2_clk_branch *branch = rk3588_vop_clk_branches;
|
||
|
+ unsigned int nr_clk = ARRAY_SIZE(rk3588_vop_clk_branches);
|
||
|
+ unsigned int idx;
|
||
|
+ struct vop2_clk *clk, *n;
|
||
|
+
|
||
|
+ INIT_LIST_HEAD(&vop2->clk_list_head);
|
||
|
+
|
||
|
+ if (vop2->data->soc_id < 3588 || vop2->hdmi0_phy_pll == NULL)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) {
|
||
|
+ list_del(&clk->list);
|
||
|
+ }
|
||
|
+
|
||
|
+ for (idx = 0; idx < nr_clk; idx++, branch++)
|
||
|
+ vop2_clk_register(vop2, branch);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+/*
|
||
|
+ * END virtual clock
|
||
|
+ */
|
||
|
+
|
||
|
static int vop2_bind(struct device *dev, struct device *master, void *data)
|
||
|
{
|
||
|
struct platform_device *pdev = to_platform_device(dev);
|
||
|
@@ -3167,6 +3709,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
|
||
|
return PTR_ERR(vop2->pclk);
|
||
|
}
|
||
|
|
||
|
+ vop2->hdmi0_phy_pll = devm_clk_get_optional(vop2->drm->dev, "hdmi0_phy_pll");
|
||
|
+ if (IS_ERR(vop2->hdmi0_phy_pll)) {
|
||
|
+ DRM_DEV_ERROR(vop2->dev, "failed to get hdmi0_phy_pll source\n");
|
||
|
+ return PTR_ERR(vop2->hdmi0_phy_pll);
|
||
|
+ }
|
||
|
+
|
||
|
vop2->irq = platform_get_irq(pdev, 0);
|
||
|
if (vop2->irq < 0) {
|
||
|
drm_err(vop2->drm, "cannot find irq for vop2\n");
|
||
|
@@ -3183,6 +3731,9 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
+ // [CC:] rework virtual clock
|
||
|
+ vop2_clk_init(vop2);
|
||
|
+
|
||
|
ret = vop2_find_rgb_encoder(vop2);
|
||
|
if (ret >= 0) {
|
||
|
vop2->rgb = rockchip_rgb_init(dev, &vop2->vps[ret].crtc,
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 63daa1b92854f3c1f7f1c46fac5af1bcf3c103e5 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 15 Jan 2024 22:47:41 +0200
|
||
|
Subject: [PATCH 32/54] arm64: dts: rockchip: Add HDMI0 bridge to rk3588
|
||
|
|
||
|
Add DT node for the HDMI0 bridge found on RK3588 SoC.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 46 +++++++++++++++++++++++
|
||
|
1 file changed, 46 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
index ae3ccafe6871..dd576b87354b 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
@@ -1501,6 +1501,52 @@ i2s9_8ch: i2s@fddfc000 {
|
||
|
status = "disabled";
|
||
|
};
|
||
|
|
||
|
+ hdmi0: hdmi@fde80000 {
|
||
|
+ compatible = "rockchip,rk3588-dw-hdmi";
|
||
|
+ reg = <0x0 0xfde80000 0x0 0x20000>;
|
||
|
+ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH 0>,
|
||
|
+ <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>,
|
||
|
+ <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH 0>,
|
||
|
+ <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH 0>,
|
||
|
+ <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH 0>;
|
||
|
+ clocks = <&cru PCLK_HDMITX0>,
|
||
|
+ <&cru CLK_HDMIHDP0>,
|
||
|
+ <&cru CLK_HDMITX0_EARC>,
|
||
|
+ <&cru CLK_HDMITX0_REF>,
|
||
|
+ <&cru MCLK_I2S5_8CH_TX>,
|
||
|
+ <&cru HCLK_VO1>;
|
||
|
+ clock-names = "pclk",
|
||
|
+ "hdp",
|
||
|
+ "earc",
|
||
|
+ "ref",
|
||
|
+ "aud",
|
||
|
+ "hclk_vo1";
|
||
|
+ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>;
|
||
|
+ reset-names = "ref", "hdp";
|
||
|
+ power-domains = <&power RK3588_PD_VO1>;
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&hdmim0_tx0_cec &hdmim0_tx0_hpd
|
||
|
+ &hdmim0_tx0_scl &hdmim0_tx0_sda>;
|
||
|
+ rockchip,grf = <&sys_grf>;
|
||
|
+ rockchip,vo1_grf = <&vo1_grf>;
|
||
|
+ phys = <&hdptxphy_hdmi0>;
|
||
|
+ phy-names = "hdmi";
|
||
|
+ status = "disabled";
|
||
|
+
|
||
|
+ ports {
|
||
|
+ #address-cells = <1>;
|
||
|
+ #size-cells = <0>;
|
||
|
+
|
||
|
+ hdmi0_in: port@0 {
|
||
|
+ reg = <0>;
|
||
|
+ };
|
||
|
+
|
||
|
+ hdmi0_out: port@1 {
|
||
|
+ reg = <1>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
qos_gpu_m0: qos@fdf35000 {
|
||
|
compatible = "rockchip,rk3588-qos", "syscon";
|
||
|
reg = <0x0 0xfdf35000 0x0 0x20>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From e2c386a032c2076f06d92da94805185f02252187 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 15 Jan 2024 22:51:17 +0200
|
||
|
Subject: [PATCH 33/54] arm64: dts: rockchip: Enable HDMI0 on rock-5b
|
||
|
|
||
|
Add the necessary DT changes to enable HDMI0 on Rock 5B.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../boot/dts/rockchip/rk3588-rock-5b.dts | 30 +++++++++++++++++++
|
||
|
1 file changed, 30 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
index f013d7841e89..cd3fd9cf9402 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
@@ -4,6 +4,7 @@
|
||
|
|
||
|
#include <dt-bindings/gpio/gpio.h>
|
||
|
#include <dt-bindings/leds/common.h>
|
||
|
+#include <dt-bindings/soc/rockchip,vop2.h>
|
||
|
#include <dt-bindings/usb/pd.h>
|
||
|
#include "rk3588.dtsi"
|
||
|
|
||
|
@@ -196,6 +197,16 @@ &gpu {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&hdmi0 {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&hdmi0_in {
|
||
|
+ hdmi0_in_vp0: endpoint {
|
||
|
+ remote-endpoint = <&vp0_out_hdmi0>;
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
&hdmirx_cma {
|
||
|
status = "okay";
|
||
|
};
|
||
|
@@ -208,6 +219,10 @@ &hdmirx_ctrler {
|
||
|
memory-region = <&hdmirx_cma>;
|
||
|
};
|
||
|
|
||
|
+&hdptxphy_hdmi0 {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
&i2c0 {
|
||
|
pinctrl-names = "default";
|
||
|
pinctrl-0 = <&i2c0m2_xfer>;
|
||
|
@@ -969,3 +984,18 @@ &usb_host1_xhci {
|
||
|
&usb_host2_xhci {
|
||
|
status = "okay";
|
||
|
};
|
||
|
+
|
||
|
+&vop_mmu {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&vop {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&vp0 {
|
||
|
+ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
|
||
|
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
|
||
|
+ remote-endpoint = <&hdmi0_in_vp0>;
|
||
|
+ };
|
||
|
+};
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From f6c1ee98ba1c9dd9f92428ada473c2119a66079b Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Wed, 17 Jan 2024 01:53:38 +0200
|
||
|
Subject: [PATCH 34/54] arm64: dts: rockchip: Enable HDMI0 on rk3588-evb1
|
||
|
|
||
|
Add the necessary DT changes to enable HDMI0 on Rockchip RK3588 EVB1.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../boot/dts/rockchip/rk3588-evb1-v10.dts | 30 +++++++++++++++++++
|
||
|
1 file changed, 30 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
index dd2fb2515900..e172647ba058 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
@@ -9,6 +9,7 @@
|
||
|
#include <dt-bindings/gpio/gpio.h>
|
||
|
#include <dt-bindings/input/input.h>
|
||
|
#include <dt-bindings/pinctrl/rockchip.h>
|
||
|
+#include <dt-bindings/soc/rockchip,vop2.h>
|
||
|
#include <dt-bindings/usb/pd.h>
|
||
|
#include "rk3588.dtsi"
|
||
|
|
||
|
@@ -342,6 +343,20 @@ &gpu {
|
||
|
status = "okay";
|
||
|
};
|
||
|
|
||
|
+&hdmi0 {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&hdmi0_in {
|
||
|
+ hdmi0_in_vp0: endpoint {
|
||
|
+ remote-endpoint = <&vp0_out_hdmi0>;
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
+&hdptxphy_hdmi0 {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
&i2c2 {
|
||
|
status = "okay";
|
||
|
|
||
|
@@ -1336,3 +1351,18 @@ &usb_host1_xhci {
|
||
|
dr_mode = "host";
|
||
|
status = "okay";
|
||
|
};
|
||
|
+
|
||
|
+&vop_mmu {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&vop {
|
||
|
+ status = "okay";
|
||
|
+};
|
||
|
+
|
||
|
+&vp0 {
|
||
|
+ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
|
||
|
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
|
||
|
+ remote-endpoint = <&hdmi0_in_vp0>;
|
||
|
+ };
|
||
|
+};
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 15af3f84555104275cf534724431641a9763d172 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 16 Jan 2024 03:13:38 +0200
|
||
|
Subject: [PATCH 35/54] [WIP] arm64: dts: rockchip: Enable HDMI0 PHY clk
|
||
|
provider on rk3588
|
||
|
|
||
|
The HDMI0 PHY can be used as a clock provider on RK3588, hence add the
|
||
|
missing #clock-cells property.
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 1 +
|
||
|
1 file changed, 1 insertion(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
index dd576b87354b..eb1aa7d8e475 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
@@ -2954,6 +2954,7 @@ hdptxphy_hdmi0: phy@fed60000 {
|
||
|
reg = <0x0 0xfed60000 0x0 0x2000>;
|
||
|
clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>;
|
||
|
clock-names = "ref", "apb";
|
||
|
+ #clock-cells = <0>;
|
||
|
#phy-cells = <0>;
|
||
|
resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>,
|
||
|
<&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>,
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From c490fa08603e50bd609a6c47c7f1d0fa3b53b268 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Fri, 3 Nov 2023 20:05:05 +0200
|
||
|
Subject: [PATCH 36/54] [WIP] arm64: dts: rockchip: Make use of HDMI0 PHY PLL
|
||
|
on rock-5b
|
||
|
|
||
|
The initial vop2 support for rk3588 in mainline is not able to handle
|
||
|
all display modes supported by connected displays, e.g.
|
||
|
2560x1440-75.00Hz, 2048x1152-60.00Hz, 1024x768-60.00Hz.
|
||
|
|
||
|
Additionally, it doesn't cope with non-integer refresh rates like 59.94,
|
||
|
29.97, 23.98, etc.
|
||
|
|
||
|
Make use of the HDMI0 PHY PLL to support the additional display modes.
|
||
|
|
||
|
Note this requires commit "drm/rockchip: vop2: Improve display modes
|
||
|
handling on rk3588", which needs a rework to be upstreamable.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 5 +++++
|
||
|
1 file changed, 5 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
index cd3fd9cf9402..c551b676860c 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
||
|
@@ -192,6 +192,11 @@ &cpu_l3 {
|
||
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
||
|
};
|
||
|
|
||
|
+&display_subsystem {
|
||
|
+ clocks = <&hdptxphy_hdmi0>;
|
||
|
+ clock-names = "hdmi0_phy_pll";
|
||
|
+};
|
||
|
+
|
||
|
&gpu {
|
||
|
mali-supply = <&vdd_gpu_s0>;
|
||
|
status = "okay";
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From c7b836ef5634362651e8ce268962156c852cca26 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Wed, 17 Jan 2024 02:00:41 +0200
|
||
|
Subject: [PATCH 37/54] [WIP] arm64: dts: rockchip: Make use of HDMI0 PHY PLL
|
||
|
on rk3588-evb1
|
||
|
|
||
|
The initial vop2 support for rk3588 in mainline is not able to handle
|
||
|
all display modes supported by connected displays, e.g.
|
||
|
2560x1440-75.00Hz, 2048x1152-60.00Hz, 1024x768-60.00Hz.
|
||
|
|
||
|
Additionally, it doesn't cope with non-integer refresh rates like 59.94,
|
||
|
29.97, 23.98, etc.
|
||
|
|
||
|
Make use of the HDMI0 PHY PLL to support the additional display modes.
|
||
|
|
||
|
Note this requires commit "drm/rockchip: vop2: Improve display modes
|
||
|
handling on rk3588", which needs a rework to be upstreamable.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts | 5 +++++
|
||
|
1 file changed, 5 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
index e172647ba058..cb3925d6c7ba 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
||
|
@@ -322,6 +322,11 @@ &cpu_l3 {
|
||
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
||
|
};
|
||
|
|
||
|
+&display_subsystem {
|
||
|
+ clocks = <&hdptxphy_hdmi0>;
|
||
|
+ clock-names = "hdmi0_phy_pll";
|
||
|
+};
|
||
|
+
|
||
|
&gmac0 {
|
||
|
clock_in_out = "output";
|
||
|
phy-handle = <&rgmii_phy>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 3ca942418bedc7a8a28ed1ee8f4c92bdabf1e378 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Sat, 18 May 2024 02:49:30 +0300
|
||
|
Subject: [PATCH 38/54] drm/bridge: dw-hdmi: Simplify clock handling
|
||
|
|
||
|
Make use of devm_clk_get_enabled() to replace devm_clk_get() and
|
||
|
clk_prepare_enable() for isfr and iahb clocks, and drop the now
|
||
|
unnecessary calls to clk_disable_unprepare().
|
||
|
|
||
|
Similarly, use devm_clk_get_optional_enabled() helper for cec clock,
|
||
|
which additionally allows to remove the -ENOENT test.
|
||
|
|
||
|
Moreover, the clock related members of struct dw_hdmi are not required
|
||
|
anymore, hence drop them.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 66 ++++++-----------------
|
||
|
1 file changed, 16 insertions(+), 50 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index 9f2bc932c371..0031f3c54882 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -138,9 +138,6 @@ struct dw_hdmi {
|
||
|
struct platform_device *audio;
|
||
|
struct platform_device *cec;
|
||
|
struct device *dev;
|
||
|
- struct clk *isfr_clk;
|
||
|
- struct clk *iahb_clk;
|
||
|
- struct clk *cec_clk;
|
||
|
struct dw_hdmi_i2c *i2c;
|
||
|
|
||
|
struct hdmi_data_info hdmi_data;
|
||
|
@@ -3326,6 +3323,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
struct device_node *ddc_node;
|
||
|
struct dw_hdmi_cec_data cec;
|
||
|
struct dw_hdmi *hdmi;
|
||
|
+ struct clk *clk;
|
||
|
struct resource *iores = NULL;
|
||
|
int irq;
|
||
|
int ret;
|
||
|
@@ -3405,50 +3403,27 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
hdmi->regm = plat_data->regm;
|
||
|
}
|
||
|
|
||
|
- hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
|
||
|
- if (IS_ERR(hdmi->isfr_clk)) {
|
||
|
- ret = PTR_ERR(hdmi->isfr_clk);
|
||
|
+ clk = devm_clk_get_enabled(hdmi->dev, "isfr");
|
||
|
+ if (IS_ERR(clk)) {
|
||
|
+ ret = PTR_ERR(clk);
|
||
|
dev_err(hdmi->dev, "Unable to get HDMI isfr clk: %d\n", ret);
|
||
|
goto err_res;
|
||
|
}
|
||
|
|
||
|
- ret = clk_prepare_enable(hdmi->isfr_clk);
|
||
|
- if (ret) {
|
||
|
- dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
|
||
|
- goto err_res;
|
||
|
- }
|
||
|
-
|
||
|
- hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
|
||
|
- if (IS_ERR(hdmi->iahb_clk)) {
|
||
|
- ret = PTR_ERR(hdmi->iahb_clk);
|
||
|
+ clk = devm_clk_get_enabled(hdmi->dev, "iahb");
|
||
|
+ if (IS_ERR(clk)) {
|
||
|
+ ret = PTR_ERR(clk);
|
||
|
dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
|
||
|
- goto err_isfr;
|
||
|
- }
|
||
|
-
|
||
|
- ret = clk_prepare_enable(hdmi->iahb_clk);
|
||
|
- if (ret) {
|
||
|
- dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
|
||
|
- goto err_isfr;
|
||
|
+ goto err_res;
|
||
|
}
|
||
|
|
||
|
- hdmi->cec_clk = devm_clk_get(hdmi->dev, "cec");
|
||
|
- if (PTR_ERR(hdmi->cec_clk) == -ENOENT) {
|
||
|
- hdmi->cec_clk = NULL;
|
||
|
- } else if (IS_ERR(hdmi->cec_clk)) {
|
||
|
- ret = PTR_ERR(hdmi->cec_clk);
|
||
|
+ clk = devm_clk_get_optional_enabled(hdmi->dev, "cec");
|
||
|
+ if (IS_ERR(clk)) {
|
||
|
+ ret = PTR_ERR(clk);
|
||
|
if (ret != -EPROBE_DEFER)
|
||
|
dev_err(hdmi->dev, "Cannot get HDMI cec clock: %d\n",
|
||
|
ret);
|
||
|
-
|
||
|
- hdmi->cec_clk = NULL;
|
||
|
- goto err_iahb;
|
||
|
- } else {
|
||
|
- ret = clk_prepare_enable(hdmi->cec_clk);
|
||
|
- if (ret) {
|
||
|
- dev_err(hdmi->dev, "Cannot enable HDMI cec clock: %d\n",
|
||
|
- ret);
|
||
|
- goto err_iahb;
|
||
|
- }
|
||
|
+ goto err_res;
|
||
|
}
|
||
|
|
||
|
/* Product and revision IDs */
|
||
|
@@ -3462,12 +3437,12 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n",
|
||
|
hdmi->version, prod_id0, prod_id1);
|
||
|
ret = -ENODEV;
|
||
|
- goto err_iahb;
|
||
|
+ goto err_res;
|
||
|
}
|
||
|
|
||
|
ret = dw_hdmi_detect_phy(hdmi);
|
||
|
if (ret < 0)
|
||
|
- goto err_iahb;
|
||
|
+ goto err_res;
|
||
|
|
||
|
dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n",
|
||
|
hdmi->version >> 12, hdmi->version & 0xfff,
|
||
|
@@ -3479,14 +3454,14 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
irq = platform_get_irq(pdev, 0);
|
||
|
if (irq < 0) {
|
||
|
ret = irq;
|
||
|
- goto err_iahb;
|
||
|
+ goto err_res;
|
||
|
}
|
||
|
|
||
|
ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
|
||
|
dw_hdmi_irq, IRQF_SHARED,
|
||
|
dev_name(dev), hdmi);
|
||
|
if (ret)
|
||
|
- goto err_iahb;
|
||
|
+ goto err_res;
|
||
|
|
||
|
/*
|
||
|
* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
|
||
|
@@ -3603,11 +3578,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
|
||
|
return hdmi;
|
||
|
|
||
|
-err_iahb:
|
||
|
- clk_disable_unprepare(hdmi->iahb_clk);
|
||
|
- clk_disable_unprepare(hdmi->cec_clk);
|
||
|
-err_isfr:
|
||
|
- clk_disable_unprepare(hdmi->isfr_clk);
|
||
|
err_res:
|
||
|
i2c_put_adapter(hdmi->ddc);
|
||
|
|
||
|
@@ -3627,10 +3597,6 @@ void dw_hdmi_remove(struct dw_hdmi *hdmi)
|
||
|
/* Disable all interrupts */
|
||
|
hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
|
||
|
|
||
|
- clk_disable_unprepare(hdmi->iahb_clk);
|
||
|
- clk_disable_unprepare(hdmi->isfr_clk);
|
||
|
- clk_disable_unprepare(hdmi->cec_clk);
|
||
|
-
|
||
|
if (hdmi->i2c)
|
||
|
i2c_del_adapter(&hdmi->i2c->adap);
|
||
|
else
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 03f4db37d31eb559828bec3f1bd5abcee19d843b Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Wed, 24 Apr 2024 14:35:06 +0300
|
||
|
Subject: [PATCH 39/54] drm/bridge: dw-hdmi: Move common data to separate
|
||
|
header
|
||
|
|
||
|
In preparation to add support for the HDMI 2.1 Quad-Pixel TX Controller,
|
||
|
move structs and common function declarations to a new dw-hdmi-common.h
|
||
|
header file.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../gpu/drm/bridge/synopsys/dw-hdmi-common.h | 166 +++++++++++++++++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 170 +++---------------
|
||
|
2 files changed, 193 insertions(+), 143 deletions(-)
|
||
|
create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
new file mode 100644
|
||
|
index 000000000000..9182564278e4
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
@@ -0,0 +1,166 @@
|
||
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||
|
+#ifndef __DW_HDMI_COMMON_H__
|
||
|
+#define __DW_HDMI_COMMON_H__
|
||
|
+
|
||
|
+#include <linux/i2c.h>
|
||
|
+#include <linux/completion.h>
|
||
|
+#include <linux/mutex.h>
|
||
|
+#include <linux/spinlock_types.h>
|
||
|
+
|
||
|
+#include <drm/bridge/dw_hdmi.h>
|
||
|
+#include <drm/drm_connector.h>
|
||
|
+#include <drm/drm_bridge.h>
|
||
|
+#include <drm/drm_modes.h>
|
||
|
+
|
||
|
+#include <sound/hdmi-codec.h>
|
||
|
+
|
||
|
+struct cec_notifier;
|
||
|
+struct device;
|
||
|
+struct drm_bridge_state;
|
||
|
+struct drm_crtc_state;
|
||
|
+struct drm_edid;
|
||
|
+struct pinctrl;
|
||
|
+struct pinctrl_state;
|
||
|
+struct platform_device;
|
||
|
+struct regmap;
|
||
|
+
|
||
|
+#define DDC_CI_ADDR 0x37
|
||
|
+#define DDC_SEGMENT_ADDR 0x30
|
||
|
+
|
||
|
+#define HDMI_EDID_LEN 512
|
||
|
+
|
||
|
+/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
|
||
|
+#define SCDC_MIN_SOURCE_VERSION 0x1
|
||
|
+
|
||
|
+#define HDMI14_MAX_TMDSCLK 340000000
|
||
|
+
|
||
|
+struct hdmi_vmode {
|
||
|
+ bool mdataenablepolarity;
|
||
|
+
|
||
|
+ unsigned int mpixelclock;
|
||
|
+ unsigned int mpixelrepetitioninput;
|
||
|
+ unsigned int mpixelrepetitionoutput;
|
||
|
+ unsigned int mtmdsclock;
|
||
|
+};
|
||
|
+
|
||
|
+struct hdmi_data_info {
|
||
|
+ unsigned int enc_in_bus_format;
|
||
|
+ unsigned int enc_out_bus_format;
|
||
|
+ unsigned int enc_in_encoding;
|
||
|
+ unsigned int enc_out_encoding;
|
||
|
+ unsigned int pix_repet_factor;
|
||
|
+ unsigned int hdcp_enable;
|
||
|
+ struct hdmi_vmode video_mode;
|
||
|
+ bool rgb_limited_range;
|
||
|
+};
|
||
|
+
|
||
|
+struct dw_hdmi_i2c {
|
||
|
+ struct i2c_adapter adap;
|
||
|
+
|
||
|
+ struct mutex lock; /* used to serialize data transfers */
|
||
|
+ struct completion cmp;
|
||
|
+ u8 stat;
|
||
|
+
|
||
|
+ u8 slave_reg;
|
||
|
+ bool is_regaddr;
|
||
|
+ bool is_segment;
|
||
|
+};
|
||
|
+
|
||
|
+struct dw_hdmi_phy_data {
|
||
|
+ enum dw_hdmi_phy_type type;
|
||
|
+ const char *name;
|
||
|
+ unsigned int gen;
|
||
|
+ bool has_svsret;
|
||
|
+ int (*configure)(struct dw_hdmi *hdmi,
|
||
|
+ const struct dw_hdmi_plat_data *pdata,
|
||
|
+ unsigned long mpixelclock);
|
||
|
+};
|
||
|
+
|
||
|
+struct dw_hdmi {
|
||
|
+ struct drm_connector connector;
|
||
|
+ struct drm_bridge bridge;
|
||
|
+ struct drm_bridge *next_bridge;
|
||
|
+
|
||
|
+ unsigned int version;
|
||
|
+
|
||
|
+ struct platform_device *audio;
|
||
|
+ struct platform_device *cec;
|
||
|
+ struct device *dev;
|
||
|
+ struct dw_hdmi_i2c *i2c;
|
||
|
+
|
||
|
+ struct hdmi_data_info hdmi_data;
|
||
|
+ const struct dw_hdmi_plat_data *plat_data;
|
||
|
+
|
||
|
+ int vic;
|
||
|
+
|
||
|
+ u8 edid[HDMI_EDID_LEN];
|
||
|
+
|
||
|
+ struct {
|
||
|
+ const struct dw_hdmi_phy_ops *ops;
|
||
|
+ const char *name;
|
||
|
+ void *data;
|
||
|
+ bool enabled;
|
||
|
+ } phy;
|
||
|
+
|
||
|
+ struct drm_display_mode previous_mode;
|
||
|
+
|
||
|
+ struct i2c_adapter *ddc;
|
||
|
+ void __iomem *regs;
|
||
|
+ bool sink_is_hdmi;
|
||
|
+ bool sink_has_audio;
|
||
|
+
|
||
|
+ struct pinctrl *pinctrl;
|
||
|
+ struct pinctrl_state *default_state;
|
||
|
+ struct pinctrl_state *unwedge_state;
|
||
|
+
|
||
|
+ struct mutex mutex; /* for state below and previous_mode */
|
||
|
+ enum drm_connector_force force; /* mutex-protected force state */
|
||
|
+ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */
|
||
|
+ bool disabled; /* DRM has disabled our bridge */
|
||
|
+ bool bridge_is_on; /* indicates the bridge is on */
|
||
|
+ bool rxsense; /* rxsense state */
|
||
|
+ u8 phy_mask; /* desired phy int mask settings */
|
||
|
+ u8 mc_clkdis; /* clock disable register */
|
||
|
+
|
||
|
+ spinlock_t audio_lock;
|
||
|
+ struct mutex audio_mutex;
|
||
|
+ unsigned int sample_non_pcm;
|
||
|
+ unsigned int sample_width;
|
||
|
+ unsigned int sample_rate;
|
||
|
+ unsigned int channels;
|
||
|
+ unsigned int audio_cts;
|
||
|
+ unsigned int audio_n;
|
||
|
+ bool audio_enable;
|
||
|
+
|
||
|
+ unsigned int reg_shift;
|
||
|
+ struct regmap *regm;
|
||
|
+ void (*enable_audio)(struct dw_hdmi *hdmi);
|
||
|
+ void (*disable_audio)(struct dw_hdmi *hdmi);
|
||
|
+
|
||
|
+ struct mutex cec_notifier_mutex;
|
||
|
+ struct cec_notifier *cec_notifier;
|
||
|
+
|
||
|
+ hdmi_codec_plugged_cb plugged_cb;
|
||
|
+ struct device *codec_dev;
|
||
|
+ enum drm_connector_status last_connector_result;
|
||
|
+};
|
||
|
+
|
||
|
+void dw_handle_plugged_change(struct dw_hdmi *hdmi, bool plugged);
|
||
|
+bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_info *display);
|
||
|
+
|
||
|
+enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector,
|
||
|
+ bool force);
|
||
|
+
|
||
|
+int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *bridge_state,
|
||
|
+ struct drm_crtc_state *crtc_state,
|
||
|
+ struct drm_connector_state *conn_state);
|
||
|
+void dw_hdmi_bridge_detach(struct drm_bridge *bridge);
|
||
|
+void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||
|
+ const struct drm_display_mode *orig_mode,
|
||
|
+ const struct drm_display_mode *mode);
|
||
|
+enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge);
|
||
|
+const struct drm_edid *dw_hdmi_bridge_edid_read(struct drm_bridge *bridge,
|
||
|
+ struct drm_connector *connector);
|
||
|
+#endif /* __DW_HDMI_COMMON_H__ */
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index 0031f3c54882..b66877771f56 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -10,10 +10,8 @@
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/hdmi.h>
|
||
|
-#include <linux/i2c.h>
|
||
|
#include <linux/irq.h>
|
||
|
#include <linux/module.h>
|
||
|
-#include <linux/mutex.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/pinctrl/consumer.h>
|
||
|
#include <linux/regmap.h>
|
||
|
@@ -25,12 +23,10 @@
|
||
|
#include <uapi/linux/media-bus-format.h>
|
||
|
#include <uapi/linux/videodev2.h>
|
||
|
|
||
|
-#include <drm/bridge/dw_hdmi.h>
|
||
|
#include <drm/display/drm_hdmi_helper.h>
|
||
|
#include <drm/display/drm_scdc_helper.h>
|
||
|
#include <drm/drm_atomic.h>
|
||
|
#include <drm/drm_atomic_helper.h>
|
||
|
-#include <drm/drm_bridge.h>
|
||
|
#include <drm/drm_edid.h>
|
||
|
#include <drm/drm_of.h>
|
||
|
#include <drm/drm_print.h>
|
||
|
@@ -38,18 +34,9 @@
|
||
|
|
||
|
#include "dw-hdmi-audio.h"
|
||
|
#include "dw-hdmi-cec.h"
|
||
|
+#include "dw-hdmi-common.h"
|
||
|
#include "dw-hdmi.h"
|
||
|
|
||
|
-#define DDC_CI_ADDR 0x37
|
||
|
-#define DDC_SEGMENT_ADDR 0x30
|
||
|
-
|
||
|
-#define HDMI_EDID_LEN 512
|
||
|
-
|
||
|
-/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
|
||
|
-#define SCDC_MIN_SOURCE_VERSION 0x1
|
||
|
-
|
||
|
-#define HDMI14_MAX_TMDSCLK 340000000
|
||
|
-
|
||
|
static const u16 csc_coeff_default[3][4] = {
|
||
|
{ 0x2000, 0x0000, 0x0000, 0x0000 },
|
||
|
{ 0x0000, 0x2000, 0x0000, 0x0000 },
|
||
|
@@ -86,117 +73,6 @@ static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {
|
||
|
{ 0x0000, 0x0000, 0x1b7c, 0x0020 }
|
||
|
};
|
||
|
|
||
|
-struct hdmi_vmode {
|
||
|
- bool mdataenablepolarity;
|
||
|
-
|
||
|
- unsigned int mpixelclock;
|
||
|
- unsigned int mpixelrepetitioninput;
|
||
|
- unsigned int mpixelrepetitionoutput;
|
||
|
- unsigned int mtmdsclock;
|
||
|
-};
|
||
|
-
|
||
|
-struct hdmi_data_info {
|
||
|
- unsigned int enc_in_bus_format;
|
||
|
- unsigned int enc_out_bus_format;
|
||
|
- unsigned int enc_in_encoding;
|
||
|
- unsigned int enc_out_encoding;
|
||
|
- unsigned int pix_repet_factor;
|
||
|
- unsigned int hdcp_enable;
|
||
|
- struct hdmi_vmode video_mode;
|
||
|
- bool rgb_limited_range;
|
||
|
-};
|
||
|
-
|
||
|
-struct dw_hdmi_i2c {
|
||
|
- struct i2c_adapter adap;
|
||
|
-
|
||
|
- struct mutex lock; /* used to serialize data transfers */
|
||
|
- struct completion cmp;
|
||
|
- u8 stat;
|
||
|
-
|
||
|
- u8 slave_reg;
|
||
|
- bool is_regaddr;
|
||
|
- bool is_segment;
|
||
|
-};
|
||
|
-
|
||
|
-struct dw_hdmi_phy_data {
|
||
|
- enum dw_hdmi_phy_type type;
|
||
|
- const char *name;
|
||
|
- unsigned int gen;
|
||
|
- bool has_svsret;
|
||
|
- int (*configure)(struct dw_hdmi *hdmi,
|
||
|
- const struct dw_hdmi_plat_data *pdata,
|
||
|
- unsigned long mpixelclock);
|
||
|
-};
|
||
|
-
|
||
|
-struct dw_hdmi {
|
||
|
- struct drm_connector connector;
|
||
|
- struct drm_bridge bridge;
|
||
|
- struct drm_bridge *next_bridge;
|
||
|
-
|
||
|
- unsigned int version;
|
||
|
-
|
||
|
- struct platform_device *audio;
|
||
|
- struct platform_device *cec;
|
||
|
- struct device *dev;
|
||
|
- struct dw_hdmi_i2c *i2c;
|
||
|
-
|
||
|
- struct hdmi_data_info hdmi_data;
|
||
|
- const struct dw_hdmi_plat_data *plat_data;
|
||
|
-
|
||
|
- int vic;
|
||
|
-
|
||
|
- u8 edid[HDMI_EDID_LEN];
|
||
|
-
|
||
|
- struct {
|
||
|
- const struct dw_hdmi_phy_ops *ops;
|
||
|
- const char *name;
|
||
|
- void *data;
|
||
|
- bool enabled;
|
||
|
- } phy;
|
||
|
-
|
||
|
- struct drm_display_mode previous_mode;
|
||
|
-
|
||
|
- struct i2c_adapter *ddc;
|
||
|
- void __iomem *regs;
|
||
|
- bool sink_is_hdmi;
|
||
|
- bool sink_has_audio;
|
||
|
-
|
||
|
- struct pinctrl *pinctrl;
|
||
|
- struct pinctrl_state *default_state;
|
||
|
- struct pinctrl_state *unwedge_state;
|
||
|
-
|
||
|
- struct mutex mutex; /* for state below and previous_mode */
|
||
|
- enum drm_connector_force force; /* mutex-protected force state */
|
||
|
- struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */
|
||
|
- bool disabled; /* DRM has disabled our bridge */
|
||
|
- bool bridge_is_on; /* indicates the bridge is on */
|
||
|
- bool rxsense; /* rxsense state */
|
||
|
- u8 phy_mask; /* desired phy int mask settings */
|
||
|
- u8 mc_clkdis; /* clock disable register */
|
||
|
-
|
||
|
- spinlock_t audio_lock;
|
||
|
- struct mutex audio_mutex;
|
||
|
- unsigned int sample_non_pcm;
|
||
|
- unsigned int sample_width;
|
||
|
- unsigned int sample_rate;
|
||
|
- unsigned int channels;
|
||
|
- unsigned int audio_cts;
|
||
|
- unsigned int audio_n;
|
||
|
- bool audio_enable;
|
||
|
-
|
||
|
- unsigned int reg_shift;
|
||
|
- struct regmap *regm;
|
||
|
- void (*enable_audio)(struct dw_hdmi *hdmi);
|
||
|
- void (*disable_audio)(struct dw_hdmi *hdmi);
|
||
|
-
|
||
|
- struct mutex cec_notifier_mutex;
|
||
|
- struct cec_notifier *cec_notifier;
|
||
|
-
|
||
|
- hdmi_codec_plugged_cb plugged_cb;
|
||
|
- struct device *codec_dev;
|
||
|
- enum drm_connector_status last_connector_result;
|
||
|
-};
|
||
|
-
|
||
|
#define HDMI_IH_PHY_STAT0_RX_SENSE \
|
||
|
(HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
|
||
|
HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
|
||
|
@@ -219,11 +95,12 @@ static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
-static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged)
|
||
|
+void dw_handle_plugged_change(struct dw_hdmi *hdmi, bool plugged)
|
||
|
{
|
||
|
if (hdmi->plugged_cb && hdmi->codec_dev)
|
||
|
hdmi->plugged_cb(hdmi->codec_dev, plugged);
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_handle_plugged_change);
|
||
|
|
||
|
int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
|
||
|
struct device *codec_dev)
|
||
|
@@ -234,7 +111,7 @@ int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
|
||
|
hdmi->plugged_cb = fn;
|
||
|
hdmi->codec_dev = codec_dev;
|
||
|
plugged = hdmi->last_connector_result == connector_status_connected;
|
||
|
- handle_plugged_change(hdmi, plugged);
|
||
|
+ dw_handle_plugged_change(hdmi, plugged);
|
||
|
mutex_unlock(&hdmi->mutex);
|
||
|
|
||
|
return 0;
|
||
|
@@ -1361,8 +1238,8 @@ void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
|
||
|
EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
|
||
|
|
||
|
/* Filter out invalid setups to avoid configuring SCDC and scrambling */
|
||
|
-static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
|
||
|
- const struct drm_display_info *display)
|
||
|
+bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_info *display)
|
||
|
{
|
||
|
/* Completely disable SCDC support for older controllers */
|
||
|
if (hdmi->version < 0x200a)
|
||
|
@@ -1387,6 +1264,7 @@ static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_support_scdc);
|
||
|
|
||
|
/*
|
||
|
* HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
|
||
|
@@ -2486,13 +2364,14 @@ static const struct drm_edid *dw_hdmi_edid_read(struct dw_hdmi *hdmi,
|
||
|
* DRM Connector Operations
|
||
|
*/
|
||
|
|
||
|
-static enum drm_connector_status
|
||
|
+enum drm_connector_status
|
||
|
dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||
|
{
|
||
|
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
|
||
|
connector);
|
||
|
return dw_hdmi_detect(hdmi);
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_connector_detect);
|
||
|
|
||
|
static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
||
|
{
|
||
|
@@ -2868,10 +2747,10 @@ static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
|
||
|
return input_fmts;
|
||
|
}
|
||
|
|
||
|
-static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
|
||
|
- struct drm_bridge_state *bridge_state,
|
||
|
- struct drm_crtc_state *crtc_state,
|
||
|
- struct drm_connector_state *conn_state)
|
||
|
+int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *bridge_state,
|
||
|
+ struct drm_crtc_state *crtc_state,
|
||
|
+ struct drm_connector_state *conn_state)
|
||
|
{
|
||
|
struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
|
||
|
@@ -2887,6 +2766,7 @@ static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_bridge_atomic_check);
|
||
|
|
||
|
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
|
||
|
enum drm_bridge_attach_flags flags)
|
||
|
@@ -2900,7 +2780,7 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
|
||
|
return dw_hdmi_connector_create(hdmi);
|
||
|
}
|
||
|
|
||
|
-static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
|
||
|
+void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
|
||
|
{
|
||
|
struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
|
||
|
@@ -2909,6 +2789,7 @@ static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
|
||
|
hdmi->cec_notifier = NULL;
|
||
|
mutex_unlock(&hdmi->cec_notifier_mutex);
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_bridge_detach);
|
||
|
|
||
|
static enum drm_mode_status
|
||
|
dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
|
||
|
@@ -2930,9 +2811,9 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
|
||
|
return mode_status;
|
||
|
}
|
||
|
|
||
|
-static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||
|
- const struct drm_display_mode *orig_mode,
|
||
|
- const struct drm_display_mode *mode)
|
||
|
+void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||
|
+ const struct drm_display_mode *orig_mode,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
{
|
||
|
struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
|
||
|
@@ -2943,6 +2824,7 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
|
||
|
|
||
|
mutex_unlock(&hdmi->mutex);
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_bridge_mode_set);
|
||
|
|
||
|
static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
|
||
|
struct drm_bridge_state *old_state)
|
||
|
@@ -2954,7 +2836,7 @@ static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
|
||
|
hdmi->curr_conn = NULL;
|
||
|
dw_hdmi_update_power(hdmi);
|
||
|
dw_hdmi_update_phy_mask(hdmi);
|
||
|
- handle_plugged_change(hdmi, false);
|
||
|
+ dw_handle_plugged_change(hdmi, false);
|
||
|
mutex_unlock(&hdmi->mutex);
|
||
|
}
|
||
|
|
||
|
@@ -2973,24 +2855,26 @@ static void dw_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
|
||
|
hdmi->curr_conn = connector;
|
||
|
dw_hdmi_update_power(hdmi);
|
||
|
dw_hdmi_update_phy_mask(hdmi);
|
||
|
- handle_plugged_change(hdmi, true);
|
||
|
+ dw_handle_plugged_change(hdmi, true);
|
||
|
mutex_unlock(&hdmi->mutex);
|
||
|
}
|
||
|
|
||
|
-static enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge)
|
||
|
+enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge)
|
||
|
{
|
||
|
struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
|
||
|
return dw_hdmi_detect(hdmi);
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_bridge_detect);
|
||
|
|
||
|
-static const struct drm_edid *dw_hdmi_bridge_edid_read(struct drm_bridge *bridge,
|
||
|
- struct drm_connector *connector)
|
||
|
+const struct drm_edid *dw_hdmi_bridge_edid_read(struct drm_bridge *bridge,
|
||
|
+ struct drm_connector *connector)
|
||
|
{
|
||
|
struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
|
||
|
return dw_hdmi_edid_read(hdmi, connector);
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_bridge_edid_read);
|
||
|
|
||
|
static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
|
||
|
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 69b642c3b37d51e9022633382f7834479d34e0e1 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 30 Apr 2024 12:48:34 +0300
|
||
|
Subject: [PATCH 40/54] drm/bridge: dw-hdmi: Commonize dw_hdmi_i2c_adapter()
|
||
|
|
||
|
In preparation to support DW HDMI QP variant, add a new parameter to
|
||
|
dw_hdmi_i2c_adapter() which allows using a different i2c_algorithm and
|
||
|
move the function to the common header.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h | 2 ++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 8 +++++---
|
||
|
2 files changed, 7 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
index 9182564278e4..a83c2873854c 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
@@ -146,6 +146,8 @@ struct dw_hdmi {
|
||
|
};
|
||
|
|
||
|
void dw_handle_plugged_change(struct dw_hdmi *hdmi, bool plugged);
|
||
|
+struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi,
|
||
|
+ const struct i2c_algorithm *algo);
|
||
|
bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
|
||
|
const struct drm_display_info *display);
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index b66877771f56..5dd0e2bc080d 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -376,7 +376,8 @@ static const struct i2c_algorithm dw_hdmi_algorithm = {
|
||
|
.functionality = dw_hdmi_i2c_func,
|
||
|
};
|
||
|
|
||
|
-static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
|
||
|
+struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi,
|
||
|
+ const struct i2c_algorithm *algo)
|
||
|
{
|
||
|
struct i2c_adapter *adap;
|
||
|
struct dw_hdmi_i2c *i2c;
|
||
|
@@ -392,7 +393,7 @@ static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
|
||
|
adap = &i2c->adap;
|
||
|
adap->owner = THIS_MODULE;
|
||
|
adap->dev.parent = hdmi->dev;
|
||
|
- adap->algo = &dw_hdmi_algorithm;
|
||
|
+ adap->algo = algo ? algo : &dw_hdmi_algorithm;
|
||
|
strscpy(adap->name, "DesignWare HDMI", sizeof(adap->name));
|
||
|
i2c_set_adapdata(adap, hdmi);
|
||
|
|
||
|
@@ -409,6 +410,7 @@ static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi)
|
||
|
|
||
|
return adap;
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_i2c_adapter);
|
||
|
|
||
|
static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
|
||
|
unsigned int n)
|
||
|
@@ -3373,7 +3375,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
|
||
|
+ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi, NULL);
|
||
|
if (IS_ERR(hdmi->ddc))
|
||
|
hdmi->ddc = NULL;
|
||
|
}
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 2a111c90c36baf2529bf90669a2fed0d33753a0b Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 30 Apr 2024 13:06:11 +0300
|
||
|
Subject: [PATCH 41/54] drm/bridge: dw-hdmi: Commonize AVI infoframe setup
|
||
|
|
||
|
In preparation to support DW HDMI QP variant and minimize code
|
||
|
duplication, split hdmi_config_AVI() into a common
|
||
|
dw_hdmi_prep_avi_infoframe() function to be shared by the two drivers.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../gpu/drm/bridge/synopsys/dw-hdmi-common.h | 4 ++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 57 +++++++++++--------
|
||
|
2 files changed, 37 insertions(+), 24 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
index a83c2873854c..d34c979a48cd 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
@@ -150,6 +150,10 @@ struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi *hdmi,
|
||
|
const struct i2c_algorithm *algo);
|
||
|
bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
|
||
|
const struct drm_display_info *display);
|
||
|
+void dw_hdmi_prep_avi_infoframe(struct hdmi_avi_infoframe *frame,
|
||
|
+ struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector *connector,
|
||
|
+ const struct drm_display_mode *mode);
|
||
|
|
||
|
enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector,
|
||
|
bool force);
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index 5dd0e2bc080d..81d73fbcb2e6 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -1638,66 +1638,75 @@ static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
|
||
|
HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
|
||
|
}
|
||
|
|
||
|
-static void hdmi_config_AVI(struct dw_hdmi *hdmi,
|
||
|
- const struct drm_connector *connector,
|
||
|
- const struct drm_display_mode *mode)
|
||
|
+void dw_hdmi_prep_avi_infoframe(struct hdmi_avi_infoframe *frame,
|
||
|
+ struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector *connector,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
{
|
||
|
- struct hdmi_avi_infoframe frame;
|
||
|
- u8 val;
|
||
|
-
|
||
|
/* Initialise info frame from DRM mode */
|
||
|
- drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
|
||
|
+ drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode);
|
||
|
|
||
|
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
|
||
|
- drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
|
||
|
+ drm_hdmi_avi_infoframe_quant_range(frame, connector, mode,
|
||
|
hdmi->hdmi_data.rgb_limited_range ?
|
||
|
HDMI_QUANTIZATION_RANGE_LIMITED :
|
||
|
HDMI_QUANTIZATION_RANGE_FULL);
|
||
|
} else {
|
||
|
- frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
|
||
|
- frame.ycc_quantization_range =
|
||
|
+ frame->quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
|
||
|
+ frame->ycc_quantization_range =
|
||
|
HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
|
||
|
}
|
||
|
|
||
|
if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
|
||
|
- frame.colorspace = HDMI_COLORSPACE_YUV444;
|
||
|
+ frame->colorspace = HDMI_COLORSPACE_YUV444;
|
||
|
else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
|
||
|
- frame.colorspace = HDMI_COLORSPACE_YUV422;
|
||
|
+ frame->colorspace = HDMI_COLORSPACE_YUV422;
|
||
|
else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
||
|
- frame.colorspace = HDMI_COLORSPACE_YUV420;
|
||
|
+ frame->colorspace = HDMI_COLORSPACE_YUV420;
|
||
|
else
|
||
|
- frame.colorspace = HDMI_COLORSPACE_RGB;
|
||
|
+ frame->colorspace = HDMI_COLORSPACE_RGB;
|
||
|
|
||
|
/* Set up colorimetry */
|
||
|
if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
|
||
|
switch (hdmi->hdmi_data.enc_out_encoding) {
|
||
|
case V4L2_YCBCR_ENC_601:
|
||
|
if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601)
|
||
|
- frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
|
||
|
+ frame->colorimetry = HDMI_COLORIMETRY_EXTENDED;
|
||
|
else
|
||
|
- frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
|
||
|
- frame.extended_colorimetry =
|
||
|
+ frame->colorimetry = HDMI_COLORIMETRY_ITU_601;
|
||
|
+ frame->extended_colorimetry =
|
||
|
HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
|
||
|
break;
|
||
|
case V4L2_YCBCR_ENC_709:
|
||
|
if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709)
|
||
|
- frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
|
||
|
+ frame->colorimetry = HDMI_COLORIMETRY_EXTENDED;
|
||
|
else
|
||
|
- frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
|
||
|
- frame.extended_colorimetry =
|
||
|
+ frame->colorimetry = HDMI_COLORIMETRY_ITU_709;
|
||
|
+ frame->extended_colorimetry =
|
||
|
HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
|
||
|
break;
|
||
|
default: /* Carries no data */
|
||
|
- frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
|
||
|
- frame.extended_colorimetry =
|
||
|
+ frame->colorimetry = HDMI_COLORIMETRY_ITU_601;
|
||
|
+ frame->extended_colorimetry =
|
||
|
HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
- frame.colorimetry = HDMI_COLORIMETRY_NONE;
|
||
|
- frame.extended_colorimetry =
|
||
|
+ frame->colorimetry = HDMI_COLORIMETRY_NONE;
|
||
|
+ frame->extended_colorimetry =
|
||
|
HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
|
||
|
}
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_prep_avi_infoframe);
|
||
|
+
|
||
|
+static void hdmi_config_AVI(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector *connector,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ struct hdmi_avi_infoframe frame;
|
||
|
+ u8 val;
|
||
|
+
|
||
|
+ dw_hdmi_prep_avi_infoframe(&frame, hdmi, connector, mode);
|
||
|
|
||
|
/*
|
||
|
* The Designware IP uses a different byte format from standard
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From ae857523d29c06985e85bed4e5b5477d5757ed6c Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 30 Apr 2024 21:47:36 +0300
|
||
|
Subject: [PATCH 42/54] drm/bridge: dw-hdmi: Commonize vmode setup
|
||
|
|
||
|
In preparation to support DW HDMI QP variant and minimize code
|
||
|
duplication, split hdmi_av_composer() into a common dw_hdmi_prep_vmode()
|
||
|
function to be shared by the two drivers.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../gpu/drm/bridge/synopsys/dw-hdmi-common.h | 2 ++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 23 +++++++++++++------
|
||
|
2 files changed, 18 insertions(+), 7 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
index d34c979a48cd..b594be2c6337 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
@@ -154,6 +154,8 @@ void dw_hdmi_prep_avi_infoframe(struct hdmi_avi_infoframe *frame,
|
||
|
struct dw_hdmi *hdmi,
|
||
|
const struct drm_connector *connector,
|
||
|
const struct drm_display_mode *mode);
|
||
|
+struct hdmi_vmode *dw_hdmi_prep_vmode(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_mode *mode);
|
||
|
|
||
|
enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector,
|
||
|
bool force);
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index 81d73fbcb2e6..5cf929f68ae9 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -1864,15 +1864,10 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
|
||
|
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
|
||
|
}
|
||
|
|
||
|
-static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
||
|
- const struct drm_display_info *display,
|
||
|
- const struct drm_display_mode *mode)
|
||
|
+struct hdmi_vmode *dw_hdmi_prep_vmode(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
{
|
||
|
- u8 inv_val, bytes;
|
||
|
- const struct drm_hdmi_info *hdmi_info = &display->hdmi;
|
||
|
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
|
||
|
- int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
|
||
|
- unsigned int vdisplay, hdisplay;
|
||
|
|
||
|
vmode->mpixelclock = mode->clock * 1000;
|
||
|
|
||
|
@@ -1900,6 +1895,20 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
||
|
|
||
|
dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
|
||
|
|
||
|
+ return vmode;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_prep_vmode);
|
||
|
+
|
||
|
+static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_info *display,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ u8 inv_val, bytes;
|
||
|
+ const struct drm_hdmi_info *hdmi_info = &display->hdmi;
|
||
|
+ struct hdmi_vmode *vmode = dw_hdmi_prep_vmode(hdmi, mode);
|
||
|
+ int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
|
||
|
+ unsigned int vdisplay, hdisplay;
|
||
|
+
|
||
|
/* Set up HDMI_FC_INVIDCONF */
|
||
|
inv_val = (hdmi->hdmi_data.hdcp_enable ||
|
||
|
(dw_hdmi_support_scdc(hdmi, display) &&
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From d26a864cd352b0fa251f5ef4298da27190e32b4c Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 30 Apr 2024 23:51:06 +0300
|
||
|
Subject: [PATCH 43/54] drm/bridge: dw-hdmi: Commonize hdmi_data_info setup
|
||
|
|
||
|
In preparation to support DW HDMI QP variant and minimize code
|
||
|
duplication, split dw_hdmi_setup() into a common dw_hdmi_prep_data()
|
||
|
function to be shared by the two drivers.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../gpu/drm/bridge/synopsys/dw-hdmi-common.h | 2 ++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 22 +++++++++++++------
|
||
|
2 files changed, 17 insertions(+), 7 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
index b594be2c6337..b4ad71639ffa 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
@@ -156,6 +156,8 @@ void dw_hdmi_prep_avi_infoframe(struct hdmi_avi_infoframe *frame,
|
||
|
const struct drm_display_mode *mode);
|
||
|
struct hdmi_vmode *dw_hdmi_prep_vmode(struct dw_hdmi *hdmi,
|
||
|
const struct drm_display_mode *mode);
|
||
|
+void dw_hdmi_prep_data(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_mode *mode);
|
||
|
|
||
|
enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector,
|
||
|
bool force);
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index 5cf929f68ae9..a9053b0d5f54 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -2133,14 +2133,9 @@ static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
|
||
|
HDMI_IH_MUTE_FC_STAT2);
|
||
|
}
|
||
|
|
||
|
-static int dw_hdmi_setup(struct dw_hdmi *hdmi,
|
||
|
- const struct drm_connector *connector,
|
||
|
- const struct drm_display_mode *mode)
|
||
|
+void dw_hdmi_prep_data(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
{
|
||
|
- int ret;
|
||
|
-
|
||
|
- hdmi_disable_overflow_interrupts(hdmi);
|
||
|
-
|
||
|
hdmi->vic = drm_match_cea_mode(mode);
|
||
|
|
||
|
if (!hdmi->vic) {
|
||
|
@@ -2180,6 +2175,19 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
|
||
|
hdmi->hdmi_data.pix_repet_factor = 0;
|
||
|
hdmi->hdmi_data.hdcp_enable = 0;
|
||
|
hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_prep_data);
|
||
|
+
|
||
|
+
|
||
|
+static int dw_hdmi_setup(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector *connector,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ hdmi_disable_overflow_interrupts(hdmi);
|
||
|
+
|
||
|
+ dw_hdmi_prep_data(hdmi, mode);
|
||
|
|
||
|
/* HDMI Initialization Step B.1 */
|
||
|
hdmi_av_composer(hdmi, &connector->display_info, mode);
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From b41dc7e05fe0f33cffe74ffda9b1f980afa2849c Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Wed, 1 May 2024 01:46:51 +0300
|
||
|
Subject: [PATCH 44/54] drm/bridge: dw-hdmi: Commonize
|
||
|
dw_hdmi_connector_create()
|
||
|
|
||
|
In preparation to support DW HDMI QP variant, add a new parameter to
|
||
|
dw_hdmi_connector_create() which allows using a different
|
||
|
drm_connector_funcs structure and move the function to the common
|
||
|
header.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h | 2 ++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 8 +++++---
|
||
|
2 files changed, 7 insertions(+), 3 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
index b4ad71639ffa..44e27ebdd238 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-common.h
|
||
|
@@ -161,6 +161,8 @@ void dw_hdmi_prep_data(struct dw_hdmi *hdmi,
|
||
|
|
||
|
enum drm_connector_status dw_hdmi_connector_detect(struct drm_connector *connector,
|
||
|
bool force);
|
||
|
+int dw_hdmi_connector_create(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector_funcs *funcs);
|
||
|
|
||
|
int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
|
||
|
struct drm_bridge_state *bridge_state,
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
index a9053b0d5f54..39be3949c149 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||
|
@@ -2470,7 +2470,8 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
|
||
|
.atomic_check = dw_hdmi_connector_atomic_check,
|
||
|
};
|
||
|
|
||
|
-static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
|
||
|
+int dw_hdmi_connector_create(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector_funcs *funcs)
|
||
|
{
|
||
|
struct drm_connector *connector = &hdmi->connector;
|
||
|
struct cec_connector_info conn_info;
|
||
|
@@ -2488,7 +2489,7 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
|
||
|
drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
|
||
|
|
||
|
drm_connector_init_with_ddc(hdmi->bridge.dev, connector,
|
||
|
- &dw_hdmi_connector_funcs,
|
||
|
+ funcs ? funcs : &dw_hdmi_connector_funcs,
|
||
|
DRM_MODE_CONNECTOR_HDMIA,
|
||
|
hdmi->ddc);
|
||
|
|
||
|
@@ -2517,6 +2518,7 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_connector_create);
|
||
|
|
||
|
/* -----------------------------------------------------------------------------
|
||
|
* DRM Bridge Operations
|
||
|
@@ -2805,7 +2807,7 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
|
||
|
return drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
|
||
|
bridge, flags);
|
||
|
|
||
|
- return dw_hdmi_connector_create(hdmi);
|
||
|
+ return dw_hdmi_connector_create(hdmi, NULL);
|
||
|
}
|
||
|
|
||
|
void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From f3b76b1900c8f8433dcdbeb4e11448c965b4fbe0 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Fri, 24 May 2024 23:46:26 +0300
|
||
|
Subject: [PATCH 45/54] drm/rockchip: dw_hdmi: Use modern drm_device based
|
||
|
logging
|
||
|
|
||
|
Prefer drm_{err|info|dbg}() over deprecated DRM_DEV_{ERROR|INFO|DEBUG}()
|
||
|
logging macros.
|
||
|
|
||
|
Conversion done with the help of the following semantic patch, followed
|
||
|
by a few minor indentation adjustments:
|
||
|
|
||
|
@@
|
||
|
identifier T;
|
||
|
@@
|
||
|
|
||
|
(
|
||
|
-DRM_DEV_ERROR(T->dev,
|
||
|
+drm_err(T,
|
||
|
...)
|
||
|
|
|
||
|
-DRM_DEV_INFO(T->dev,
|
||
|
+drm_info(T,
|
||
|
...)
|
||
|
|
|
||
|
-DRM_DEV_DEBUG(T->dev,
|
||
|
+drm_dbg(T,
|
||
|
...)
|
||
|
)
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 24 ++++++++++-----------
|
||
|
1 file changed, 11 insertions(+), 13 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
index fe33092abbe7..2509ce19313f 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
@@ -212,7 +212,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
|
||
|
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||
|
if (IS_ERR(hdmi->regmap)) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
|
||
|
+ drm_err(hdmi, "Unable to get rockchip,grf\n");
|
||
|
return PTR_ERR(hdmi->regmap);
|
||
|
}
|
||
|
|
||
|
@@ -223,7 +223,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
|
||
|
return -EPROBE_DEFER;
|
||
|
} else if (IS_ERR(hdmi->ref_clk)) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
|
||
|
+ drm_err(hdmi, "failed to get reference clock\n");
|
||
|
return PTR_ERR(hdmi->ref_clk);
|
||
|
}
|
||
|
|
||
|
@@ -233,7 +233,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
} else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
|
||
|
return -EPROBE_DEFER;
|
||
|
} else if (IS_ERR(hdmi->grf_clk)) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
|
||
|
+ drm_err(hdmi, "failed to get grf clock\n");
|
||
|
return PTR_ERR(hdmi->grf_clk);
|
||
|
}
|
||
|
|
||
|
@@ -322,17 +322,16 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
||
|
|
||
|
ret = clk_prepare_enable(hdmi->grf_clk);
|
||
|
if (ret < 0) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
|
||
|
+ drm_err(hdmi, "failed to enable grfclk %d\n", ret);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
|
||
|
if (ret != 0)
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
|
||
|
+ drm_err(hdmi, "Could not write to GRF: %d\n", ret);
|
||
|
|
||
|
clk_disable_unprepare(hdmi->grf_clk);
|
||
|
- DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
|
||
|
- ret ? "LIT" : "BIG");
|
||
|
+ drm_dbg(hdmi, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
@@ -592,7 +591,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
ret = rockchip_hdmi_parse_dt(hdmi);
|
||
|
if (ret) {
|
||
|
if (ret != -EPROBE_DEFER)
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
|
||
|
+ drm_err(hdmi, "Unable to parse OF data\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
@@ -600,26 +599,25 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
if (IS_ERR(hdmi->phy)) {
|
||
|
ret = PTR_ERR(hdmi->phy);
|
||
|
if (ret != -EPROBE_DEFER)
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
|
||
|
+ drm_err(hdmi, "failed to get phy\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ret = regulator_enable(hdmi->avdd_0v9);
|
||
|
if (ret) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
|
||
|
+ drm_err(hdmi, "failed to enable avdd0v9: %d\n", ret);
|
||
|
goto err_avdd_0v9;
|
||
|
}
|
||
|
|
||
|
ret = regulator_enable(hdmi->avdd_1v8);
|
||
|
if (ret) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
|
||
|
+ drm_err(hdmi, "failed to enable avdd1v8: %d\n", ret);
|
||
|
goto err_avdd_1v8;
|
||
|
}
|
||
|
|
||
|
ret = clk_prepare_enable(hdmi->ref_clk);
|
||
|
if (ret) {
|
||
|
- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
|
||
|
- ret);
|
||
|
+ drm_err(hdmi, "Failed to enable HDMI reference clock: %d\n", ret);
|
||
|
goto err_clk;
|
||
|
}
|
||
|
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 14a35060796b9c44c63e0155cc777a59601e8fa1 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Tue, 7 May 2024 19:16:01 +0300
|
||
|
Subject: [PATCH 46/54] drm/rockchip: dw_hdmi: Simplify clock handling
|
||
|
|
||
|
Make use of devm_clk_get_optional_enabled() to replace devm_clk_get()
|
||
|
and clk_prepare_enable() for ref_clk and drop the now unnecessary calls
|
||
|
to clk_disable_unprepare().
|
||
|
|
||
|
Additionally, use devm_clk_get_optional() helper for grf_clk to replace
|
||
|
the open coding call to devm_clk_get() followed by the -ENOENT test.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 38 ++++++++-------------
|
||
|
1 file changed, 14 insertions(+), 24 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
index 2509ce19313f..7d07039ef096 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
@@ -209,6 +209,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
|
||
|
static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
{
|
||
|
struct device_node *np = hdmi->dev->of_node;
|
||
|
+ int ret;
|
||
|
|
||
|
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||
|
if (IS_ERR(hdmi->regmap)) {
|
||
|
@@ -216,25 +217,23 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
return PTR_ERR(hdmi->regmap);
|
||
|
}
|
||
|
|
||
|
- hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
|
||
|
+ hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
|
||
|
if (!hdmi->ref_clk)
|
||
|
- hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");
|
||
|
+ hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "vpll");
|
||
|
|
||
|
- if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
|
||
|
- return -EPROBE_DEFER;
|
||
|
- } else if (IS_ERR(hdmi->ref_clk)) {
|
||
|
- drm_err(hdmi, "failed to get reference clock\n");
|
||
|
- return PTR_ERR(hdmi->ref_clk);
|
||
|
+ if (IS_ERR(hdmi->ref_clk)) {
|
||
|
+ ret = PTR_ERR(hdmi->ref_clk);
|
||
|
+ if (ret != -EPROBE_DEFER)
|
||
|
+ drm_err(hdmi, "failed to get reference clock\n");
|
||
|
+ return ret;
|
||
|
}
|
||
|
|
||
|
- hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
|
||
|
- if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
|
||
|
- hdmi->grf_clk = NULL;
|
||
|
- } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
|
||
|
- return -EPROBE_DEFER;
|
||
|
- } else if (IS_ERR(hdmi->grf_clk)) {
|
||
|
- drm_err(hdmi, "failed to get grf clock\n");
|
||
|
- return PTR_ERR(hdmi->grf_clk);
|
||
|
+ hdmi->grf_clk = devm_clk_get_optional(hdmi->dev, "grf");
|
||
|
+ if (IS_ERR(hdmi->grf_clk)) {
|
||
|
+ ret = PTR_ERR(hdmi->grf_clk);
|
||
|
+ if (ret != -EPROBE_DEFER)
|
||
|
+ drm_err(hdmi, "failed to get grf clock\n");
|
||
|
+ return ret;
|
||
|
}
|
||
|
|
||
|
hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
|
||
|
@@ -615,12 +614,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
goto err_avdd_1v8;
|
||
|
}
|
||
|
|
||
|
- ret = clk_prepare_enable(hdmi->ref_clk);
|
||
|
- if (ret) {
|
||
|
- drm_err(hdmi, "Failed to enable HDMI reference clock: %d\n", ret);
|
||
|
- goto err_clk;
|
||
|
- }
|
||
|
-
|
||
|
if (hdmi->chip_data == &rk3568_chip_data) {
|
||
|
regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
|
||
|
HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
|
||
|
@@ -649,8 +642,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
|
||
|
err_bind:
|
||
|
drm_encoder_cleanup(encoder);
|
||
|
- clk_disable_unprepare(hdmi->ref_clk);
|
||
|
-err_clk:
|
||
|
regulator_disable(hdmi->avdd_1v8);
|
||
|
err_avdd_1v8:
|
||
|
regulator_disable(hdmi->avdd_0v9);
|
||
|
@@ -665,7 +656,6 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
|
||
|
|
||
|
dw_hdmi_unbind(hdmi->hdmi);
|
||
|
drm_encoder_cleanup(&hdmi->encoder.encoder);
|
||
|
- clk_disable_unprepare(hdmi->ref_clk);
|
||
|
|
||
|
regulator_disable(hdmi->avdd_1v8);
|
||
|
regulator_disable(hdmi->avdd_0v9);
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 6ba21ebba361b107956768931a60367dab071fdd Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Wed, 15 May 2024 02:24:03 +0300
|
||
|
Subject: [PATCH 47/54] drm/rockchip: dw_hdmi: Use devm_regulator_get_enable()
|
||
|
|
||
|
The regulators are only enabled at bind() and disabled at unbind(),
|
||
|
hence replace the boilerplate code by making use of
|
||
|
devm_regulator_get_enable() helper.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 34 ++++-----------------
|
||
|
1 file changed, 6 insertions(+), 28 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
index 7d07039ef096..edfd877c98fc 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
@@ -77,8 +77,6 @@ struct rockchip_hdmi {
|
||
|
struct clk *ref_clk;
|
||
|
struct clk *grf_clk;
|
||
|
struct dw_hdmi *hdmi;
|
||
|
- struct regulator *avdd_0v9;
|
||
|
- struct regulator *avdd_1v8;
|
||
|
struct phy *phy;
|
||
|
};
|
||
|
|
||
|
@@ -236,15 +234,13 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
- hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
|
||
|
- if (IS_ERR(hdmi->avdd_0v9))
|
||
|
- return PTR_ERR(hdmi->avdd_0v9);
|
||
|
+ ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9");
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
|
||
|
- hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
|
||
|
- if (IS_ERR(hdmi->avdd_1v8))
|
||
|
- return PTR_ERR(hdmi->avdd_1v8);
|
||
|
+ ret = devm_regulator_get_enable(hdmi->dev, "avdd-1v8");
|
||
|
|
||
|
- return 0;
|
||
|
+ return ret;
|
||
|
}
|
||
|
|
||
|
static enum drm_mode_status
|
||
|
@@ -602,18 +598,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
- ret = regulator_enable(hdmi->avdd_0v9);
|
||
|
- if (ret) {
|
||
|
- drm_err(hdmi, "failed to enable avdd0v9: %d\n", ret);
|
||
|
- goto err_avdd_0v9;
|
||
|
- }
|
||
|
-
|
||
|
- ret = regulator_enable(hdmi->avdd_1v8);
|
||
|
- if (ret) {
|
||
|
- drm_err(hdmi, "failed to enable avdd1v8: %d\n", ret);
|
||
|
- goto err_avdd_1v8;
|
||
|
- }
|
||
|
-
|
||
|
if (hdmi->chip_data == &rk3568_chip_data) {
|
||
|
regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
|
||
|
HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
|
||
|
@@ -642,10 +626,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
|
||
|
err_bind:
|
||
|
drm_encoder_cleanup(encoder);
|
||
|
- regulator_disable(hdmi->avdd_1v8);
|
||
|
-err_avdd_1v8:
|
||
|
- regulator_disable(hdmi->avdd_0v9);
|
||
|
-err_avdd_0v9:
|
||
|
+
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
@@ -656,9 +637,6 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
|
||
|
|
||
|
dw_hdmi_unbind(hdmi->hdmi);
|
||
|
drm_encoder_cleanup(&hdmi->encoder.encoder);
|
||
|
-
|
||
|
- regulator_disable(hdmi->avdd_1v8);
|
||
|
- regulator_disable(hdmi->avdd_0v9);
|
||
|
}
|
||
|
|
||
|
static const struct component_ops dw_hdmi_rockchip_ops = {
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 0de628fab9d6fa101e6b973599a89fdc84504f73 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 27 May 2024 22:38:38 +0300
|
||
|
Subject: [PATCH 48/54] drm/rockchip: dw_hdmi: Drop useless assignments of
|
||
|
mpll_cfg, cur_ctr, phy_config
|
||
|
|
||
|
The mpll_cfg, cur_ctr and phy_config members in struct dw_hdmi_plat_data
|
||
|
are only used to configure the Synopsys PHYs supported internally by DW
|
||
|
HDMI transmitter driver (gpu/drm/bridge/synopsys/dw-hdmi.c), via
|
||
|
hdmi_phy_configure_dwc_hdmi_3d_tx(), which is further invoked from
|
||
|
dw_hdmi_phy_init(). This is part of the internal
|
||
|
dw_hdmi_synopsys_phy_ops struct, setup in dw_hdmi_detect_phy().
|
||
|
|
||
|
To handle vendor PHYs, the DW HDMI driver doesn't use the internal PHY
|
||
|
ops, but expects the glue layers to provide the phy_ops and phy_name
|
||
|
members of struct dw_hdmi_plat_data.
|
||
|
|
||
|
Drop the unnecessary assignments of DW internal PHY related members from
|
||
|
structs rk3228_hdmi_drv_data and rk3328_hdmi_drv_data, since both set
|
||
|
the phy_force_vendor flag and provide the expected vendor PHY data.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 6 ------
|
||
|
1 file changed, 6 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
index edfd877c98fc..ca6728a43159 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
@@ -444,9 +444,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
|
||
|
|
||
|
static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
|
||
|
.mode_valid = dw_hdmi_rockchip_mode_valid,
|
||
|
- .mpll_cfg = rockchip_mpll_cfg,
|
||
|
- .cur_ctr = rockchip_cur_ctr,
|
||
|
- .phy_config = rockchip_phy_config,
|
||
|
.phy_data = &rk3228_chip_data,
|
||
|
.phy_ops = &rk3228_hdmi_phy_ops,
|
||
|
.phy_name = "inno_dw_hdmi_phy2",
|
||
|
@@ -481,9 +478,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
|
||
|
|
||
|
static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
|
||
|
.mode_valid = dw_hdmi_rockchip_mode_valid,
|
||
|
- .mpll_cfg = rockchip_mpll_cfg,
|
||
|
- .cur_ctr = rockchip_cur_ctr,
|
||
|
- .phy_config = rockchip_phy_config,
|
||
|
.phy_data = &rk3328_chip_data,
|
||
|
.phy_ops = &rk3328_hdmi_phy_ops,
|
||
|
.phy_name = "inno_dw_hdmi_phy2",
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 73bf7e4a35984bca8b89f4acb277f45c0786f7b9 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Thu, 28 Mar 2024 00:47:03 +0200
|
||
|
Subject: [PATCH 49/54] dt-bindings: display: rockchip,dw-hdmi: Add compatible
|
||
|
for RK3588
|
||
|
|
||
|
Document the DW HDMI TX Controller found on Rockchip RK3588 SoC.
|
||
|
|
||
|
Since RK3588 uses different clocks than previous Rockchip SoCs and also
|
||
|
requires a couple of reset lines and some additional properties, provide
|
||
|
the required changes in the binding to be able to handle all variants.
|
||
|
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
.../display/rockchip/rockchip,dw-hdmi.yaml | 124 +++++++++++++-----
|
||
|
1 file changed, 89 insertions(+), 35 deletions(-)
|
||
|
|
||
|
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
|
||
|
index 2aac62219ff6..651189de7af5 100644
|
||
|
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
|
||
|
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
|
||
|
@@ -12,10 +12,8 @@ maintainers:
|
||
|
description: |
|
||
|
The HDMI transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
|
||
|
with a companion PHY IP.
|
||
|
-
|
||
|
-allOf:
|
||
|
- - $ref: ../bridge/synopsys,dw-hdmi.yaml#
|
||
|
- - $ref: /schemas/sound/dai-common.yaml#
|
||
|
+ RK3588 SoC updated the TX controller IP to DW HDMI 2.1 Quad-Pixel (QP),
|
||
|
+ while making use of a HDMI/eDP TX Combo PHY based on a Samsung IP block.
|
||
|
|
||
|
properties:
|
||
|
compatible:
|
||
|
@@ -25,6 +23,7 @@ properties:
|
||
|
- rockchip,rk3328-dw-hdmi
|
||
|
- rockchip,rk3399-dw-hdmi
|
||
|
- rockchip,rk3568-dw-hdmi
|
||
|
+ - rockchip,rk3588-dw-hdmi
|
||
|
|
||
|
reg-io-width:
|
||
|
const: 4
|
||
|
@@ -40,36 +39,6 @@ properties:
|
||
|
A 1.8V supply that powers up the SoC internal circuitry. The pin name on the
|
||
|
SoC usually is HDMI_TX_AVDD_1V8.
|
||
|
|
||
|
- clocks:
|
||
|
- minItems: 2
|
||
|
- items:
|
||
|
- - {}
|
||
|
- - {}
|
||
|
- # The next three clocks are all optional, but shall be specified in this
|
||
|
- # order when present.
|
||
|
- - description: The HDMI CEC controller main clock
|
||
|
- - description: Power for GRF IO
|
||
|
- - description: External clock for some HDMI PHY (old clock name, deprecated)
|
||
|
- - description: External clock for some HDMI PHY (new name)
|
||
|
-
|
||
|
- clock-names:
|
||
|
- minItems: 2
|
||
|
- items:
|
||
|
- - {}
|
||
|
- - {}
|
||
|
- - enum:
|
||
|
- - cec
|
||
|
- - grf
|
||
|
- - vpll
|
||
|
- - ref
|
||
|
- - enum:
|
||
|
- - grf
|
||
|
- - vpll
|
||
|
- - ref
|
||
|
- - enum:
|
||
|
- - vpll
|
||
|
- - ref
|
||
|
-
|
||
|
ddc-i2c-bus:
|
||
|
$ref: /schemas/types.yaml#/definitions/phandle
|
||
|
description:
|
||
|
@@ -131,13 +100,98 @@ properties:
|
||
|
required:
|
||
|
- compatible
|
||
|
- reg
|
||
|
- - reg-io-width
|
||
|
- clocks
|
||
|
- clock-names
|
||
|
- interrupts
|
||
|
- ports
|
||
|
- rockchip,grf
|
||
|
|
||
|
+allOf:
|
||
|
+ - $ref: /schemas/sound/dai-common.yaml#
|
||
|
+ - if:
|
||
|
+ properties:
|
||
|
+ compatible:
|
||
|
+ contains:
|
||
|
+ enum:
|
||
|
+ - rockchip,rk3588-dw-hdmi
|
||
|
+ then:
|
||
|
+ properties:
|
||
|
+ reg:
|
||
|
+ maxItems: 1
|
||
|
+
|
||
|
+ clocks:
|
||
|
+ minItems: 1
|
||
|
+ items:
|
||
|
+ - description: APB system interface clock
|
||
|
+ # The next clocks are optional, but shall be specified in this
|
||
|
+ # order when present.
|
||
|
+ - description: TMDS/FRL link clock
|
||
|
+ - description: EARC RX biphase clock
|
||
|
+ - description: Reference clock
|
||
|
+ - description: Audio interface clock
|
||
|
+ - description: Video datapath clock
|
||
|
+
|
||
|
+ clock-names:
|
||
|
+ minItems: 1
|
||
|
+ items:
|
||
|
+ - const: pclk
|
||
|
+ - enum: [hdp, earc, ref, aud, hclk_vo1]
|
||
|
+ - enum: [earc, ref, aud, hclk_vo1]
|
||
|
+ - enum: [ref, aud, hclk_vo1]
|
||
|
+ - enum: [aud, hclk_vo1]
|
||
|
+ - const: hclk_vo1
|
||
|
+
|
||
|
+ resets:
|
||
|
+ minItems: 2
|
||
|
+ maxItems: 2
|
||
|
+
|
||
|
+ reset-names:
|
||
|
+ items:
|
||
|
+ - const: ref
|
||
|
+ - const: hdp
|
||
|
+
|
||
|
+ interrupts:
|
||
|
+ minItems: 1
|
||
|
+ maxItems: 5
|
||
|
+
|
||
|
+ rockchip,vo1_grf:
|
||
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
||
|
+ description:
|
||
|
+ phandle to the VO1 GRF
|
||
|
+
|
||
|
+ required:
|
||
|
+ - resets
|
||
|
+ - reset-names
|
||
|
+ - rockchip,vo1_grf
|
||
|
+
|
||
|
+ else:
|
||
|
+ $ref: ../bridge/synopsys,dw-hdmi.yaml#
|
||
|
+
|
||
|
+ properties:
|
||
|
+ clocks:
|
||
|
+ minItems: 2
|
||
|
+ items:
|
||
|
+ - {}
|
||
|
+ - {}
|
||
|
+ # The next three clocks are all optional, but shall be specified in this
|
||
|
+ # order when present.
|
||
|
+ - description: The HDMI CEC controller main clock
|
||
|
+ - description: Power for GRF IO
|
||
|
+ - description: External clock for some HDMI PHY (old clock name, deprecated)
|
||
|
+ - description: External clock for some HDMI PHY (new name)
|
||
|
+
|
||
|
+ clock-names:
|
||
|
+ minItems: 2
|
||
|
+ items:
|
||
|
+ - {}
|
||
|
+ - {}
|
||
|
+ - enum: [cec, grf, vpll, ref]
|
||
|
+ - enum: [grf, vpll, ref]
|
||
|
+ - enum: [vpll, ref]
|
||
|
+
|
||
|
+ required:
|
||
|
+ - reg-io-width
|
||
|
+
|
||
|
unevaluatedProperties: false
|
||
|
|
||
|
examples:
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 3c174706f556aa659210015116982f06b34b8dda Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 20 May 2024 14:49:50 +0300
|
||
|
Subject: [PATCH 50/54] drm/bridge: synopsys: Add DW HDMI QP TX controller
|
||
|
driver
|
||
|
|
||
|
The Synopsys DesignWare HDMI 2.1 Quad-Pixel (QP) TX controller supports
|
||
|
the following features, among others:
|
||
|
|
||
|
* Fixed Rate Link (FRL)
|
||
|
* 4K@120Hz and 8K@60Hz video modes
|
||
|
* Variable Refresh Rate (VRR) including Quick Media Switching (QMS), aka
|
||
|
Cinema VRR
|
||
|
* Fast Vactive (FVA), aka Quick Frame Transport (QFT)
|
||
|
* SCDC I2C DDC access
|
||
|
* TMDS Scrambler enabling 2160p@60Hz with RGB/YCbCr4:4:4
|
||
|
* YCbCr4:2:0 enabling 2160p@60Hz at lower HDMI link speeds
|
||
|
* Multi-stream audio
|
||
|
* Enhanced Audio Return Channel (EARC)
|
||
|
|
||
|
Add driver to enable basic support, i.e. RGB output up to 4K@60Hz,
|
||
|
without audio, CEC or any of the HDMI 2.1 specific features.
|
||
|
|
||
|
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
|
||
|
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/bridge/synopsys/Makefile | 2 +-
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 796 ++++++++++++++++++
|
||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 831 +++++++++++++++++++
|
||
|
include/drm/bridge/dw_hdmi.h | 8 +
|
||
|
4 files changed, 1636 insertions(+), 1 deletion(-)
|
||
|
create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
|
||
|
create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile
|
||
|
index ce715562e9e5..8354e4879f70 100644
|
||
|
--- a/drivers/gpu/drm/bridge/synopsys/Makefile
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/Makefile
|
||
|
@@ -1,5 +1,5 @@
|
||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||
|
-obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
|
||
|
+obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-qp.o
|
||
|
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
|
||
|
obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o
|
||
|
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
|
||
|
new file mode 100644
|
||
|
index 000000000000..09a4bf7246d6
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
|
||
|
@@ -0,0 +1,796 @@
|
||
|
+// SPDX-License-Identifier: GPL-2.0+
|
||
|
+/*
|
||
|
+ * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
|
||
|
+ * Copyright (c) 2024 Collabora Ltd.
|
||
|
+ *
|
||
|
+ * Author: Algea Cao <algea.cao@rock-chips.com>
|
||
|
+ * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
+ */
|
||
|
+#include <linux/hdmi.h>
|
||
|
+#include <linux/irq.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/of.h>
|
||
|
+
|
||
|
+#include <drm/display/drm_hdmi_helper.h>
|
||
|
+#include <drm/display/drm_scdc_helper.h>
|
||
|
+#include <drm/drm_atomic.h>
|
||
|
+#include <drm/drm_atomic_helper.h>
|
||
|
+#include <drm/drm_edid.h>
|
||
|
+#include <drm/drm_of.h>
|
||
|
+#include <drm/drm_print.h>
|
||
|
+#include <drm/drm_probe_helper.h>
|
||
|
+
|
||
|
+#include <uapi/linux/media-bus-format.h>
|
||
|
+#include <uapi/linux/videodev2.h>
|
||
|
+
|
||
|
+#include "dw-hdmi-common.h"
|
||
|
+#include "dw-hdmi-qp.h"
|
||
|
+
|
||
|
+static void dw_hdmi_qp_write(struct dw_hdmi *hdmi, unsigned int val, int offset)
|
||
|
+{
|
||
|
+ regmap_write(hdmi->regm, offset, val);
|
||
|
+}
|
||
|
+
|
||
|
+static unsigned int dw_hdmi_qp_read(struct dw_hdmi *hdmi, int offset)
|
||
|
+{
|
||
|
+ unsigned int val = 0;
|
||
|
+
|
||
|
+ regmap_read(hdmi->regm, offset, &val);
|
||
|
+
|
||
|
+ return val;
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_mod(struct dw_hdmi *hdmi, unsigned int data,
|
||
|
+ unsigned int mask, unsigned int reg)
|
||
|
+{
|
||
|
+ regmap_update_bits(hdmi->regm, reg, mask, data);
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_i2c_init(struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ /* Software reset */
|
||
|
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||
|
+
|
||
|
+ dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
|
||
|
+
|
||
|
+ /* Clear DONE and ERROR interrupts */
|
||
|
+ dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR,
|
||
|
+ MAINUNIT_1_INT_CLEAR);
|
||
|
+}
|
||
|
+
|
||
|
+static int dw_hdmi_qp_i2c_read(struct dw_hdmi *hdmi,
|
||
|
+ unsigned char *buf, unsigned int length)
|
||
|
+{
|
||
|
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
|
||
|
+ int stat;
|
||
|
+
|
||
|
+ if (!i2c->is_regaddr) {
|
||
|
+ dev_dbg(hdmi->dev, "set read register address to 0\n");
|
||
|
+ i2c->slave_reg = 0x00;
|
||
|
+ i2c->is_regaddr = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ while (length--) {
|
||
|
+ reinit_completion(&i2c->cmp);
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
|
||
|
+ I2CM_INTERFACE_CONTROL0);
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, I2CM_FM_READ, I2CM_WR_MASK,
|
||
|
+ I2CM_INTERFACE_CONTROL0);
|
||
|
+
|
||
|
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
|
||
|
+ if (!stat) {
|
||
|
+ dev_err(hdmi->dev, "i2c read timed out\n");
|
||
|
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||
|
+ return -EAGAIN;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Check for error condition on the bus */
|
||
|
+ if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
|
||
|
+ dev_err(hdmi->dev, "i2c read error\n");
|
||
|
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||
|
+ return -EIO;
|
||
|
+ }
|
||
|
+
|
||
|
+ *buf++ = dw_hdmi_qp_read(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff;
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
|
||
|
+ }
|
||
|
+
|
||
|
+ i2c->is_segment = false;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int dw_hdmi_qp_i2c_write(struct dw_hdmi *hdmi,
|
||
|
+ unsigned char *buf, unsigned int length)
|
||
|
+{
|
||
|
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
|
||
|
+ int stat;
|
||
|
+
|
||
|
+ if (!i2c->is_regaddr) {
|
||
|
+ /* Use the first write byte as register address */
|
||
|
+ i2c->slave_reg = buf[0];
|
||
|
+ length--;
|
||
|
+ buf++;
|
||
|
+ i2c->is_regaddr = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ while (length--) {
|
||
|
+ reinit_completion(&i2c->cmp);
|
||
|
+
|
||
|
+ dw_hdmi_qp_write(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3);
|
||
|
+ dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
|
||
|
+ I2CM_INTERFACE_CONTROL0);
|
||
|
+ dw_hdmi_qp_mod(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK,
|
||
|
+ I2CM_INTERFACE_CONTROL0);
|
||
|
+
|
||
|
+ stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
|
||
|
+ if (!stat) {
|
||
|
+ dev_err(hdmi->dev, "i2c write time out!\n");
|
||
|
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||
|
+ return -EAGAIN;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Check for error condition on the bus */
|
||
|
+ if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
|
||
|
+ dev_err(hdmi->dev, "i2c write nack!\n");
|
||
|
+ dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||
|
+ return -EIO;
|
||
|
+ }
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int dw_hdmi_qp_i2c_xfer(struct i2c_adapter *adap,
|
||
|
+ struct i2c_msg *msgs, int num)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi = i2c_get_adapdata(adap);
|
||
|
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
|
||
|
+ u8 addr = msgs[0].addr;
|
||
|
+ int i, ret = 0;
|
||
|
+
|
||
|
+ if (addr == DDC_CI_ADDR)
|
||
|
+ /*
|
||
|
+ * The internal I2C controller does not support the multi-byte
|
||
|
+ * read and write operations needed for DDC/CI.
|
||
|
+ * TOFIX: Blacklist the DDC/CI address until we filter out
|
||
|
+ * unsupported I2C operations.
|
||
|
+ */
|
||
|
+ return -EOPNOTSUPP;
|
||
|
+
|
||
|
+ for (i = 0; i < num; i++) {
|
||
|
+ if (msgs[i].len == 0) {
|
||
|
+ dev_err(hdmi->dev,
|
||
|
+ "unsupported transfer %d/%d, no data\n",
|
||
|
+ i + 1, num);
|
||
|
+ return -EOPNOTSUPP;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ mutex_lock(&i2c->lock);
|
||
|
+
|
||
|
+ /* Unmute DONE and ERROR interrupts */
|
||
|
+ dw_hdmi_qp_mod(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
|
||
|
+ I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N,
|
||
|
+ MAINUNIT_1_INT_MASK_N);
|
||
|
+
|
||
|
+ /* Set slave device address taken from the first I2C message */
|
||
|
+ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
|
||
|
+ addr = DDC_ADDR;
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0);
|
||
|
+
|
||
|
+ /* Set slave device register address on transfer */
|
||
|
+ i2c->is_regaddr = false;
|
||
|
+
|
||
|
+ /* Set segment pointer for I2C extended read mode operation */
|
||
|
+ i2c->is_segment = false;
|
||
|
+
|
||
|
+ for (i = 0; i < num; i++) {
|
||
|
+ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
|
||
|
+ i2c->is_segment = true;
|
||
|
+ dw_hdmi_qp_mod(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR,
|
||
|
+ I2CM_INTERFACE_CONTROL1);
|
||
|
+ dw_hdmi_qp_mod(hdmi, *msgs[i].buf, I2CM_SEG_PTR,
|
||
|
+ I2CM_INTERFACE_CONTROL1);
|
||
|
+ } else {
|
||
|
+ if (msgs[i].flags & I2C_M_RD)
|
||
|
+ ret = dw_hdmi_qp_i2c_read(hdmi, msgs[i].buf,
|
||
|
+ msgs[i].len);
|
||
|
+ else
|
||
|
+ ret = dw_hdmi_qp_i2c_write(hdmi, msgs[i].buf,
|
||
|
+ msgs[i].len);
|
||
|
+ }
|
||
|
+ if (ret < 0)
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!ret)
|
||
|
+ ret = num;
|
||
|
+
|
||
|
+ /* Mute DONE and ERROR interrupts */
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N,
|
||
|
+ MAINUNIT_1_INT_MASK_N);
|
||
|
+
|
||
|
+ mutex_unlock(&i2c->lock);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static u32 dw_hdmi_qp_i2c_func(struct i2c_adapter *adapter)
|
||
|
+{
|
||
|
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct i2c_algorithm dw_hdmi_algorithm = {
|
||
|
+ .master_xfer = dw_hdmi_qp_i2c_xfer,
|
||
|
+ .functionality = dw_hdmi_qp_i2c_func,
|
||
|
+};
|
||
|
+
|
||
|
+/* -----------------------------------------------------------------------------
|
||
|
+ * HDMI TX Setup
|
||
|
+ */
|
||
|
+
|
||
|
+static void hdmi_infoframe_set_checksum(u8 *ptr, int size)
|
||
|
+{
|
||
|
+ u8 csum = 0;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ ptr[3] = 0;
|
||
|
+ /* compute checksum */
|
||
|
+ for (i = 0; i < size; i++)
|
||
|
+ csum += ptr[i];
|
||
|
+
|
||
|
+ ptr[3] = 256 - csum;
|
||
|
+}
|
||
|
+
|
||
|
+static void hdmi_config_AVI(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector *connector,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ struct hdmi_avi_infoframe frame;
|
||
|
+ u32 val, i, j;
|
||
|
+ u8 buf[17];
|
||
|
+
|
||
|
+ dw_hdmi_prep_avi_infoframe(&frame, hdmi, connector, mode);
|
||
|
+
|
||
|
+ frame.scan_mode = HDMI_SCAN_MODE_NONE;
|
||
|
+ frame.video_code = hdmi->vic;
|
||
|
+
|
||
|
+ hdmi_avi_infoframe_pack_only(&frame, buf, 17);
|
||
|
+
|
||
|
+ /* mode which vic >= 128 must use avi version 3 */
|
||
|
+ if (hdmi->vic >= 128) {
|
||
|
+ frame.version = 3;
|
||
|
+ buf[1] = frame.version;
|
||
|
+ buf[4] &= 0x1f;
|
||
|
+ buf[4] |= ((frame.colorspace & 0x7) << 5);
|
||
|
+ buf[7] = frame.video_code;
|
||
|
+ hdmi_infoframe_set_checksum(buf, 17);
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * The Designware IP uses a different byte format from standard
|
||
|
+ * AVI info frames, though generally the bits are in the correct
|
||
|
+ * bytes.
|
||
|
+ */
|
||
|
+
|
||
|
+ val = (frame.version << 8) | (frame.length << 16);
|
||
|
+ dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0);
|
||
|
+
|
||
|
+ for (i = 0; i < 4; i++) {
|
||
|
+ for (j = 0; j < 4; j++) {
|
||
|
+ if (i * 4 + j >= 14)
|
||
|
+ break;
|
||
|
+ if (!j)
|
||
|
+ val = buf[i * 4 + j + 3];
|
||
|
+ val |= buf[i * 4 + j + 3] << (8 * j);
|
||
|
+ }
|
||
|
+
|
||
|
+ dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4);
|
||
|
+ }
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
|
||
|
+ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN);
|
||
|
+}
|
||
|
+
|
||
|
+static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
|
||
|
+ const struct drm_connector *connector)
|
||
|
+{
|
||
|
+ const struct drm_connector_state *conn_state = connector->state;
|
||
|
+ struct hdr_output_metadata *hdr_metadata;
|
||
|
+ struct hdmi_drm_infoframe frame;
|
||
|
+ u8 buffer[30];
|
||
|
+ ssize_t err;
|
||
|
+ int i;
|
||
|
+ u32 val;
|
||
|
+
|
||
|
+ if (!hdmi->plat_data->use_drm_infoframe)
|
||
|
+ return;
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
|
||
|
+
|
||
|
+ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) {
|
||
|
+ dev_dbg(hdmi->dev, "No need to set HDR metadata in infoframe\n");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!conn_state->hdr_output_metadata) {
|
||
|
+ dev_dbg(hdmi->dev, "source metadata not set yet\n");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ hdr_metadata = (struct hdr_output_metadata *)
|
||
|
+ conn_state->hdr_output_metadata->data;
|
||
|
+
|
||
|
+ if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf &
|
||
|
+ BIT(hdr_metadata->hdmi_metadata_type1.eotf))) {
|
||
|
+ dev_err(hdmi->dev, "EOTF %d not supported\n",
|
||
|
+ hdr_metadata->hdmi_metadata_type1.eotf);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
|
||
|
+ if (err < 0)
|
||
|
+ return;
|
||
|
+
|
||
|
+ err = hdmi_drm_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||
|
+ if (err < 0) {
|
||
|
+ dev_err(hdmi->dev, "Failed to pack drm infoframe: %zd\n", err);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ val = (frame.version << 8) | (frame.length << 16);
|
||
|
+ dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0);
|
||
|
+
|
||
|
+ for (i = 0; i <= frame.length; i++) {
|
||
|
+ if (i % 4 == 0)
|
||
|
+ val = buffer[3 + i];
|
||
|
+ val |= buffer[3 + i] << ((i % 4) * 8);
|
||
|
+
|
||
|
+ if (i % 4 == 3 || (i == (frame.length)))
|
||
|
+ dw_hdmi_qp_write(hdmi, val,
|
||
|
+ PKT_DRMI_CONTENTS1 + ((i / 4) * 4));
|
||
|
+ }
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
|
||
|
+ dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN,
|
||
|
+ PKTSCHED_PKT_EN);
|
||
|
+}
|
||
|
+
|
||
|
+static int dw_hdmi_qp_setup(struct dw_hdmi *hdmi,
|
||
|
+ struct drm_connector *connector,
|
||
|
+ struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ u8 bytes = 0;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ dw_hdmi_prep_data(hdmi, mode);
|
||
|
+
|
||
|
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
|
||
|
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
|
||
|
+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * According to the dw-hdmi specification 6.4.2
|
||
|
+ * vp_pr_cd[3:0]:
|
||
|
+ * 0000b: No pixel repetition (pixel sent only once)
|
||
|
+ * 0001b: Pixel sent two times (pixel repeated once)
|
||
|
+ */
|
||
|
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||
|
+ hdmi->hdmi_data.pix_repet_factor = 1;
|
||
|
+
|
||
|
+ /* HDMI Initialization Step B.1 */
|
||
|
+ dw_hdmi_prep_vmode(hdmi, mode);
|
||
|
+
|
||
|
+ /* HDMI Initialization Step B.2 */
|
||
|
+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
|
||
|
+ &connector->display_info,
|
||
|
+ &hdmi->previous_mode);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+ hdmi->phy.enabled = true;
|
||
|
+
|
||
|
+ /* not for DVI mode */
|
||
|
+ if (hdmi->sink_is_hdmi) {
|
||
|
+ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, 0, OPMODE_DVI, LINK_CONFIG0);
|
||
|
+ dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
||
|
+
|
||
|
+ if (hdmi->hdmi_data.video_mode.mtmdsclock > HDMI14_MAX_TMDSCLK) {
|
||
|
+ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) {
|
||
|
+ drm_scdc_readb(hdmi->ddc, SCDC_SINK_VERSION, &bytes);
|
||
|
+ drm_scdc_writeb(hdmi->ddc, SCDC_SOURCE_VERSION,
|
||
|
+ min_t(u8, bytes, SCDC_MIN_SOURCE_VERSION));
|
||
|
+ drm_scdc_set_high_tmds_clock_ratio(connector, 1);
|
||
|
+ drm_scdc_set_scrambling(connector, 1);
|
||
|
+ }
|
||
|
+ dw_hdmi_qp_write(hdmi, 1, SCRAMB_CONFIG0);
|
||
|
+ } else {
|
||
|
+ if (dw_hdmi_support_scdc(hdmi, &connector->display_info)) {
|
||
|
+ drm_scdc_set_high_tmds_clock_ratio(connector, 0);
|
||
|
+ drm_scdc_set_scrambling(connector, 0);
|
||
|
+ }
|
||
|
+ dw_hdmi_qp_write(hdmi, 0, SCRAMB_CONFIG0);
|
||
|
+ }
|
||
|
+
|
||
|
+ /* HDMI Initialization Step F */
|
||
|
+ hdmi_config_AVI(hdmi, connector, mode);
|
||
|
+ hdmi_config_drm_infoframe(hdmi, connector);
|
||
|
+ } else {
|
||
|
+ dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
|
||
|
+
|
||
|
+ dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
||
|
+ dw_hdmi_qp_mod(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0);
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_update_power(struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ int force = hdmi->force;
|
||
|
+
|
||
|
+ if (hdmi->disabled) {
|
||
|
+ force = DRM_FORCE_OFF;
|
||
|
+ } else if (force == DRM_FORCE_UNSPECIFIED) {
|
||
|
+ if (hdmi->rxsense)
|
||
|
+ force = DRM_FORCE_ON;
|
||
|
+ else
|
||
|
+ force = DRM_FORCE_OFF;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (force == DRM_FORCE_OFF) {
|
||
|
+ if (hdmi->bridge_is_on) {
|
||
|
+ if (hdmi->phy.enabled) {
|
||
|
+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
|
||
|
+ hdmi->phy.enabled = false;
|
||
|
+ }
|
||
|
+
|
||
|
+ hdmi->bridge_is_on = false;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ if (!hdmi->bridge_is_on) {
|
||
|
+ hdmi->bridge_is_on = true;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * The curr_conn field is guaranteed to be valid here, as this function
|
||
|
+ * is only be called when !hdmi->disabled.
|
||
|
+ */
|
||
|
+ dw_hdmi_qp_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_connector_force(struct drm_connector *connector)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi =
|
||
|
+ container_of(connector, struct dw_hdmi, connector);
|
||
|
+
|
||
|
+ mutex_lock(&hdmi->mutex);
|
||
|
+ hdmi->force = connector->force;
|
||
|
+ dw_hdmi_qp_update_power(hdmi);
|
||
|
+ mutex_unlock(&hdmi->mutex);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct drm_connector_funcs dw_hdmi_qp_connector_funcs = {
|
||
|
+ .fill_modes = drm_helper_probe_single_connector_modes,
|
||
|
+ .detect = dw_hdmi_connector_detect,
|
||
|
+ .destroy = drm_connector_cleanup,
|
||
|
+ .force = dw_hdmi_qp_connector_force,
|
||
|
+ .reset = drm_atomic_helper_connector_reset,
|
||
|
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||
|
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||
|
+};
|
||
|
+
|
||
|
+static int dw_hdmi_qp_bridge_attach(struct drm_bridge *bridge,
|
||
|
+ enum drm_bridge_attach_flags flags)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
+
|
||
|
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
||
|
+ return drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
|
||
|
+ bridge, flags);
|
||
|
+
|
||
|
+ return dw_hdmi_connector_create(hdmi, &dw_hdmi_qp_connector_funcs);
|
||
|
+}
|
||
|
+
|
||
|
+static enum drm_mode_status
|
||
|
+dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge,
|
||
|
+ const struct drm_display_info *info,
|
||
|
+ const struct drm_display_mode *mode)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
|
||
|
+ enum drm_mode_status mode_status = MODE_OK;
|
||
|
+
|
||
|
+ if (pdata->mode_valid)
|
||
|
+ mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
|
||
|
+ mode);
|
||
|
+
|
||
|
+ return mode_status;
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *old_state)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
+
|
||
|
+ mutex_lock(&hdmi->mutex);
|
||
|
+ hdmi->disabled = true;
|
||
|
+ hdmi->curr_conn = NULL;
|
||
|
+ dw_hdmi_qp_update_power(hdmi);
|
||
|
+ dw_handle_plugged_change(hdmi, false);
|
||
|
+ mutex_unlock(&hdmi->mutex);
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
|
||
|
+ struct drm_bridge_state *old_state)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi = bridge->driver_private;
|
||
|
+ struct drm_atomic_state *state = old_state->base.state;
|
||
|
+ struct drm_connector *connector;
|
||
|
+
|
||
|
+ connector = drm_atomic_get_new_connector_for_encoder(state,
|
||
|
+ bridge->encoder);
|
||
|
+
|
||
|
+ mutex_lock(&hdmi->mutex);
|
||
|
+ hdmi->disabled = false;
|
||
|
+ hdmi->curr_conn = connector;
|
||
|
+ dw_hdmi_qp_update_power(hdmi);
|
||
|
+ dw_handle_plugged_change(hdmi, true);
|
||
|
+ mutex_unlock(&hdmi->mutex);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
||
|
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||
|
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||
|
+ .atomic_reset = drm_atomic_helper_bridge_reset,
|
||
|
+ .attach = dw_hdmi_qp_bridge_attach,
|
||
|
+ .detach = dw_hdmi_bridge_detach,
|
||
|
+ .atomic_check = dw_hdmi_bridge_atomic_check,
|
||
|
+ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
|
||
|
+ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
|
||
|
+ .mode_set = dw_hdmi_bridge_mode_set,
|
||
|
+ .mode_valid = dw_hdmi_qp_bridge_mode_valid,
|
||
|
+ .detect = dw_hdmi_bridge_detect,
|
||
|
+ .edid_read = dw_hdmi_bridge_edid_read,
|
||
|
+};
|
||
|
+
|
||
|
+static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi = dev_id;
|
||
|
+ struct dw_hdmi_i2c *i2c = hdmi->i2c;
|
||
|
+ u32 stat;
|
||
|
+
|
||
|
+ stat = dw_hdmi_qp_read(hdmi, MAINUNIT_1_INT_STATUS);
|
||
|
+
|
||
|
+ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ |
|
||
|
+ I2CM_NACK_RCVD_IRQ);
|
||
|
+
|
||
|
+ if (i2c->stat) {
|
||
|
+ dw_hdmi_qp_write(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR);
|
||
|
+ complete(&i2c->cmp);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (stat)
|
||
|
+ return IRQ_HANDLED;
|
||
|
+
|
||
|
+ return IRQ_NONE;
|
||
|
+}
|
||
|
+
|
||
|
+static int dw_hdmi_qp_detect_phy(struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ if (!hdmi->plat_data->phy_force_vendor) {
|
||
|
+ dev_err(hdmi->dev, "Internal HDMI PHY not supported\n");
|
||
|
+ return -ENODEV;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Vendor PHYs require support from the glue layer. */
|
||
|
+ if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) {
|
||
|
+ dev_err(hdmi->dev,
|
||
|
+ "Vendor HDMI PHY not supported by glue layer\n");
|
||
|
+ return -ENODEV;
|
||
|
+ }
|
||
|
+
|
||
|
+ hdmi->phy.ops = hdmi->plat_data->phy_ops;
|
||
|
+ hdmi->phy.data = hdmi->plat_data->phy_data;
|
||
|
+ hdmi->phy.name = hdmi->plat_data->phy_name;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct regmap_config dw_hdmi_qp_regmap_config = {
|
||
|
+ .reg_bits = 32,
|
||
|
+ .val_bits = 32,
|
||
|
+ .reg_stride = 4,
|
||
|
+ .max_register = EARCRX_1_INT_FORCE,
|
||
|
+};
|
||
|
+
|
||
|
+static void dw_hdmi_qp_init_hw(struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N);
|
||
|
+ dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N);
|
||
|
+ dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0);
|
||
|
+
|
||
|
+ dw_hdmi_qp_i2c_init(hdmi);
|
||
|
+
|
||
|
+ if (hdmi->phy.ops->setup_hpd)
|
||
|
+ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
|
||
|
+}
|
||
|
+
|
||
|
+static struct dw_hdmi *
|
||
|
+dw_hdmi_qp_probe(struct platform_device *pdev,
|
||
|
+ const struct dw_hdmi_plat_data *plat_data)
|
||
|
+{
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
+ struct device_node *np = dev->of_node;
|
||
|
+ struct device_node *ddc_node;
|
||
|
+ struct dw_hdmi *hdmi;
|
||
|
+ struct resource *iores = NULL;
|
||
|
+ int irq, ret;
|
||
|
+
|
||
|
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
|
||
|
+ if (!hdmi)
|
||
|
+ return ERR_PTR(-ENOMEM);
|
||
|
+
|
||
|
+ hdmi->plat_data = plat_data;
|
||
|
+ hdmi->dev = dev;
|
||
|
+ hdmi->disabled = true;
|
||
|
+ hdmi->rxsense = true;
|
||
|
+ hdmi->last_connector_result = connector_status_disconnected;
|
||
|
+
|
||
|
+ mutex_init(&hdmi->mutex);
|
||
|
+ mutex_init(&hdmi->audio_mutex);
|
||
|
+ mutex_init(&hdmi->cec_notifier_mutex);
|
||
|
+ spin_lock_init(&hdmi->audio_lock);
|
||
|
+
|
||
|
+ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
|
||
|
+ if (ddc_node) {
|
||
|
+ hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
|
||
|
+ of_node_put(ddc_node);
|
||
|
+ if (!hdmi->ddc) {
|
||
|
+ dev_dbg(hdmi->dev, "failed to read ddc node\n");
|
||
|
+ return ERR_PTR(-EPROBE_DEFER);
|
||
|
+ }
|
||
|
+
|
||
|
+ } else {
|
||
|
+ dev_dbg(hdmi->dev, "no ddc property found\n");
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!plat_data->regm) {
|
||
|
+ const struct regmap_config *reg_config;
|
||
|
+
|
||
|
+ reg_config = &dw_hdmi_qp_regmap_config;
|
||
|
+
|
||
|
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||
|
+ hdmi->regs = devm_ioremap_resource(dev, iores);
|
||
|
+ if (IS_ERR(hdmi->regs)) {
|
||
|
+ ret = PTR_ERR(hdmi->regs);
|
||
|
+ goto err_res;
|
||
|
+ }
|
||
|
+
|
||
|
+ hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config);
|
||
|
+ if (IS_ERR(hdmi->regm)) {
|
||
|
+ dev_err(dev, "Failed to configure regmap\n");
|
||
|
+ ret = PTR_ERR(hdmi->regm);
|
||
|
+ goto err_res;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ hdmi->regm = plat_data->regm;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Allow SCDC advertising in dw_hdmi_support_scdc() */
|
||
|
+ hdmi->version = 0x200a;
|
||
|
+
|
||
|
+ ret = dw_hdmi_qp_detect_phy(hdmi);
|
||
|
+ if (ret < 0)
|
||
|
+ goto err_res;
|
||
|
+
|
||
|
+ dw_hdmi_qp_init_hw(hdmi);
|
||
|
+
|
||
|
+ if ((dw_hdmi_qp_read(hdmi, CMU_STATUS) & DISPLAY_CLK_MONITOR) ==
|
||
|
+ DISPLAY_CLK_LOCKED)
|
||
|
+ hdmi->disabled = false;
|
||
|
+
|
||
|
+ /* Not handled for now: IRQ0 (AVP), IRQ1 (CEC), IRQ2 (EARC) */
|
||
|
+ irq = platform_get_irq(pdev, 3);
|
||
|
+ if (irq < 0) {
|
||
|
+ ret = irq;
|
||
|
+ goto err_res;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = devm_request_threaded_irq(dev, irq,
|
||
|
+ dw_hdmi_qp_main_hardirq, NULL,
|
||
|
+ IRQF_SHARED, dev_name(dev), hdmi);
|
||
|
+ if (ret)
|
||
|
+ goto err_res;
|
||
|
+
|
||
|
+ /* If DDC bus is not specified, try to register HDMI I2C bus */
|
||
|
+ if (!hdmi->ddc) {
|
||
|
+ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi, &dw_hdmi_algorithm);
|
||
|
+ if (IS_ERR(hdmi->ddc))
|
||
|
+ hdmi->ddc = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ hdmi->bridge.driver_private = hdmi;
|
||
|
+ hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs;
|
||
|
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
|
||
|
+ | DRM_BRIDGE_OP_HPD;
|
||
|
+ hdmi->bridge.ddc = hdmi->ddc;
|
||
|
+ hdmi->bridge.of_node = pdev->dev.of_node;
|
||
|
+ hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
|
||
|
+
|
||
|
+ drm_bridge_add(&hdmi->bridge);
|
||
|
+
|
||
|
+ return hdmi;
|
||
|
+
|
||
|
+err_res:
|
||
|
+ i2c_put_adapter(hdmi->ddc);
|
||
|
+
|
||
|
+ return ERR_PTR(ret);
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_qp_remove(struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ drm_bridge_remove(&hdmi->bridge);
|
||
|
+
|
||
|
+ if (hdmi->audio && !IS_ERR(hdmi->audio))
|
||
|
+ platform_device_unregister(hdmi->audio);
|
||
|
+ if (!IS_ERR(hdmi->cec))
|
||
|
+ platform_device_unregister(hdmi->cec);
|
||
|
+
|
||
|
+ if (hdmi->i2c)
|
||
|
+ i2c_del_adapter(&hdmi->i2c->adap);
|
||
|
+ else
|
||
|
+ i2c_put_adapter(hdmi->ddc);
|
||
|
+}
|
||
|
+
|
||
|
+struct dw_hdmi *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||
|
+ struct drm_encoder *encoder,
|
||
|
+ struct dw_hdmi_plat_data *plat_data)
|
||
|
+{
|
||
|
+ struct dw_hdmi *hdmi;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ hdmi = dw_hdmi_qp_probe(pdev, plat_data);
|
||
|
+ if (IS_ERR(hdmi))
|
||
|
+ return hdmi;
|
||
|
+
|
||
|
+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
|
||
|
+ if (ret) {
|
||
|
+ dw_hdmi_qp_remove(hdmi);
|
||
|
+ return ERR_PTR(ret);
|
||
|
+ }
|
||
|
+
|
||
|
+ return hdmi;
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind);
|
||
|
+
|
||
|
+void dw_hdmi_qp_unbind(struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ dw_hdmi_qp_remove(hdmi);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_unbind);
|
||
|
+
|
||
|
+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi *hdmi)
|
||
|
+{
|
||
|
+ dw_hdmi_qp_init_hw(hdmi);
|
||
|
+}
|
||
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume);
|
||
|
+
|
||
|
+MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
|
||
|
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@collabora.com>");
|
||
|
+MODULE_DESCRIPTION("DW HDMI QP transmitter driver");
|
||
|
+MODULE_LICENSE("GPL");
|
||
|
+MODULE_ALIAS("platform:dw-hdmi-qp");
|
||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
|
||
|
new file mode 100644
|
||
|
index 000000000000..4cac70f2d11d
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
|
||
|
@@ -0,0 +1,831 @@
|
||
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
+/*
|
||
|
+ * Copyright (C) Rockchip Electronics Co.Ltd
|
||
|
+ * Author:
|
||
|
+ * Algea Cao <algea.cao@rock-chips.com>
|
||
|
+ */
|
||
|
+#ifndef __DW_HDMI_QP_H__
|
||
|
+#define __DW_HDMI_QP_H__
|
||
|
+/* Main Unit Registers */
|
||
|
+#define CORE_ID 0x0
|
||
|
+#define VER_NUMBER 0x4
|
||
|
+#define VER_TYPE 0x8
|
||
|
+#define CONFIG_REG 0xc
|
||
|
+#define CONFIG_CEC BIT(28)
|
||
|
+#define CONFIG_AUD_UD BIT(23)
|
||
|
+#define CORE_TIMESTAMP_HHMM 0x14
|
||
|
+#define CORE_TIMESTAMP_MMDD 0x18
|
||
|
+#define CORE_TIMESTAMP_YYYY 0x1c
|
||
|
+/* Reset Manager Registers */
|
||
|
+#define GLOBAL_SWRESET_REQUEST 0x40
|
||
|
+#define EARCRX_CMDC_SWINIT_P BIT(27)
|
||
|
+#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10)
|
||
|
+#define GLOBAL_SWDISABLE 0x44
|
||
|
+#define CEC_SWDISABLE BIT(17)
|
||
|
+#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10)
|
||
|
+#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6)
|
||
|
+#define RESET_MANAGER_CONFIG0 0x48
|
||
|
+#define RESET_MANAGER_STATUS0 0x50
|
||
|
+#define RESET_MANAGER_STATUS1 0x54
|
||
|
+#define RESET_MANAGER_STATUS2 0x58
|
||
|
+/* Timer Base Registers */
|
||
|
+#define TIMER_BASE_CONFIG0 0x80
|
||
|
+#define TIMER_BASE_STATUS0 0x84
|
||
|
+/* CMU Registers */
|
||
|
+#define CMU_CONFIG0 0xa0
|
||
|
+#define CMU_CONFIG1 0xa4
|
||
|
+#define CMU_CONFIG2 0xa8
|
||
|
+#define CMU_CONFIG3 0xac
|
||
|
+#define CMU_STATUS 0xb0
|
||
|
+#define DISPLAY_CLK_MONITOR 0x3f
|
||
|
+#define DISPLAY_CLK_LOCKED 0X15
|
||
|
+#define EARC_BPCLK_OFF BIT(9)
|
||
|
+#define AUDCLK_OFF BIT(7)
|
||
|
+#define LINKQPCLK_OFF BIT(5)
|
||
|
+#define VIDQPCLK_OFF BIT(3)
|
||
|
+#define IPI_CLK_OFF BIT(1)
|
||
|
+#define CMU_IPI_CLK_FREQ 0xb4
|
||
|
+#define CMU_VIDQPCLK_FREQ 0xb8
|
||
|
+#define CMU_LINKQPCLK_FREQ 0xbc
|
||
|
+#define CMU_AUDQPCLK_FREQ 0xc0
|
||
|
+#define CMU_EARC_BPCLK_FREQ 0xc4
|
||
|
+/* I2CM Registers */
|
||
|
+#define I2CM_SM_SCL_CONFIG0 0xe0
|
||
|
+#define I2CM_FM_SCL_CONFIG0 0xe4
|
||
|
+#define I2CM_CONFIG0 0xe8
|
||
|
+#define I2CM_CONTROL0 0xec
|
||
|
+#define I2CM_STATUS0 0xf0
|
||
|
+#define I2CM_INTERFACE_CONTROL0 0xf4
|
||
|
+#define I2CM_ADDR 0xff000
|
||
|
+#define I2CM_SLVADDR 0xfe0
|
||
|
+#define I2CM_WR_MASK 0x1e
|
||
|
+#define I2CM_EXT_READ BIT(4)
|
||
|
+#define I2CM_SHORT_READ BIT(3)
|
||
|
+#define I2CM_FM_READ BIT(2)
|
||
|
+#define I2CM_FM_WRITE BIT(1)
|
||
|
+#define I2CM_FM_EN BIT(0)
|
||
|
+#define I2CM_INTERFACE_CONTROL1 0xf8
|
||
|
+#define I2CM_SEG_PTR 0x7f80
|
||
|
+#define I2CM_SEG_ADDR 0x7f
|
||
|
+#define I2CM_INTERFACE_WRDATA_0_3 0xfc
|
||
|
+#define I2CM_INTERFACE_WRDATA_4_7 0x100
|
||
|
+#define I2CM_INTERFACE_WRDATA_8_11 0x104
|
||
|
+#define I2CM_INTERFACE_WRDATA_12_15 0x108
|
||
|
+#define I2CM_INTERFACE_RDDATA_0_3 0x10c
|
||
|
+#define I2CM_INTERFACE_RDDATA_4_7 0x110
|
||
|
+#define I2CM_INTERFACE_RDDATA_8_11 0x114
|
||
|
+#define I2CM_INTERFACE_RDDATA_12_15 0x118
|
||
|
+/* SCDC Registers */
|
||
|
+#define SCDC_CONFIG0 0x140
|
||
|
+#define SCDC_I2C_FM_EN BIT(12)
|
||
|
+#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6)
|
||
|
+#define SCDC_UPD_FLAGS_POLL_EN BIT(4)
|
||
|
+#define SCDC_CONTROL0 0x148
|
||
|
+#define SCDC_STATUS0 0x150
|
||
|
+#define STATUS_UPDATE BIT(0)
|
||
|
+#define FRL_START BIT(4)
|
||
|
+#define FLT_UPDATE BIT(5)
|
||
|
+/* FLT Registers */
|
||
|
+#define FLT_CONFIG0 0x160
|
||
|
+#define FLT_CONFIG1 0x164
|
||
|
+#define FLT_CONFIG2 0x168
|
||
|
+#define FLT_CONTROL0 0x170
|
||
|
+/* Main Unit 2 Registers */
|
||
|
+#define MAINUNIT_STATUS0 0x180
|
||
|
+/* Video Interface Registers */
|
||
|
+#define VIDEO_INTERFACE_CONFIG0 0x800
|
||
|
+#define VIDEO_INTERFACE_CONFIG1 0x804
|
||
|
+#define VIDEO_INTERFACE_CONFIG2 0x808
|
||
|
+#define VIDEO_INTERFACE_CONTROL0 0x80c
|
||
|
+#define VIDEO_INTERFACE_STATUS0 0x814
|
||
|
+/* Video Packing Registers */
|
||
|
+#define VIDEO_PACKING_CONFIG0 0x81c
|
||
|
+/* Audio Interface Registers */
|
||
|
+#define AUDIO_INTERFACE_CONFIG0 0x820
|
||
|
+#define AUD_IF_SEL_MSK 0x3
|
||
|
+#define AUD_IF_SPDIF 0x2
|
||
|
+#define AUD_IF_I2S 0x1
|
||
|
+#define AUD_IF_PAI 0x0
|
||
|
+#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2)
|
||
|
+#define AUD_FIFO_INIT_ON_OVF_EN BIT(2)
|
||
|
+#define I2S_LINES_EN_MSK GENMASK(7, 4)
|
||
|
+#define I2S_LINES_EN(x) BIT(x + 4)
|
||
|
+#define I2S_BPCUV_RCV_MSK BIT(12)
|
||
|
+#define I2S_BPCUV_RCV_EN BIT(12)
|
||
|
+#define I2S_BPCUV_RCV_DIS 0
|
||
|
+#define SPDIF_LINES_EN GENMASK(19, 16)
|
||
|
+#define AUD_FORMAT_MSK GENMASK(26, 24)
|
||
|
+#define AUD_3DOBA (0x7 << 24)
|
||
|
+#define AUD_3DASP (0x6 << 24)
|
||
|
+#define AUD_MSOBA (0x5 << 24)
|
||
|
+#define AUD_MSASP (0x4 << 24)
|
||
|
+#define AUD_HBR (0x3 << 24)
|
||
|
+#define AUD_DST (0x2 << 24)
|
||
|
+#define AUD_OBA (0x1 << 24)
|
||
|
+#define AUD_ASP (0x0 << 24)
|
||
|
+#define AUDIO_INTERFACE_CONFIG1 0x824
|
||
|
+#define AUDIO_INTERFACE_CONTROL0 0x82c
|
||
|
+#define AUDIO_FIFO_CLR_P BIT(0)
|
||
|
+#define AUDIO_INTERFACE_STATUS0 0x834
|
||
|
+/* Frame Composer Registers */
|
||
|
+#define FRAME_COMPOSER_CONFIG0 0x840
|
||
|
+#define FRAME_COMPOSER_CONFIG1 0x844
|
||
|
+#define FRAME_COMPOSER_CONFIG2 0x848
|
||
|
+#define FRAME_COMPOSER_CONFIG3 0x84c
|
||
|
+#define FRAME_COMPOSER_CONFIG4 0x850
|
||
|
+#define FRAME_COMPOSER_CONFIG5 0x854
|
||
|
+#define FRAME_COMPOSER_CONFIG6 0x858
|
||
|
+#define FRAME_COMPOSER_CONFIG7 0x85c
|
||
|
+#define FRAME_COMPOSER_CONFIG8 0x860
|
||
|
+#define FRAME_COMPOSER_CONFIG9 0x864
|
||
|
+#define FRAME_COMPOSER_CONTROL0 0x86c
|
||
|
+/* Video Monitor Registers */
|
||
|
+#define VIDEO_MONITOR_CONFIG0 0x880
|
||
|
+#define VIDEO_MONITOR_STATUS0 0x884
|
||
|
+#define VIDEO_MONITOR_STATUS1 0x888
|
||
|
+#define VIDEO_MONITOR_STATUS2 0x88c
|
||
|
+#define VIDEO_MONITOR_STATUS3 0x890
|
||
|
+#define VIDEO_MONITOR_STATUS4 0x894
|
||
|
+#define VIDEO_MONITOR_STATUS5 0x898
|
||
|
+#define VIDEO_MONITOR_STATUS6 0x89c
|
||
|
+/* HDCP2 Logic Registers */
|
||
|
+#define HDCP2LOGIC_CONFIG0 0x8e0
|
||
|
+#define HDCP2_BYPASS BIT(0)
|
||
|
+#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4
|
||
|
+#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8
|
||
|
+/* HDCP14 Registers */
|
||
|
+#define HDCP14_CONFIG0 0x900
|
||
|
+#define HDCP14_CONFIG1 0x904
|
||
|
+#define HDCP14_CONFIG2 0x908
|
||
|
+#define HDCP14_CONFIG3 0x90c
|
||
|
+#define HDCP14_KEY_SEED 0x914
|
||
|
+#define HDCP14_KEY_H 0x918
|
||
|
+#define HDCP14_KEY_L 0x91c
|
||
|
+#define HDCP14_KEY_STATUS 0x920
|
||
|
+#define HDCP14_AKSV_H 0x924
|
||
|
+#define HDCP14_AKSV_L 0x928
|
||
|
+#define HDCP14_AN_H 0x92c
|
||
|
+#define HDCP14_AN_L 0x930
|
||
|
+#define HDCP14_STATUS0 0x934
|
||
|
+#define HDCP14_STATUS1 0x938
|
||
|
+/* Scrambler Registers */
|
||
|
+#define SCRAMB_CONFIG0 0x960
|
||
|
+/* Video Configuration Registers */
|
||
|
+#define LINK_CONFIG0 0x968
|
||
|
+#define OPMODE_FRL_4LANES BIT(8)
|
||
|
+#define OPMODE_DVI BIT(4)
|
||
|
+#define OPMODE_FRL BIT(0)
|
||
|
+/* TMDS FIFO Registers */
|
||
|
+#define TMDS_FIFO_CONFIG0 0x970
|
||
|
+#define TMDS_FIFO_CONTROL0 0x974
|
||
|
+/* FRL RSFEC Registers */
|
||
|
+#define FRL_RSFEC_CONFIG0 0xa20
|
||
|
+#define FRL_RSFEC_STATUS0 0xa30
|
||
|
+/* FRL Packetizer Registers */
|
||
|
+#define FRL_PKTZ_CONFIG0 0xa40
|
||
|
+#define FRL_PKTZ_CONTROL0 0xa44
|
||
|
+#define FRL_PKTZ_CONTROL1 0xa50
|
||
|
+#define FRL_PKTZ_STATUS1 0xa54
|
||
|
+/* Packet Scheduler Registers */
|
||
|
+#define PKTSCHED_CONFIG0 0xa80
|
||
|
+#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84
|
||
|
+#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88
|
||
|
+#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c
|
||
|
+#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90
|
||
|
+#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94
|
||
|
+#define PKTSCHED_PKT_CONFIG0 0xa98
|
||
|
+#define PKTSCHED_PKT_CONFIG1 0xa9c
|
||
|
+#define PKTSCHED_DRMI_FIELDRATE BIT(13)
|
||
|
+#define PKTSCHED_AVI_FIELDRATE BIT(12)
|
||
|
+#define PKTSCHED_PKT_CONFIG2 0xaa0
|
||
|
+#define PKTSCHED_PKT_CONFIG3 0xaa4
|
||
|
+#define PKTSCHED_PKT_EN 0xaa8
|
||
|
+#define PKTSCHED_DRMI_TX_EN BIT(17)
|
||
|
+#define PKTSCHED_AUDI_TX_EN BIT(15)
|
||
|
+#define PKTSCHED_AVI_TX_EN BIT(13)
|
||
|
+#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10)
|
||
|
+#define PKTSCHED_AMD_TX_EN BIT(8)
|
||
|
+#define PKTSCHED_GCP_TX_EN BIT(3)
|
||
|
+#define PKTSCHED_AUDS_TX_EN BIT(2)
|
||
|
+#define PKTSCHED_ACR_TX_EN BIT(1)
|
||
|
+#define PKTSCHED_NULL_TX_EN BIT(0)
|
||
|
+#define PKTSCHED_PKT_CONTROL0 0xaac
|
||
|
+#define PKTSCHED_PKT_SEND 0xab0
|
||
|
+#define PKTSCHED_PKT_STATUS0 0xab4
|
||
|
+#define PKTSCHED_PKT_STATUS1 0xab8
|
||
|
+#define PKT_NULL_CONTENTS0 0xb00
|
||
|
+#define PKT_NULL_CONTENTS1 0xb04
|
||
|
+#define PKT_NULL_CONTENTS2 0xb08
|
||
|
+#define PKT_NULL_CONTENTS3 0xb0c
|
||
|
+#define PKT_NULL_CONTENTS4 0xb10
|
||
|
+#define PKT_NULL_CONTENTS5 0xb14
|
||
|
+#define PKT_NULL_CONTENTS6 0xb18
|
||
|
+#define PKT_NULL_CONTENTS7 0xb1c
|
||
|
+#define PKT_ACP_CONTENTS0 0xb20
|
||
|
+#define PKT_ACP_CONTENTS1 0xb24
|
||
|
+#define PKT_ACP_CONTENTS2 0xb28
|
||
|
+#define PKT_ACP_CONTENTS3 0xb2c
|
||
|
+#define PKT_ACP_CONTENTS4 0xb30
|
||
|
+#define PKT_ACP_CONTENTS5 0xb34
|
||
|
+#define PKT_ACP_CONTENTS6 0xb38
|
||
|
+#define PKT_ACP_CONTENTS7 0xb3c
|
||
|
+#define PKT_ISRC1_CONTENTS0 0xb40
|
||
|
+#define PKT_ISRC1_CONTENTS1 0xb44
|
||
|
+#define PKT_ISRC1_CONTENTS2 0xb48
|
||
|
+#define PKT_ISRC1_CONTENTS3 0xb4c
|
||
|
+#define PKT_ISRC1_CONTENTS4 0xb50
|
||
|
+#define PKT_ISRC1_CONTENTS5 0xb54
|
||
|
+#define PKT_ISRC1_CONTENTS6 0xb58
|
||
|
+#define PKT_ISRC1_CONTENTS7 0xb5c
|
||
|
+#define PKT_ISRC2_CONTENTS0 0xb60
|
||
|
+#define PKT_ISRC2_CONTENTS1 0xb64
|
||
|
+#define PKT_ISRC2_CONTENTS2 0xb68
|
||
|
+#define PKT_ISRC2_CONTENTS3 0xb6c
|
||
|
+#define PKT_ISRC2_CONTENTS4 0xb70
|
||
|
+#define PKT_ISRC2_CONTENTS5 0xb74
|
||
|
+#define PKT_ISRC2_CONTENTS6 0xb78
|
||
|
+#define PKT_ISRC2_CONTENTS7 0xb7c
|
||
|
+#define PKT_GMD_CONTENTS0 0xb80
|
||
|
+#define PKT_GMD_CONTENTS1 0xb84
|
||
|
+#define PKT_GMD_CONTENTS2 0xb88
|
||
|
+#define PKT_GMD_CONTENTS3 0xb8c
|
||
|
+#define PKT_GMD_CONTENTS4 0xb90
|
||
|
+#define PKT_GMD_CONTENTS5 0xb94
|
||
|
+#define PKT_GMD_CONTENTS6 0xb98
|
||
|
+#define PKT_GMD_CONTENTS7 0xb9c
|
||
|
+#define PKT_AMD_CONTENTS0 0xba0
|
||
|
+#define PKT_AMD_CONTENTS1 0xba4
|
||
|
+#define PKT_AMD_CONTENTS2 0xba8
|
||
|
+#define PKT_AMD_CONTENTS3 0xbac
|
||
|
+#define PKT_AMD_CONTENTS4 0xbb0
|
||
|
+#define PKT_AMD_CONTENTS5 0xbb4
|
||
|
+#define PKT_AMD_CONTENTS6 0xbb8
|
||
|
+#define PKT_AMD_CONTENTS7 0xbbc
|
||
|
+#define PKT_VSI_CONTENTS0 0xbc0
|
||
|
+#define PKT_VSI_CONTENTS1 0xbc4
|
||
|
+#define PKT_VSI_CONTENTS2 0xbc8
|
||
|
+#define PKT_VSI_CONTENTS3 0xbcc
|
||
|
+#define PKT_VSI_CONTENTS4 0xbd0
|
||
|
+#define PKT_VSI_CONTENTS5 0xbd4
|
||
|
+#define PKT_VSI_CONTENTS6 0xbd8
|
||
|
+#define PKT_VSI_CONTENTS7 0xbdc
|
||
|
+#define PKT_AVI_CONTENTS0 0xbe0
|
||
|
+#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4)
|
||
|
+#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04
|
||
|
+#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08
|
||
|
+#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80
|
||
|
+#define PKT_AVI_CONTENTS1 0xbe4
|
||
|
+#define PKT_AVI_CONTENTS2 0xbe8
|
||
|
+#define PKT_AVI_CONTENTS3 0xbec
|
||
|
+#define PKT_AVI_CONTENTS4 0xbf0
|
||
|
+#define PKT_AVI_CONTENTS5 0xbf4
|
||
|
+#define PKT_AVI_CONTENTS6 0xbf8
|
||
|
+#define PKT_AVI_CONTENTS7 0xbfc
|
||
|
+#define PKT_SPDI_CONTENTS0 0xc00
|
||
|
+#define PKT_SPDI_CONTENTS1 0xc04
|
||
|
+#define PKT_SPDI_CONTENTS2 0xc08
|
||
|
+#define PKT_SPDI_CONTENTS3 0xc0c
|
||
|
+#define PKT_SPDI_CONTENTS4 0xc10
|
||
|
+#define PKT_SPDI_CONTENTS5 0xc14
|
||
|
+#define PKT_SPDI_CONTENTS6 0xc18
|
||
|
+#define PKT_SPDI_CONTENTS7 0xc1c
|
||
|
+#define PKT_AUDI_CONTENTS0 0xc20
|
||
|
+#define PKT_AUDI_CONTENTS1 0xc24
|
||
|
+#define PKT_AUDI_CONTENTS2 0xc28
|
||
|
+#define PKT_AUDI_CONTENTS3 0xc2c
|
||
|
+#define PKT_AUDI_CONTENTS4 0xc30
|
||
|
+#define PKT_AUDI_CONTENTS5 0xc34
|
||
|
+#define PKT_AUDI_CONTENTS6 0xc38
|
||
|
+#define PKT_AUDI_CONTENTS7 0xc3c
|
||
|
+#define PKT_NVI_CONTENTS0 0xc40
|
||
|
+#define PKT_NVI_CONTENTS1 0xc44
|
||
|
+#define PKT_NVI_CONTENTS2 0xc48
|
||
|
+#define PKT_NVI_CONTENTS3 0xc4c
|
||
|
+#define PKT_NVI_CONTENTS4 0xc50
|
||
|
+#define PKT_NVI_CONTENTS5 0xc54
|
||
|
+#define PKT_NVI_CONTENTS6 0xc58
|
||
|
+#define PKT_NVI_CONTENTS7 0xc5c
|
||
|
+#define PKT_DRMI_CONTENTS0 0xc60
|
||
|
+#define PKT_DRMI_CONTENTS1 0xc64
|
||
|
+#define PKT_DRMI_CONTENTS2 0xc68
|
||
|
+#define PKT_DRMI_CONTENTS3 0xc6c
|
||
|
+#define PKT_DRMI_CONTENTS4 0xc70
|
||
|
+#define PKT_DRMI_CONTENTS5 0xc74
|
||
|
+#define PKT_DRMI_CONTENTS6 0xc78
|
||
|
+#define PKT_DRMI_CONTENTS7 0xc7c
|
||
|
+#define PKT_GHDMI1_CONTENTS0 0xc80
|
||
|
+#define PKT_GHDMI1_CONTENTS1 0xc84
|
||
|
+#define PKT_GHDMI1_CONTENTS2 0xc88
|
||
|
+#define PKT_GHDMI1_CONTENTS3 0xc8c
|
||
|
+#define PKT_GHDMI1_CONTENTS4 0xc90
|
||
|
+#define PKT_GHDMI1_CONTENTS5 0xc94
|
||
|
+#define PKT_GHDMI1_CONTENTS6 0xc98
|
||
|
+#define PKT_GHDMI1_CONTENTS7 0xc9c
|
||
|
+#define PKT_GHDMI2_CONTENTS0 0xca0
|
||
|
+#define PKT_GHDMI2_CONTENTS1 0xca4
|
||
|
+#define PKT_GHDMI2_CONTENTS2 0xca8
|
||
|
+#define PKT_GHDMI2_CONTENTS3 0xcac
|
||
|
+#define PKT_GHDMI2_CONTENTS4 0xcb0
|
||
|
+#define PKT_GHDMI2_CONTENTS5 0xcb4
|
||
|
+#define PKT_GHDMI2_CONTENTS6 0xcb8
|
||
|
+#define PKT_GHDMI2_CONTENTS7 0xcbc
|
||
|
+/* EMP Packetizer Registers */
|
||
|
+#define PKT_EMP_CONFIG0 0xce0
|
||
|
+#define PKT_EMP_CONTROL0 0xcec
|
||
|
+#define PKT_EMP_CONTROL1 0xcf0
|
||
|
+#define PKT_EMP_CONTROL2 0xcf4
|
||
|
+#define PKT_EMP_VTEM_CONTENTS0 0xd00
|
||
|
+#define PKT_EMP_VTEM_CONTENTS1 0xd04
|
||
|
+#define PKT_EMP_VTEM_CONTENTS2 0xd08
|
||
|
+#define PKT_EMP_VTEM_CONTENTS3 0xd0c
|
||
|
+#define PKT_EMP_VTEM_CONTENTS4 0xd10
|
||
|
+#define PKT_EMP_VTEM_CONTENTS5 0xd14
|
||
|
+#define PKT_EMP_VTEM_CONTENTS6 0xd18
|
||
|
+#define PKT_EMP_VTEM_CONTENTS7 0xd1c
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS0 0xd20
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS1 0xd24
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS2 0xd28
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS4 0xd30
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS5 0xd34
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS6 0xd38
|
||
|
+#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS0 0xd40
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS1 0xd44
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS2 0xd48
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS4 0xd50
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS5 0xd54
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS6 0xd58
|
||
|
+#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS0 0xd60
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS1 0xd64
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS2 0xd68
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS4 0xd70
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS5 0xd74
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS6 0xd78
|
||
|
+#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS0 0xd80
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS1 0xd84
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS2 0xd88
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS4 0xd90
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS5 0xd94
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS6 0xd98
|
||
|
+#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS0 0xda0
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS1 0xda4
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS2 0xda8
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS3 0xdac
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8
|
||
|
+#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8
|
||
|
+#define PKT5_EMP_CVTEM_CONTENTS7 0xddc
|
||
|
+/* Audio Packetizer Registers */
|
||
|
+#define AUDPKT_CONTROL0 0xe20
|
||
|
+#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12)
|
||
|
+#define AUDPKT_PBIT_FORCE_EN BIT(12)
|
||
|
+#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0)
|
||
|
+#define AUDPKT_CHSTATUS_OVR_EN BIT(0)
|
||
|
+#define AUDPKT_CONTROL1 0xe24
|
||
|
+#define AUDPKT_ACR_CONTROL0 0xe40
|
||
|
+#define AUDPKT_ACR_N_VALUE 0xfffff
|
||
|
+#define AUDPKT_ACR_CONTROL1 0xe44
|
||
|
+#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4)
|
||
|
+#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4)
|
||
|
+#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1)
|
||
|
+#define AUDPKT_ACR_CTS_OVR_EN BIT(1)
|
||
|
+#define AUDPKT_ACR_STATUS0 0xe4c
|
||
|
+#define AUDPKT_CHSTATUS_OVR0 0xe60
|
||
|
+#define AUDPKT_CHSTATUS_OVR1 0xe64
|
||
|
+/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */
|
||
|
+#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0)
|
||
|
+#define AUDPKT_CHSTATUS_SR_22050 0x4
|
||
|
+#define AUDPKT_CHSTATUS_SR_24000 0x6
|
||
|
+#define AUDPKT_CHSTATUS_SR_32000 0x3
|
||
|
+#define AUDPKT_CHSTATUS_SR_44100 0x0
|
||
|
+#define AUDPKT_CHSTATUS_SR_48000 0x2
|
||
|
+#define AUDPKT_CHSTATUS_SR_88200 0x8
|
||
|
+#define AUDPKT_CHSTATUS_SR_96000 0xa
|
||
|
+#define AUDPKT_CHSTATUS_SR_176400 0xc
|
||
|
+#define AUDPKT_CHSTATUS_SR_192000 0xe
|
||
|
+#define AUDPKT_CHSTATUS_SR_768000 0x9
|
||
|
+#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1
|
||
|
+/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */
|
||
|
+#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12)
|
||
|
+#define AUDPKT_CHSTATUS_OSR_8000 0x6
|
||
|
+#define AUDPKT_CHSTATUS_OSR_11025 0xa
|
||
|
+#define AUDPKT_CHSTATUS_OSR_12000 0x2
|
||
|
+#define AUDPKT_CHSTATUS_OSR_16000 0x8
|
||
|
+#define AUDPKT_CHSTATUS_OSR_22050 0xb
|
||
|
+#define AUDPKT_CHSTATUS_OSR_24000 0x9
|
||
|
+#define AUDPKT_CHSTATUS_OSR_32000 0xc
|
||
|
+#define AUDPKT_CHSTATUS_OSR_44100 0xf
|
||
|
+#define AUDPKT_CHSTATUS_OSR_48000 0xd
|
||
|
+#define AUDPKT_CHSTATUS_OSR_88200 0x7
|
||
|
+#define AUDPKT_CHSTATUS_OSR_96000 0x5
|
||
|
+#define AUDPKT_CHSTATUS_OSR_176400 0x3
|
||
|
+#define AUDPKT_CHSTATUS_OSR_192000 0x1
|
||
|
+#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0
|
||
|
+#define AUDPKT_CHSTATUS_OVR2 0xe68
|
||
|
+#define AUDPKT_CHSTATUS_OVR3 0xe6c
|
||
|
+#define AUDPKT_CHSTATUS_OVR4 0xe70
|
||
|
+#define AUDPKT_CHSTATUS_OVR5 0xe74
|
||
|
+#define AUDPKT_CHSTATUS_OVR6 0xe78
|
||
|
+#define AUDPKT_CHSTATUS_OVR7 0xe7c
|
||
|
+#define AUDPKT_CHSTATUS_OVR8 0xe80
|
||
|
+#define AUDPKT_CHSTATUS_OVR9 0xe84
|
||
|
+#define AUDPKT_CHSTATUS_OVR10 0xe88
|
||
|
+#define AUDPKT_CHSTATUS_OVR11 0xe8c
|
||
|
+#define AUDPKT_CHSTATUS_OVR12 0xe90
|
||
|
+#define AUDPKT_CHSTATUS_OVR13 0xe94
|
||
|
+#define AUDPKT_CHSTATUS_OVR14 0xe98
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c
|
||
|
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20
|
||
|
+#define AUDPKT_VBIT_OVR0 0xf24
|
||
|
+/* CEC Registers */
|
||
|
+#define CEC_TX_CONTROL 0x1000
|
||
|
+#define CEC_STATUS 0x1004
|
||
|
+#define CEC_CONFIG 0x1008
|
||
|
+#define CEC_ADDR 0x100c
|
||
|
+#define CEC_TX_COUNT 0x1020
|
||
|
+#define CEC_TX_DATA3_0 0x1024
|
||
|
+#define CEC_TX_DATA7_4 0x1028
|
||
|
+#define CEC_TX_DATA11_8 0x102c
|
||
|
+#define CEC_TX_DATA15_12 0x1030
|
||
|
+#define CEC_RX_COUNT_STATUS 0x1040
|
||
|
+#define CEC_RX_DATA3_0 0x1044
|
||
|
+#define CEC_RX_DATA7_4 0x1048
|
||
|
+#define CEC_RX_DATA11_8 0x104c
|
||
|
+#define CEC_RX_DATA15_12 0x1050
|
||
|
+#define CEC_LOCK_CONTROL 0x1054
|
||
|
+#define CEC_RXQUAL_BITTIME_CONFIG 0x1060
|
||
|
+#define CEC_RX_BITTIME_CONFIG 0x1064
|
||
|
+#define CEC_TX_BITTIME_CONFIG 0x1068
|
||
|
+/* eARC RX CMDC Registers */
|
||
|
+#define EARCRX_CMDC_CONFIG0 0x1800
|
||
|
+#define EARCRX_XACTREAD_STOP_CFG BIT(26)
|
||
|
+#define EARCRX_XACTREAD_RETRY_CFG BIT(25)
|
||
|
+#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24)
|
||
|
+#define EARCRX_CMDC_XACT_RESTART_EN BIT(18)
|
||
|
+#define EARCRX_CMDC_CONFIG1 0x1804
|
||
|
+#define EARCRX_CMDC_CONTROL 0x1808
|
||
|
+#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4)
|
||
|
+#define EARCRX_CMDC_DISCOVERY_EN BIT(3)
|
||
|
+#define EARCRX_CONNECTOR_HPD BIT(1)
|
||
|
+#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c
|
||
|
+#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810
|
||
|
+#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814
|
||
|
+#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818
|
||
|
+#define EARCRX_CMDC_STATUS 0x181c
|
||
|
+#define EARCRX_CMDC_XACT_INFO 0x1820
|
||
|
+#define EARCRX_CMDC_XACT_ACTION 0x1824
|
||
|
+#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828
|
||
|
+#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c
|
||
|
+#define EARCRX_CMDC_XACT_WR0 0x1840
|
||
|
+#define EARCRX_CMDC_XACT_WR1 0x1844
|
||
|
+#define EARCRX_CMDC_XACT_WR2 0x1848
|
||
|
+#define EARCRX_CMDC_XACT_WR3 0x184c
|
||
|
+#define EARCRX_CMDC_XACT_WR4 0x1850
|
||
|
+#define EARCRX_CMDC_XACT_WR5 0x1854
|
||
|
+#define EARCRX_CMDC_XACT_WR6 0x1858
|
||
|
+#define EARCRX_CMDC_XACT_WR7 0x185c
|
||
|
+#define EARCRX_CMDC_XACT_WR8 0x1860
|
||
|
+#define EARCRX_CMDC_XACT_WR9 0x1864
|
||
|
+#define EARCRX_CMDC_XACT_WR10 0x1868
|
||
|
+#define EARCRX_CMDC_XACT_WR11 0x186c
|
||
|
+#define EARCRX_CMDC_XACT_WR12 0x1870
|
||
|
+#define EARCRX_CMDC_XACT_WR13 0x1874
|
||
|
+#define EARCRX_CMDC_XACT_WR14 0x1878
|
||
|
+#define EARCRX_CMDC_XACT_WR15 0x187c
|
||
|
+#define EARCRX_CMDC_XACT_WR16 0x1880
|
||
|
+#define EARCRX_CMDC_XACT_WR17 0x1884
|
||
|
+#define EARCRX_CMDC_XACT_WR18 0x1888
|
||
|
+#define EARCRX_CMDC_XACT_WR19 0x188c
|
||
|
+#define EARCRX_CMDC_XACT_WR20 0x1890
|
||
|
+#define EARCRX_CMDC_XACT_WR21 0x1894
|
||
|
+#define EARCRX_CMDC_XACT_WR22 0x1898
|
||
|
+#define EARCRX_CMDC_XACT_WR23 0x189c
|
||
|
+#define EARCRX_CMDC_XACT_WR24 0x18a0
|
||
|
+#define EARCRX_CMDC_XACT_WR25 0x18a4
|
||
|
+#define EARCRX_CMDC_XACT_WR26 0x18a8
|
||
|
+#define EARCRX_CMDC_XACT_WR27 0x18ac
|
||
|
+#define EARCRX_CMDC_XACT_WR28 0x18b0
|
||
|
+#define EARCRX_CMDC_XACT_WR29 0x18b4
|
||
|
+#define EARCRX_CMDC_XACT_WR30 0x18b8
|
||
|
+#define EARCRX_CMDC_XACT_WR31 0x18bc
|
||
|
+#define EARCRX_CMDC_XACT_WR32 0x18c0
|
||
|
+#define EARCRX_CMDC_XACT_WR33 0x18c4
|
||
|
+#define EARCRX_CMDC_XACT_WR34 0x18c8
|
||
|
+#define EARCRX_CMDC_XACT_WR35 0x18cc
|
||
|
+#define EARCRX_CMDC_XACT_WR36 0x18d0
|
||
|
+#define EARCRX_CMDC_XACT_WR37 0x18d4
|
||
|
+#define EARCRX_CMDC_XACT_WR38 0x18d8
|
||
|
+#define EARCRX_CMDC_XACT_WR39 0x18dc
|
||
|
+#define EARCRX_CMDC_XACT_WR40 0x18e0
|
||
|
+#define EARCRX_CMDC_XACT_WR41 0x18e4
|
||
|
+#define EARCRX_CMDC_XACT_WR42 0x18e8
|
||
|
+#define EARCRX_CMDC_XACT_WR43 0x18ec
|
||
|
+#define EARCRX_CMDC_XACT_WR44 0x18f0
|
||
|
+#define EARCRX_CMDC_XACT_WR45 0x18f4
|
||
|
+#define EARCRX_CMDC_XACT_WR46 0x18f8
|
||
|
+#define EARCRX_CMDC_XACT_WR47 0x18fc
|
||
|
+#define EARCRX_CMDC_XACT_WR48 0x1900
|
||
|
+#define EARCRX_CMDC_XACT_WR49 0x1904
|
||
|
+#define EARCRX_CMDC_XACT_WR50 0x1908
|
||
|
+#define EARCRX_CMDC_XACT_WR51 0x190c
|
||
|
+#define EARCRX_CMDC_XACT_WR52 0x1910
|
||
|
+#define EARCRX_CMDC_XACT_WR53 0x1914
|
||
|
+#define EARCRX_CMDC_XACT_WR54 0x1918
|
||
|
+#define EARCRX_CMDC_XACT_WR55 0x191c
|
||
|
+#define EARCRX_CMDC_XACT_WR56 0x1920
|
||
|
+#define EARCRX_CMDC_XACT_WR57 0x1924
|
||
|
+#define EARCRX_CMDC_XACT_WR58 0x1928
|
||
|
+#define EARCRX_CMDC_XACT_WR59 0x192c
|
||
|
+#define EARCRX_CMDC_XACT_WR60 0x1930
|
||
|
+#define EARCRX_CMDC_XACT_WR61 0x1934
|
||
|
+#define EARCRX_CMDC_XACT_WR62 0x1938
|
||
|
+#define EARCRX_CMDC_XACT_WR63 0x193c
|
||
|
+#define EARCRX_CMDC_XACT_WR64 0x1940
|
||
|
+#define EARCRX_CMDC_XACT_RD0 0x1960
|
||
|
+#define EARCRX_CMDC_XACT_RD1 0x1964
|
||
|
+#define EARCRX_CMDC_XACT_RD2 0x1968
|
||
|
+#define EARCRX_CMDC_XACT_RD3 0x196c
|
||
|
+#define EARCRX_CMDC_XACT_RD4 0x1970
|
||
|
+#define EARCRX_CMDC_XACT_RD5 0x1974
|
||
|
+#define EARCRX_CMDC_XACT_RD6 0x1978
|
||
|
+#define EARCRX_CMDC_XACT_RD7 0x197c
|
||
|
+#define EARCRX_CMDC_XACT_RD8 0x1980
|
||
|
+#define EARCRX_CMDC_XACT_RD9 0x1984
|
||
|
+#define EARCRX_CMDC_XACT_RD10 0x1988
|
||
|
+#define EARCRX_CMDC_XACT_RD11 0x198c
|
||
|
+#define EARCRX_CMDC_XACT_RD12 0x1990
|
||
|
+#define EARCRX_CMDC_XACT_RD13 0x1994
|
||
|
+#define EARCRX_CMDC_XACT_RD14 0x1998
|
||
|
+#define EARCRX_CMDC_XACT_RD15 0x199c
|
||
|
+#define EARCRX_CMDC_XACT_RD16 0x19a0
|
||
|
+#define EARCRX_CMDC_XACT_RD17 0x19a4
|
||
|
+#define EARCRX_CMDC_XACT_RD18 0x19a8
|
||
|
+#define EARCRX_CMDC_XACT_RD19 0x19ac
|
||
|
+#define EARCRX_CMDC_XACT_RD20 0x19b0
|
||
|
+#define EARCRX_CMDC_XACT_RD21 0x19b4
|
||
|
+#define EARCRX_CMDC_XACT_RD22 0x19b8
|
||
|
+#define EARCRX_CMDC_XACT_RD23 0x19bc
|
||
|
+#define EARCRX_CMDC_XACT_RD24 0x19c0
|
||
|
+#define EARCRX_CMDC_XACT_RD25 0x19c4
|
||
|
+#define EARCRX_CMDC_XACT_RD26 0x19c8
|
||
|
+#define EARCRX_CMDC_XACT_RD27 0x19cc
|
||
|
+#define EARCRX_CMDC_XACT_RD28 0x19d0
|
||
|
+#define EARCRX_CMDC_XACT_RD29 0x19d4
|
||
|
+#define EARCRX_CMDC_XACT_RD30 0x19d8
|
||
|
+#define EARCRX_CMDC_XACT_RD31 0x19dc
|
||
|
+#define EARCRX_CMDC_XACT_RD32 0x19e0
|
||
|
+#define EARCRX_CMDC_XACT_RD33 0x19e4
|
||
|
+#define EARCRX_CMDC_XACT_RD34 0x19e8
|
||
|
+#define EARCRX_CMDC_XACT_RD35 0x19ec
|
||
|
+#define EARCRX_CMDC_XACT_RD36 0x19f0
|
||
|
+#define EARCRX_CMDC_XACT_RD37 0x19f4
|
||
|
+#define EARCRX_CMDC_XACT_RD38 0x19f8
|
||
|
+#define EARCRX_CMDC_XACT_RD39 0x19fc
|
||
|
+#define EARCRX_CMDC_XACT_RD40 0x1a00
|
||
|
+#define EARCRX_CMDC_XACT_RD41 0x1a04
|
||
|
+#define EARCRX_CMDC_XACT_RD42 0x1a08
|
||
|
+#define EARCRX_CMDC_XACT_RD43 0x1a0c
|
||
|
+#define EARCRX_CMDC_XACT_RD44 0x1a10
|
||
|
+#define EARCRX_CMDC_XACT_RD45 0x1a14
|
||
|
+#define EARCRX_CMDC_XACT_RD46 0x1a18
|
||
|
+#define EARCRX_CMDC_XACT_RD47 0x1a1c
|
||
|
+#define EARCRX_CMDC_XACT_RD48 0x1a20
|
||
|
+#define EARCRX_CMDC_XACT_RD49 0x1a24
|
||
|
+#define EARCRX_CMDC_XACT_RD50 0x1a28
|
||
|
+#define EARCRX_CMDC_XACT_RD51 0x1a2c
|
||
|
+#define EARCRX_CMDC_XACT_RD52 0x1a30
|
||
|
+#define EARCRX_CMDC_XACT_RD53 0x1a34
|
||
|
+#define EARCRX_CMDC_XACT_RD54 0x1a38
|
||
|
+#define EARCRX_CMDC_XACT_RD55 0x1a3c
|
||
|
+#define EARCRX_CMDC_XACT_RD56 0x1a40
|
||
|
+#define EARCRX_CMDC_XACT_RD57 0x1a44
|
||
|
+#define EARCRX_CMDC_XACT_RD58 0x1a48
|
||
|
+#define EARCRX_CMDC_XACT_RD59 0x1a4c
|
||
|
+#define EARCRX_CMDC_XACT_RD60 0x1a50
|
||
|
+#define EARCRX_CMDC_XACT_RD61 0x1a54
|
||
|
+#define EARCRX_CMDC_XACT_RD62 0x1a58
|
||
|
+#define EARCRX_CMDC_XACT_RD63 0x1a5c
|
||
|
+#define EARCRX_CMDC_XACT_RD64 0x1a60
|
||
|
+#define EARCRX_CMDC_SYNC_CONFIG 0x1b00
|
||
|
+/* eARC RX DMAC Registers */
|
||
|
+#define EARCRX_DMAC_PHY_CONTROL 0x1c00
|
||
|
+#define EARCRX_DMAC_CONFIG 0x1c08
|
||
|
+#define EARCRX_DMAC_CONTROL0 0x1c0c
|
||
|
+#define EARCRX_DMAC_AUDIO_EN BIT(1)
|
||
|
+#define EARCRX_DMAC_EN BIT(0)
|
||
|
+#define EARCRX_DMAC_CONTROL1 0x1c10
|
||
|
+#define EARCRX_DMAC_STATUS 0x1c14
|
||
|
+#define EARCRX_DMAC_CHSTATUS0 0x1c18
|
||
|
+#define EARCRX_DMAC_CHSTATUS1 0x1c1c
|
||
|
+#define EARCRX_DMAC_CHSTATUS2 0x1c20
|
||
|
+#define EARCRX_DMAC_CHSTATUS3 0x1c24
|
||
|
+#define EARCRX_DMAC_CHSTATUS4 0x1c28
|
||
|
+#define EARCRX_DMAC_CHSTATUS5 0x1c2c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c
|
||
|
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78
|
||
|
+#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c
|
||
|
+#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80
|
||
|
+/* Main Unit Interrupt Registers */
|
||
|
+#define MAIN_INTVEC_INDEX 0x3000
|
||
|
+#define MAINUNIT_0_INT_STATUS 0x3010
|
||
|
+#define MAINUNIT_0_INT_MASK_N 0x3014
|
||
|
+#define MAINUNIT_0_INT_CLEAR 0x3018
|
||
|
+#define MAINUNIT_0_INT_FORCE 0x301c
|
||
|
+#define MAINUNIT_1_INT_STATUS 0x3020
|
||
|
+#define FLT_EXIT_TO_LTSL_IRQ BIT(22)
|
||
|
+#define FLT_EXIT_TO_LTS4_IRQ BIT(21)
|
||
|
+#define FLT_EXIT_TO_LTSP_IRQ BIT(20)
|
||
|
+#define SCDC_NACK_RCVD_IRQ BIT(12)
|
||
|
+#define SCDC_RR_REPLY_STOP_IRQ BIT(11)
|
||
|
+#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10)
|
||
|
+#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9)
|
||
|
+#define SCDC_UPD_FLAGS_RD_IRQ BIT(8)
|
||
|
+#define I2CM_NACK_RCVD_IRQ BIT(2)
|
||
|
+#define I2CM_READ_REQUEST_IRQ BIT(1)
|
||
|
+#define I2CM_OP_DONE_IRQ BIT(0)
|
||
|
+#define MAINUNIT_1_INT_MASK_N 0x3024
|
||
|
+#define I2CM_NACK_RCVD_MASK_N BIT(2)
|
||
|
+#define I2CM_READ_REQUEST_MASK_N BIT(1)
|
||
|
+#define I2CM_OP_DONE_MASK_N BIT(0)
|
||
|
+#define MAINUNIT_1_INT_CLEAR 0x3028
|
||
|
+#define I2CM_NACK_RCVD_CLEAR BIT(2)
|
||
|
+#define I2CM_READ_REQUEST_CLEAR BIT(1)
|
||
|
+#define I2CM_OP_DONE_CLEAR BIT(0)
|
||
|
+#define MAINUNIT_1_INT_FORCE 0x302c
|
||
|
+/* AVPUNIT Interrupt Registers */
|
||
|
+#define AVP_INTVEC_INDEX 0x3800
|
||
|
+#define AVP_0_INT_STATUS 0x3810
|
||
|
+#define AVP_0_INT_MASK_N 0x3814
|
||
|
+#define AVP_0_INT_CLEAR 0x3818
|
||
|
+#define AVP_0_INT_FORCE 0x381c
|
||
|
+#define AVP_1_INT_STATUS 0x3820
|
||
|
+#define AVP_1_INT_MASK_N 0x3824
|
||
|
+#define HDCP14_AUTH_CHG_MASK_N BIT(6)
|
||
|
+#define AVP_1_INT_CLEAR 0x3828
|
||
|
+#define AVP_1_INT_FORCE 0x382c
|
||
|
+#define AVP_2_INT_STATUS 0x3830
|
||
|
+#define AVP_2_INT_MASK_N 0x3834
|
||
|
+#define AVP_2_INT_CLEAR 0x3838
|
||
|
+#define AVP_2_INT_FORCE 0x383c
|
||
|
+#define AVP_3_INT_STATUS 0x3840
|
||
|
+#define AVP_3_INT_MASK_N 0x3844
|
||
|
+#define AVP_3_INT_CLEAR 0x3848
|
||
|
+#define AVP_3_INT_FORCE 0x384c
|
||
|
+#define AVP_4_INT_STATUS 0x3850
|
||
|
+#define AVP_4_INT_MASK_N 0x3854
|
||
|
+#define AVP_4_INT_CLEAR 0x3858
|
||
|
+#define AVP_4_INT_FORCE 0x385c
|
||
|
+#define AVP_5_INT_STATUS 0x3860
|
||
|
+#define AVP_5_INT_MASK_N 0x3864
|
||
|
+#define AVP_5_INT_CLEAR 0x3868
|
||
|
+#define AVP_5_INT_FORCE 0x386c
|
||
|
+#define AVP_6_INT_STATUS 0x3870
|
||
|
+#define AVP_6_INT_MASK_N 0x3874
|
||
|
+#define AVP_6_INT_CLEAR 0x3878
|
||
|
+#define AVP_6_INT_FORCE 0x387c
|
||
|
+/* CEC Interrupt Registers */
|
||
|
+#define CEC_INT_STATUS 0x4000
|
||
|
+#define CEC_INT_MASK_N 0x4004
|
||
|
+#define CEC_INT_CLEAR 0x4008
|
||
|
+#define CEC_INT_FORCE 0x400c
|
||
|
+/* eARC RX Interrupt Registers */
|
||
|
+#define EARCRX_INTVEC_INDEX 0x4800
|
||
|
+#define EARCRX_0_INT_STATUS 0x4810
|
||
|
+#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9)
|
||
|
+#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8)
|
||
|
+#define EARCRX_0_INT_MASK_N 0x4814
|
||
|
+#define EARCRX_0_INT_CLEAR 0x4818
|
||
|
+#define EARCRX_0_INT_FORCE 0x481c
|
||
|
+#define EARCRX_1_INT_STATUS 0x4820
|
||
|
+#define EARCRX_1_INT_MASK_N 0x4824
|
||
|
+#define EARCRX_1_INT_CLEAR 0x4828
|
||
|
+#define EARCRX_1_INT_FORCE 0x482c
|
||
|
+
|
||
|
+#endif /* __DW_HDMI_QP_H__ */
|
||
|
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
|
||
|
index 6a46baa0737c..a9cc4604222a 100644
|
||
|
--- a/include/drm/bridge/dw_hdmi.h
|
||
|
+++ b/include/drm/bridge/dw_hdmi.h
|
||
|
@@ -131,6 +131,7 @@ struct dw_hdmi_plat_data {
|
||
|
unsigned long input_bus_encoding;
|
||
|
bool use_drm_infoframe;
|
||
|
bool ycbcr_420_allowed;
|
||
|
+ bool is_hdmi_qp;
|
||
|
|
||
|
/*
|
||
|
* Private data passed to all the .mode_valid() and .configure_phy()
|
||
|
@@ -162,6 +163,7 @@ struct dw_hdmi_plat_data {
|
||
|
unsigned long mpixelclock);
|
||
|
|
||
|
unsigned int disable_cec : 1;
|
||
|
+
|
||
|
};
|
||
|
|
||
|
struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||
|
@@ -206,6 +208,12 @@ void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
|
||
|
bool force, bool disabled, bool rxsense);
|
||
|
void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data);
|
||
|
|
||
|
+void dw_hdmi_qp_unbind(struct dw_hdmi *hdmi);
|
||
|
+struct dw_hdmi *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||
|
+ struct drm_encoder *encoder,
|
||
|
+ struct dw_hdmi_plat_data *plat_data);
|
||
|
+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi *hdmi);
|
||
|
+
|
||
|
bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi);
|
||
|
|
||
|
#endif /* __IMX_HDMI_H__ */
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From aad4f4cecc70fa0170fcad63933585f71c975164 Mon Sep 17 00:00:00 2001
|
||
|
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
Date: Mon, 20 May 2024 15:07:08 +0300
|
||
|
Subject: [PATCH 51/54] drm/rockchip: dw_hdmi: Add basic RK3588 support
|
||
|
|
||
|
RK3588 SoC updated the Synopsis DesignWare HDMI transmitter used in the
|
||
|
older SoCs to Quad-Pixel (QP) variant, which is HDMI 2.1 compliant,
|
||
|
while making use of a HDMI/eDP TX Combo PHY based on a Samsung IP block.
|
||
|
|
||
|
Add just the basic support for now, i.e. RGB output up to 4K@30Hz,
|
||
|
without audio, CEC or any of the HDMI 2.1 specific features.
|
||
|
|
||
|
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
|
||
|
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
|
||
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 261 +++++++++++++++++++-
|
||
|
1 file changed, 253 insertions(+), 8 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
index ca6728a43159..70f7006bde08 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
||
|
@@ -29,8 +29,8 @@
|
||
|
|
||
|
#define RK3288_GRF_SOC_CON6 0x025C
|
||
|
#define RK3288_HDMI_LCDC_SEL BIT(4)
|
||
|
-#define RK3328_GRF_SOC_CON2 0x0408
|
||
|
|
||
|
+#define RK3328_GRF_SOC_CON2 0x0408
|
||
|
#define RK3328_HDMI_SDAIN_MSK BIT(11)
|
||
|
#define RK3328_HDMI_SCLIN_MSK BIT(10)
|
||
|
#define RK3328_HDMI_HPD_IOE BIT(2)
|
||
|
@@ -54,6 +54,21 @@
|
||
|
#define RK3568_HDMI_SDAIN_MSK BIT(15)
|
||
|
#define RK3568_HDMI_SCLIN_MSK BIT(14)
|
||
|
|
||
|
+#define RK3588_GRF_SOC_CON2 0x0308
|
||
|
+#define RK3588_HDMI0_HPD_INT_MSK BIT(13)
|
||
|
+#define RK3588_HDMI0_HPD_INT_CLR BIT(12)
|
||
|
+#define RK3588_GRF_SOC_CON7 0x031c
|
||
|
+#define RK3588_SET_HPD_PATH_MASK (0x3 << 12)
|
||
|
+#define RK3588_GRF_SOC_STATUS1 0x0384
|
||
|
+#define RK3588_HDMI0_LEVEL_INT BIT(16)
|
||
|
+#define RK3588_GRF_VO1_CON3 0x000c
|
||
|
+#define RK3588_SCLIN_MASK BIT(9)
|
||
|
+#define RK3588_SDAIN_MASK BIT(10)
|
||
|
+#define RK3588_MODE_MASK BIT(11)
|
||
|
+#define RK3588_I2S_SEL_MASK BIT(13)
|
||
|
+#define RK3588_GRF_VO1_CON9 0x0024
|
||
|
+#define RK3588_HDMI0_GRANT_SEL BIT(10)
|
||
|
+
|
||
|
#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
|
||
|
|
||
|
/**
|
||
|
@@ -71,6 +86,7 @@ struct rockchip_hdmi_chip_data {
|
||
|
struct rockchip_hdmi {
|
||
|
struct device *dev;
|
||
|
struct regmap *regmap;
|
||
|
+ struct regmap *vo1_regmap;
|
||
|
struct rockchip_encoder encoder;
|
||
|
const struct rockchip_hdmi_chip_data *chip_data;
|
||
|
const struct dw_hdmi_plat_data *plat_data;
|
||
|
@@ -78,6 +94,10 @@ struct rockchip_hdmi {
|
||
|
struct clk *grf_clk;
|
||
|
struct dw_hdmi *hdmi;
|
||
|
struct phy *phy;
|
||
|
+
|
||
|
+ bool is_hdmi_qp;
|
||
|
+ struct gpio_desc *qp_enable_gpio;
|
||
|
+ struct delayed_work qp_hpd_work;
|
||
|
};
|
||
|
|
||
|
static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
|
||
|
@@ -206,8 +226,12 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
|
||
|
|
||
|
static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
{
|
||
|
+ static const char * const qp_clk_names[] = {
|
||
|
+ "pclk", "hdp", "earc", "aud", "hclk_vo1",
|
||
|
+ };
|
||
|
struct device_node *np = hdmi->dev->of_node;
|
||
|
- int ret;
|
||
|
+ struct clk *qp_clk;
|
||
|
+ int ret, i;
|
||
|
|
||
|
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||
|
if (IS_ERR(hdmi->regmap)) {
|
||
|
@@ -234,6 +258,34 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+ if (hdmi->is_hdmi_qp) {
|
||
|
+ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
|
||
|
+ if (IS_ERR(hdmi->vo1_regmap)) {
|
||
|
+ drm_err(hdmi, "Unable to get rockchip,vo1_grf\n");
|
||
|
+ return PTR_ERR(hdmi->vo1_regmap);
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = 0; i < ARRAY_SIZE(qp_clk_names); i++) {
|
||
|
+ qp_clk = devm_clk_get_optional_enabled(hdmi->dev, qp_clk_names[i]);
|
||
|
+
|
||
|
+ if (IS_ERR(qp_clk)) {
|
||
|
+ ret = PTR_ERR(qp_clk);
|
||
|
+ if (ret != -EPROBE_DEFER)
|
||
|
+ drm_err(hdmi, "failed to get %s clock: %d\n",
|
||
|
+ qp_clk_names[i], ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ hdmi->qp_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
|
||
|
+ GPIOD_OUT_HIGH);
|
||
|
+ if (IS_ERR(hdmi->qp_enable_gpio)) {
|
||
|
+ ret = PTR_ERR(hdmi->qp_enable_gpio);
|
||
|
+ drm_err(hdmi, "failed to request enable GPIO: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9");
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
@@ -303,8 +355,32 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
|
||
|
static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
||
|
{
|
||
|
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
||
|
+ struct drm_crtc *crtc = encoder->crtc;
|
||
|
u32 val;
|
||
|
- int ret;
|
||
|
+ int ret, rate;
|
||
|
+
|
||
|
+ if (hdmi->is_hdmi_qp) {
|
||
|
+ /* Unconditionally switch to TMDS as FRL is not yet supported */
|
||
|
+ gpiod_set_value(hdmi->qp_enable_gpio, 1);
|
||
|
+
|
||
|
+ if (crtc && crtc->state) {
|
||
|
+ clk_set_rate(hdmi->ref_clk,
|
||
|
+ crtc->state->adjusted_mode.crtc_clock * 1000);
|
||
|
+ /*
|
||
|
+ * FIXME: Temporary workaround to pass pixel clock rate
|
||
|
+ * to the PHY driver until phy_configure_opts_hdmi
|
||
|
+ * becomes available in the PHY API. See also the related
|
||
|
+ * comment in rk_hdptx_phy_power_on() from
|
||
|
+ * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
|
||
|
+ */
|
||
|
+ if (hdmi->phy) {
|
||
|
+ rate = crtc->state->mode.clock * 10;
|
||
|
+ phy_set_bus_width(hdmi->phy, rate);
|
||
|
+ drm_dbg(hdmi, "%s set bus_width=%u\n",
|
||
|
+ __func__, rate);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
|
||
|
if (hdmi->chip_data->lcdsel_grf_reg < 0)
|
||
|
return;
|
||
|
@@ -356,6 +432,9 @@ static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
|
||
|
{
|
||
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||
|
|
||
|
+ if (hdmi->is_hdmi_qp)
|
||
|
+ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display);
|
||
|
+
|
||
|
return phy_power_on(hdmi->phy);
|
||
|
}
|
||
|
|
||
|
@@ -430,6 +509,29 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
|
||
|
RK3328_HDMI_HPD_IOE));
|
||
|
}
|
||
|
|
||
|
+static enum drm_connector_status
|
||
|
+dw_hdmi_rk3588_read_hpd(struct dw_hdmi *dw_hdmi, void *data)
|
||
|
+{
|
||
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||
|
+ u32 val;
|
||
|
+
|
||
|
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
|
||
|
+
|
||
|
+ return val & RK3588_HDMI0_LEVEL_INT ?
|
||
|
+ connector_status_connected : connector_status_disconnected;
|
||
|
+}
|
||
|
+
|
||
|
+static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
|
||
|
+{
|
||
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||
|
+
|
||
|
+ regmap_write(hdmi->regmap,
|
||
|
+ RK3588_GRF_SOC_CON2,
|
||
|
+ HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
|
||
|
+ RK3588_HDMI0_HPD_INT_CLR |
|
||
|
+ RK3588_HDMI0_HPD_INT_MSK));
|
||
|
+}
|
||
|
+
|
||
|
static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
|
||
|
.init = dw_hdmi_rockchip_genphy_init,
|
||
|
.disable = dw_hdmi_rockchip_genphy_disable,
|
||
|
@@ -513,6 +615,82 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
|
||
|
.use_drm_infoframe = true,
|
||
|
};
|
||
|
|
||
|
+static const struct dw_hdmi_phy_ops rk3588_hdmi_phy_ops = {
|
||
|
+ .init = dw_hdmi_rockchip_genphy_init,
|
||
|
+ .disable = dw_hdmi_rockchip_genphy_disable,
|
||
|
+ .read_hpd = dw_hdmi_rk3588_read_hpd,
|
||
|
+ .setup_hpd = dw_hdmi_rk3588_setup_hpd,
|
||
|
+};
|
||
|
+
|
||
|
+struct rockchip_hdmi_chip_data rk3588_chip_data = {
|
||
|
+ .lcdsel_grf_reg = -1,
|
||
|
+};
|
||
|
+
|
||
|
+static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
|
||
|
+ .phy_data = &rk3588_chip_data,
|
||
|
+ .phy_ops = &rk3588_hdmi_phy_ops,
|
||
|
+ .phy_name = "samsung_hdptx_phy",
|
||
|
+ .phy_force_vendor = true,
|
||
|
+ .use_drm_infoframe = true,
|
||
|
+ .is_hdmi_qp = true,
|
||
|
+};
|
||
|
+
|
||
|
+static void dw_hdmi_rk3588_hpd_work(struct work_struct *p_work)
|
||
|
+{
|
||
|
+ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi,
|
||
|
+ qp_hpd_work.work);
|
||
|
+
|
||
|
+ struct drm_device *drm = hdmi->encoder.encoder.dev;
|
||
|
+ bool changed;
|
||
|
+
|
||
|
+ if (drm) {
|
||
|
+ changed = drm_helper_hpd_irq_event(drm);
|
||
|
+ if (changed)
|
||
|
+ drm_dbg(hdmi, "connector status changed\n");
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static irqreturn_t dw_hdmi_rk3588_hardirq(int irq, void *dev_id)
|
||
|
+{
|
||
|
+ struct rockchip_hdmi *hdmi = dev_id;
|
||
|
+ u32 intr_stat, val;
|
||
|
+
|
||
|
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
|
||
|
+
|
||
|
+ if (intr_stat) {
|
||
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
|
||
|
+ RK3588_HDMI0_HPD_INT_MSK);
|
||
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
||
|
+ return IRQ_WAKE_THREAD;
|
||
|
+ }
|
||
|
+
|
||
|
+ return IRQ_NONE;
|
||
|
+}
|
||
|
+
|
||
|
+static irqreturn_t dw_hdmi_rk3588_irq(int irq, void *dev_id)
|
||
|
+{
|
||
|
+ struct rockchip_hdmi *hdmi = dev_id;
|
||
|
+ u32 intr_stat, val;
|
||
|
+ int debounce_ms;
|
||
|
+
|
||
|
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
|
||
|
+ if (!intr_stat)
|
||
|
+ return IRQ_NONE;
|
||
|
+
|
||
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
|
||
|
+ RK3588_HDMI0_HPD_INT_CLR);
|
||
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
||
|
+
|
||
|
+ debounce_ms = intr_stat & RK3588_HDMI0_LEVEL_INT ? 150 : 20;
|
||
|
+ mod_delayed_work(system_wq, &hdmi->qp_hpd_work,
|
||
|
+ msecs_to_jiffies(debounce_ms));
|
||
|
+
|
||
|
+ val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
|
||
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
||
|
+
|
||
|
+ return IRQ_HANDLED;
|
||
|
+}
|
||
|
+
|
||
|
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
||
|
{ .compatible = "rockchip,rk3228-dw-hdmi",
|
||
|
.data = &rk3228_hdmi_drv_data
|
||
|
@@ -529,6 +707,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
||
|
{ .compatible = "rockchip,rk3568-dw-hdmi",
|
||
|
.data = &rk3568_hdmi_drv_data
|
||
|
},
|
||
|
+ { .compatible = "rockchip,rk3588-dw-hdmi",
|
||
|
+ .data = &rk3588_hdmi_drv_data
|
||
|
+ },
|
||
|
{},
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
|
||
|
@@ -542,7 +723,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
struct drm_device *drm = data;
|
||
|
struct drm_encoder *encoder;
|
||
|
struct rockchip_hdmi *hdmi;
|
||
|
- int ret;
|
||
|
+ int ret, irq;
|
||
|
+ u32 val;
|
||
|
|
||
|
if (!pdev->dev.of_node)
|
||
|
return -ENODEV;
|
||
|
@@ -553,13 +735,14 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
|
||
|
match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
|
||
|
plat_data = devm_kmemdup(&pdev->dev, match->data,
|
||
|
- sizeof(*plat_data), GFP_KERNEL);
|
||
|
+ sizeof(*plat_data), GFP_KERNEL);
|
||
|
if (!plat_data)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
hdmi->dev = &pdev->dev;
|
||
|
hdmi->plat_data = plat_data;
|
||
|
hdmi->chip_data = plat_data->phy_data;
|
||
|
+ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp;
|
||
|
plat_data->phy_data = hdmi;
|
||
|
plat_data->priv_data = hdmi;
|
||
|
encoder = &hdmi->encoder.encoder;
|
||
|
@@ -598,6 +781,37 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
RK3568_HDMI_SCLIN_MSK,
|
||
|
RK3568_HDMI_SDAIN_MSK |
|
||
|
RK3568_HDMI_SCLIN_MSK));
|
||
|
+ } else if (hdmi->is_hdmi_qp) {
|
||
|
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
|
||
|
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
|
||
|
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
|
||
|
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
|
||
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
|
||
|
+
|
||
|
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
|
||
|
+ RK3588_SET_HPD_PATH_MASK);
|
||
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
|
||
|
+
|
||
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
|
||
|
+ RK3588_HDMI0_GRANT_SEL);
|
||
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
|
||
|
+
|
||
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
|
||
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
||
|
+
|
||
|
+ INIT_DELAYED_WORK(&hdmi->qp_hpd_work, dw_hdmi_rk3588_hpd_work);
|
||
|
+
|
||
|
+ irq = platform_get_irq(pdev, 4);
|
||
|
+ if (irq < 0)
|
||
|
+ return irq;
|
||
|
+
|
||
|
+ ret = devm_request_threaded_irq(hdmi->dev, irq,
|
||
|
+ dw_hdmi_rk3588_hardirq,
|
||
|
+ dw_hdmi_rk3588_irq,
|
||
|
+ IRQF_SHARED, "dw-hdmi-qp-hpd",
|
||
|
+ hdmi);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
}
|
||
|
|
||
|
drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
|
||
|
@@ -605,7 +819,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||
|
|
||
|
platform_set_drvdata(pdev, hdmi);
|
||
|
|
||
|
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
|
||
|
+ if (hdmi->is_hdmi_qp)
|
||
|
+ hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, plat_data);
|
||
|
+ else
|
||
|
+ hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
|
||
|
|
||
|
/*
|
||
|
* If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
|
||
|
@@ -629,7 +846,13 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
|
||
|
{
|
||
|
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
|
||
|
|
||
|
- dw_hdmi_unbind(hdmi->hdmi);
|
||
|
+ if (hdmi->is_hdmi_qp) {
|
||
|
+ cancel_delayed_work_sync(&hdmi->qp_hpd_work);
|
||
|
+ dw_hdmi_qp_unbind(hdmi->hdmi);
|
||
|
+ } else {
|
||
|
+ dw_hdmi_unbind(hdmi->hdmi);
|
||
|
+ }
|
||
|
+
|
||
|
drm_encoder_cleanup(&hdmi->encoder.encoder);
|
||
|
}
|
||
|
|
||
|
@@ -651,8 +874,30 @@ static void dw_hdmi_rockchip_remove(struct platform_device *pdev)
|
||
|
static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev)
|
||
|
{
|
||
|
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
|
||
|
+ u32 val;
|
||
|
+
|
||
|
+ if (hdmi->is_hdmi_qp) {
|
||
|
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
|
||
|
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
|
||
|
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
|
||
|
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
|
||
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
|
||
|
|
||
|
- dw_hdmi_resume(hdmi->hdmi);
|
||
|
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
|
||
|
+ RK3588_SET_HPD_PATH_MASK);
|
||
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
|
||
|
+
|
||
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
|
||
|
+ RK3588_HDMI0_GRANT_SEL);
|
||
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
|
||
|
+
|
||
|
+ dw_hdmi_qp_resume(dev, hdmi->hdmi);
|
||
|
+
|
||
|
+ if (hdmi->encoder.encoder.dev)
|
||
|
+ drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
|
||
|
+ } else {
|
||
|
+ dw_hdmi_resume(hdmi->hdmi);
|
||
|
+ }
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From 4682a4746176a2d0dbeb71e6085a12391aa3c5dd Mon Sep 17 00:00:00 2001
|
||
|
From: Detlev Casanova <detlev.casanova@collabora.com>
|
||
|
Date: Fri, 3 May 2024 14:27:39 -0400
|
||
|
Subject: [PATCH 52/54] vop2: Add clock resets support
|
||
|
|
||
|
At the end of initialization, each VP clock needs to be reset before
|
||
|
they can be used.
|
||
|
|
||
|
Failing to do so can put the VOP in an undefined state where the
|
||
|
generated HDMI signal is either lost or not matching the selected mode.
|
||
|
|
||
|
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
|
||
|
---
|
||
|
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 30 ++++++++++++++++++++
|
||
|
1 file changed, 30 insertions(+)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
||
|
index 86d2c15af39f..6f626f2f198a 100644
|
||
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
||
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
||
|
@@ -19,6 +19,7 @@
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/pm_runtime.h>
|
||
|
#include <linux/regmap.h>
|
||
|
+#include <linux/reset.h>
|
||
|
#include <linux/swab.h>
|
||
|
|
||
|
#include <drm/drm.h>
|
||
|
@@ -159,6 +160,7 @@ struct vop2_win {
|
||
|
struct vop2_video_port {
|
||
|
struct drm_crtc crtc;
|
||
|
struct vop2 *vop2;
|
||
|
+ struct reset_control *dclk_rst;
|
||
|
struct clk *dclk;
|
||
|
unsigned int id;
|
||
|
const struct vop2_video_port_data *data;
|
||
|
@@ -2064,6 +2066,26 @@ static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
+static int vop2_clk_reset(struct vop2_video_port *vp)
|
||
|
+{
|
||
|
+ struct reset_control *rstc = vp->dclk_rst;
|
||
|
+ struct vop2 *vop2 = vp->vop2;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ if (!rstc)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ ret = reset_control_assert(rstc);
|
||
|
+ if (ret < 0)
|
||
|
+ drm_warn(vop2->drm, "failed to assert reset\n");
|
||
|
+ udelay(10);
|
||
|
+ ret = reset_control_deassert(rstc);
|
||
|
+ if (ret < 0)
|
||
|
+ drm_warn(vop2->drm, "failed to deassert reset\n");
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
||
|
struct drm_atomic_state *state)
|
||
|
{
|
||
|
@@ -2233,6 +2255,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
||
|
|
||
|
vop2_vp_write(vp, RK3568_VP_DSP_CTRL, dsp_ctrl);
|
||
|
|
||
|
+ vop2_clk_reset(vp);
|
||
|
+
|
||
|
drm_crtc_vblank_on(crtc);
|
||
|
|
||
|
vop2_unlock(vop2);
|
||
|
@@ -2920,6 +2944,12 @@ static int vop2_create_crtcs(struct vop2 *vop2)
|
||
|
vp->data = vp_data;
|
||
|
|
||
|
snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
|
||
|
+ vp->dclk_rst = devm_reset_control_get_optional(vop2->dev, dclk_name);
|
||
|
+ if (IS_ERR(vp->dclk_rst)) {
|
||
|
+ drm_err(vop2->drm, "failed to get %s reset\n", dclk_name);
|
||
|
+ return PTR_ERR(vp->dclk_rst);
|
||
|
+ }
|
||
|
+
|
||
|
vp->dclk = devm_clk_get(vop2->dev, dclk_name);
|
||
|
if (IS_ERR(vp->dclk)) {
|
||
|
drm_err(vop2->drm, "failed to get %s\n", dclk_name);
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From ec32c5f52b353b97633cc166e5b1e8342d2ae78e Mon Sep 17 00:00:00 2001
|
||
|
From: Detlev Casanova <detlev.casanova@collabora.com>
|
||
|
Date: Fri, 3 May 2024 14:28:12 -0400
|
||
|
Subject: [PATCH 53/54] arm64: dts: rockchip: Add VOP clock resets for rk3588s
|
||
|
|
||
|
This adds the needed clock resets for all rk3588(s) based SOCs.
|
||
|
|
||
|
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
|
||
|
---
|
||
|
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 8 ++++++++
|
||
|
1 file changed, 8 insertions(+)
|
||
|
|
||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
index eb1aa7d8e475..0fecbf46e127 100644
|
||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
||
|
@@ -1403,6 +1403,14 @@ vop: vop@fdd90000 {
|
||
|
"pclk_vop";
|
||
|
iommus = <&vop_mmu>;
|
||
|
power-domains = <&power RK3588_PD_VOP>;
|
||
|
+ resets = <&cru SRST_D_VOP0>,
|
||
|
+ <&cru SRST_D_VOP1>,
|
||
|
+ <&cru SRST_D_VOP2>,
|
||
|
+ <&cru SRST_D_VOP3>;
|
||
|
+ reset-names = "dclk_vp0",
|
||
|
+ "dclk_vp1",
|
||
|
+ "dclk_vp2",
|
||
|
+ "dclk_vp3";
|
||
|
rockchip,grf = <&sys_grf>;
|
||
|
rockchip,vop-grf = <&vop_grf>;
|
||
|
rockchip,vo1-grf = <&vo1_grf>;
|
||
|
--
|
||
|
2.44.1
|
||
|
|
||
|
|
||
|
From dc492a647595f2866fb2e20ccf576bcaff42a109 Mon Sep 17 00:00:00 2001
|
||
|
From: Detlev Casanova <detlev.casanova@collabora.com>
|
||
|
Date: Mon, 6 May 2024 13:54:01 -0400
|
||
|
Subject: [PATCH 54/54] dt-bindings: display: vop2: Add VP clock resets
|
||
|
|
||
|
Add the documentation for VOP2 video ports reset clocks.
|
||
|
One reset can be set per video port.
|
||
|
|
||
|
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
|
||
|
---
|
||
|
.../display/rockchip/rockchip-vop2.yaml | 27 +++++++++++++++++++
|
||
|
1 file changed, 27 insertions(+)
|
||
|
|
||
|
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
|
||
|
index 2531726af306..941fd059498d 100644
|
||
|
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
|
||
|
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip-vop2.yaml
|
||
|
@@ -65,6 +65,22 @@ properties:
|
||
|
- const: dclk_vp3
|
||
|
- const: pclk_vop
|
||
|
|
||
|
+ resets:
|
||
|
+ minItems: 3
|
||
|
+ items:
|
||
|
+ - description: Pixel clock reset for video port 0.
|
||
|
+ - description: Pixel clock reset for video port 1.
|
||
|
+ - description: Pixel clock reset for video port 2.
|
||
|
+ - description: Pixel clock reset for video port 3.
|
||
|
+
|
||
|
+ reset-names:
|
||
|
+ minItems: 3
|
||
|
+ items:
|
||
|
+ - const: dclk_vp0
|
||
|
+ - const: dclk_vp1
|
||
|
+ - const: dclk_vp2
|
||
|
+ - const: dclk_vp3
|
||
|
+
|
||
|
rockchip,grf:
|
||
|
$ref: /schemas/types.yaml#/definitions/phandle
|
||
|
description:
|
||
|
@@ -128,6 +144,11 @@ allOf:
|
||
|
clock-names:
|
||
|
minItems: 7
|
||
|
|
||
|
+ resets:
|
||
|
+ minItems: 4
|
||
|
+ reset-names:
|
||
|
+ minItems: 4
|
||
|
+
|
||
|
ports:
|
||
|
required:
|
||
|
- port@0
|
||
|
@@ -183,6 +204,12 @@ examples:
|
||
|
"dclk_vp0",
|
||
|
"dclk_vp1",
|
||
|
"dclk_vp2";
|
||
|
+ resets = <&cru SRST_VOP0>,
|
||
|
+ <&cru SRST_VOP1>,
|
||
|
+ <&cru SRST_VOP2>;
|
||
|
+ reset-names = "dclk_vp0",
|
||
|
+ "dclk_vp1",
|
||
|
+ "dclk_vp2";
|
||
|
power-domains = <&power RK3568_PD_VO>;
|
||
|
iommus = <&vop_mmu>;
|
||
|
vop_out: ports {
|
||
|
--
|
||
|
2.44.1
|
||
|
|