17517 lines
506 KiB
Diff
17517 lines
506 KiB
Diff
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
|
|
new file mode 100644
|
|
index 000000000..dcca84d57
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
|
|
@@ -0,0 +1,166 @@
|
|
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
|
+%YAML 1.2
|
|
+---
|
|
+$id: http://devicetree.org/schemas/phy/phy-rockchip-usbdp.yaml#
|
|
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
|
+
|
|
+title: Rockchip USBDP Combo PHY with Samsung IP block
|
|
+
|
|
+maintainers:
|
|
+ - Frank Wang <frank.wang@rock-chips.com>
|
|
+ - Zhang Yubing <yubing.zhang@rock-chips.com>
|
|
+
|
|
+properties:
|
|
+ compatible:
|
|
+ enum:
|
|
+ - rockchip,rk3588-usbdp-phy
|
|
+
|
|
+ reg:
|
|
+ maxItems: 1
|
|
+
|
|
+ clocks:
|
|
+ maxItems: 4
|
|
+
|
|
+ clock-names:
|
|
+ items:
|
|
+ - const: refclk
|
|
+ - const: immortal
|
|
+ - const: pclk
|
|
+ - const: utmi
|
|
+
|
|
+ resets:
|
|
+ maxItems: 5
|
|
+
|
|
+ reset-names:
|
|
+ items:
|
|
+ - const: init
|
|
+ - const: cmn
|
|
+ - const: lane
|
|
+ - const: pcs_apb
|
|
+ - const: pma_apb
|
|
+
|
|
+ rockchip,dp-lane-mux:
|
|
+ $ref: /schemas/types.yaml#/definitions/uint32-array
|
|
+ minItems: 2
|
|
+ maxItems: 4
|
|
+ description:
|
|
+ An array of physical Tyep-C lanes indexes. Position of an entry determines
|
|
+ the dp lane index, while the value of an entry indicater physical Type-C lane.
|
|
+ The support dp lanes number are 2 or 4. e.g. for 2 lanes dp lanes map, we could
|
|
+ have "rockchip,dp-lane-mux = <2, 3>;", assuming dp lane0 on Type-C phy lane2,
|
|
+ dp lane1 on Type-C phy lane3. For 4 lanes dp lanes map, we could have
|
|
+ "rockchip,dp-lane-mux = <0, 1, 2, 3>;", assuming dp lane0 on Type-C phy lane0,
|
|
+ dp lane1 on Type-C phy lane1, dp lane2 on Type-C phy lane2, dp lane3 on Type-C
|
|
+ phy lane3. If dp lane map by DisplayPort Alt mode, this property is not need.
|
|
+
|
|
+ rockchip,u2phy-grf:
|
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
|
+ description:
|
|
+ Phandle to the syscon managing the 'usb2 phy general register files'.
|
|
+
|
|
+ rockchip,usb-grf:
|
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
|
+ description:
|
|
+ Phandle to the syscon managing the 'usb general register files'.
|
|
+
|
|
+ rockchip,usbdpphy-grf:
|
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
|
+ description:
|
|
+ Phandle to the syscon managing the 'usbdp phy general register files'.
|
|
+
|
|
+ rockchip,vo-grf:
|
|
+ $ref: /schemas/types.yaml#/definitions/phandle
|
|
+ description:
|
|
+ Phandle to the syscon managing the 'video output general register files'.
|
|
+ When select the dp lane mapping will request its phandle.
|
|
+
|
|
+ sbu1-dc-gpios:
|
|
+ description:
|
|
+ GPIO connected to the SBU1 line of the USB-C connector via a big resistor
|
|
+ (~100K) to apply a DC offset for signalling the connector orientation.
|
|
+
|
|
+ sbu2-dc-gpios:
|
|
+ description:
|
|
+ GPIO connected to the SBU2 line of the USB-C connector via a big resistor
|
|
+ (~100K) to apply a DC offset for signalling the connector orientation.
|
|
+
|
|
+ orientation-switch:
|
|
+ description: Flag the port as possible handler of orientation switching
|
|
+ type: boolean
|
|
+
|
|
+ mode-switch:
|
|
+ description: Flag the port as possible handle of altmode switching
|
|
+ type: boolean
|
|
+
|
|
+ dp-port:
|
|
+ type: object
|
|
+ additionalProperties: false
|
|
+
|
|
+ properties:
|
|
+ "#phy-cells":
|
|
+ const: 0
|
|
+
|
|
+ required:
|
|
+ - "#phy-cells"
|
|
+
|
|
+ usb3-port:
|
|
+ type: object
|
|
+ additionalProperties: false
|
|
+
|
|
+ properties:
|
|
+ "#phy-cells":
|
|
+ const: 0
|
|
+
|
|
+ required:
|
|
+ - "#phy-cells"
|
|
+
|
|
+ port:
|
|
+ $ref: /schemas/graph.yaml#/properties/port
|
|
+ description:
|
|
+ A port node to link the PHY to a TypeC controller for the purpose of
|
|
+ handling orientation switching.
|
|
+
|
|
+required:
|
|
+ - compatible
|
|
+ - reg
|
|
+ - clocks
|
|
+ - clock-names
|
|
+ - resets
|
|
+ - reset-names
|
|
+ - dp-port
|
|
+ - usb3-port
|
|
+
|
|
+additionalProperties: false
|
|
+
|
|
+examples:
|
|
+ - |
|
|
+ #include <dt-bindings/clock/rk3588-cru.h>
|
|
+
|
|
+ usbdp_phy0: phy@fed80000 {
|
|
+ compatible = "rockchip,rk3588-usbdp-phy";
|
|
+ reg = <0x0 0xfed80000 0x0 0x10000>;
|
|
+ rockchip,u2phy-grf = <&usb2phy0_grf>;
|
|
+ rockchip,usb-grf = <&usb_grf>;
|
|
+ rockchip,usbdpphy-grf = <&usbdpphy0_grf>;
|
|
+ rockchip,vo-grf = <&vo0_grf>;
|
|
+ clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>,
|
|
+ <&cru CLK_USBDP_PHY0_IMMORTAL>,
|
|
+ <&cru PCLK_USBDPPHY0>;
|
|
+ clock-names = "refclk", "immortal", "pclk";
|
|
+ resets = <&cru SRST_USBDP_COMBO_PHY0_INIT>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY0_CMN>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY0_LANE>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY0_PCS>,
|
|
+ <&cru SRST_P_USBDPPHY0>;
|
|
+ reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb";
|
|
+ status = "disabled";
|
|
+
|
|
+ usbdp_phy0_dp: dp-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usbdp_phy0_u3: usb3-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml
|
|
index e4fa6a07b..ce1fd5b0d 100644
|
|
--- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml
|
|
+++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml
|
|
@@ -28,6 +28,9 @@ properties:
|
|
- rockchip,rk3588-sys-grf
|
|
- rockchip,rk3588-pcie3-phy-grf
|
|
- rockchip,rk3588-pcie3-pipe-grf
|
|
+ - rockchip,rk3588-usb-grf
|
|
+ - rockchip,rk3588-usbdpphy-grf
|
|
+ - rockchip,rk3588-vo-grf
|
|
- rockchip,rv1108-usbgrf
|
|
- const: syscon
|
|
- items:
|
|
@@ -64,6 +67,9 @@ properties:
|
|
reg:
|
|
maxItems: 1
|
|
|
|
+ clocks:
|
|
+ maxItems: 1
|
|
+
|
|
"#address-cells":
|
|
const: 1
|
|
|
|
@@ -245,6 +251,22 @@ allOf:
|
|
|
|
unevaluatedProperties: false
|
|
|
|
+ - if:
|
|
+ properties:
|
|
+ compatible:
|
|
+ contains:
|
|
+ enum:
|
|
+ - rockchip,rk3588-vo-grf
|
|
+
|
|
+ then:
|
|
+ required:
|
|
+ - clocks
|
|
+
|
|
+ else:
|
|
+ properties:
|
|
+ clocks: false
|
|
+
|
|
+
|
|
examples:
|
|
- |
|
|
#include <dt-bindings/clock/rk3399-cru.h>
|
|
diff --git a/Documentation/devicetree/bindings/sound/es8328.txt b/Documentation/devicetree/bindings/sound/es8328.txt
|
|
deleted file mode 100644
|
|
index 33fbf058c..000000000
|
|
--- a/Documentation/devicetree/bindings/sound/es8328.txt
|
|
+++ /dev/null
|
|
@@ -1,38 +0,0 @@
|
|
-Everest ES8328 audio CODEC
|
|
-
|
|
-This device supports both I2C and SPI.
|
|
-
|
|
-Required properties:
|
|
-
|
|
- - compatible : Should be "everest,es8328" or "everest,es8388"
|
|
- - DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V
|
|
- - AVDD-supply : Regulator providing analog supply voltage 3.3V
|
|
- - PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V
|
|
- - IPVDD-supply : Regulator providing analog output voltage 3.3V
|
|
- - clocks : A 22.5792 or 11.2896 MHz clock
|
|
- - reg : the I2C address of the device for I2C, the chip select number for SPI
|
|
-
|
|
-Pins on the device (for linking into audio routes):
|
|
-
|
|
- * LOUT1
|
|
- * LOUT2
|
|
- * ROUT1
|
|
- * ROUT2
|
|
- * LINPUT1
|
|
- * RINPUT1
|
|
- * LINPUT2
|
|
- * RINPUT2
|
|
- * Mic Bias
|
|
-
|
|
-
|
|
-Example:
|
|
-
|
|
-codec: es8328@11 {
|
|
- compatible = "everest,es8328";
|
|
- DVDD-supply = <®_3p3v>;
|
|
- AVDD-supply = <®_3p3v>;
|
|
- PVDD-supply = <®_3p3v>;
|
|
- HPVDD-supply = <®_3p3v>;
|
|
- clocks = <&clks 169>;
|
|
- reg = <0x11>;
|
|
-};
|
|
diff --git a/Documentation/devicetree/bindings/sound/everest,es8328.yaml b/Documentation/devicetree/bindings/sound/everest,es8328.yaml
|
|
new file mode 100644
|
|
index 000000000..a0f4670fa
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/sound/everest,es8328.yaml
|
|
@@ -0,0 +1,77 @@
|
|
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
|
+%YAML 1.2
|
|
+---
|
|
+$id: http://devicetree.org/schemas/sound/everest,es8328.yaml#
|
|
+$schema: http://devicetree.org/meta-schemas/core.yaml#
|
|
+
|
|
+title: Everest ES8328 audio CODEC
|
|
+
|
|
+description:
|
|
+ Everest Audio Codec, which can be connected via I2C or SPI.
|
|
+ Pins on the device (for linking into audio routes) are
|
|
+ * LOUT1
|
|
+ * LOUT2
|
|
+ * ROUT1
|
|
+ * ROUT2
|
|
+ * LINPUT1
|
|
+ * RINPUT1
|
|
+ * LINPUT2
|
|
+ * RINPUT2
|
|
+ * Mic Bias
|
|
+
|
|
+maintainers:
|
|
+ - David Yang <yangxiaohua@everest-semi.com>
|
|
+
|
|
+properties:
|
|
+ compatible:
|
|
+ enum:
|
|
+ - everest,es8328
|
|
+ - everest,es8388
|
|
+
|
|
+ reg:
|
|
+ maxItems: 1
|
|
+
|
|
+ "#sound-dai-cells":
|
|
+ const: 0
|
|
+
|
|
+ clocks:
|
|
+ items:
|
|
+ - description: A 22.5792 or 11.2896 MHz clock
|
|
+
|
|
+ DVDD-supply:
|
|
+ description: Regulator providing digital core supply voltage 1.8 - 3.6V
|
|
+
|
|
+ AVDD-supply:
|
|
+ description: Regulator providing analog supply voltage 3.3V
|
|
+
|
|
+ PVDD-supply:
|
|
+ description: Regulator providing digital IO supply voltage 1.8 - 3.6V
|
|
+
|
|
+ HPVDD-supply:
|
|
+ description: Regulator providing analog output voltage 3.3V
|
|
+
|
|
+required:
|
|
+ - compatible
|
|
+ - clocks
|
|
+ - DVDD-supply
|
|
+ - AVDD-supply
|
|
+ - PVDD-supply
|
|
+ - HPVDD-supply
|
|
+
|
|
+additionalProperties: false
|
|
+
|
|
+examples:
|
|
+ - |
|
|
+ i2c {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ es8328: codec@11 {
|
|
+ compatible = "everest,es8328";
|
|
+ reg = <0x11>;
|
|
+ AVDD-supply = <®_3p3v>;
|
|
+ DVDD-supply = <®_3p3v>;
|
|
+ HPVDD-supply = <®_3p3v>;
|
|
+ PVDD-supply = <®_3p3v>;
|
|
+ clocks = <&clks 169>;
|
|
+ };
|
|
+ };
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
|
|
index b9d789d57..2ef5c98c4 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/usb/pd.h>
|
|
#include "rk3588.dtsi"
|
|
|
|
/ {
|
|
@@ -56,12 +57,78 @@ button-escape {
|
|
};
|
|
};
|
|
|
|
+ analog-sound {
|
|
+ compatible = "simple-audio-card";
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&hp_detect>;
|
|
+ simple-audio-card,name = "RK3588 EVB1 Audio";
|
|
+ simple-audio-card,aux-devs = <&_headphone>, <&_speaker>;
|
|
+ simple-audio-card,bitclock-master = <&masterdai>;
|
|
+ simple-audio-card,format = "i2s";
|
|
+ simple-audio-card,frame-master = <&masterdai>;
|
|
+ simple-audio-card,hp-det-gpio = <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>;
|
|
+ simple-audio-card,mclk-fs = <256>;
|
|
+ simple-audio-card,pin-switches = "Headphones", "Speaker";
|
|
+ simple-audio-card,routing =
|
|
+ "Speaker Amplifier INL", "LOUT2",
|
|
+ "Speaker Amplifier INR", "ROUT2",
|
|
+ "Speaker", "Speaker Amplifier OUTL",
|
|
+ "Speaker", "Speaker Amplifier OUTR",
|
|
+ "Headphones Amplifier INL", "LOUT1",
|
|
+ "Headphones Amplifier INR", "ROUT1",
|
|
+ "Headphones", "Headphones Amplifier OUTL",
|
|
+ "Headphones", "Headphones Amplifier OUTR",
|
|
+ "LINPUT1", "Onboard Microphone",
|
|
+ "RINPUT1", "Onboard Microphone",
|
|
+ "LINPUT2", "Microphone Jack",
|
|
+ "RINPUT2", "Microphone Jack";
|
|
+ simple-audio-card,widgets =
|
|
+ "Microphone", "Microphone Jack",
|
|
+ "Microphone", "Onboard Microphone",
|
|
+ "Headphone", "Headphones",
|
|
+ "Speaker", "Speaker";
|
|
+
|
|
+ simple-audio-card,cpu {
|
|
+ sound-dai = <&i2s0_8ch>;
|
|
+ };
|
|
+
|
|
+ masterdai: simple-audio-card,codec {
|
|
+ sound-dai = <&es8388>;
|
|
+ system-clock-frequency = <12288000>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ amp_headphone: headphone-amplifier {
|
|
+ compatible = "simple-audio-amplifier";
|
|
+ enable-gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&headphone_amplifier_en>;
|
|
+ sound-name-prefix = "Headphones Amplifier";
|
|
+ };
|
|
+
|
|
+ amp_speaker: speaker-amplifier {
|
|
+ compatible = "simple-audio-amplifier";
|
|
+ enable-gpios = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&speaker_amplifier_en>;
|
|
+ sound-name-prefix = "Speaker Amplifier";
|
|
+ };
|
|
+
|
|
backlight: backlight {
|
|
compatible = "pwm-backlight";
|
|
power-supply = <&vcc12v_dcin>;
|
|
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>, <&wifi_host_wake_irq>;
|
|
+ };
|
|
+
|
|
pcie20_avdd0v85: pcie20-avdd0v85-regulator {
|
|
compatible = "regulator-fixed";
|
|
regulator-name = "pcie20_avdd0v85";
|
|
@@ -167,46 +234,75 @@ vcc5v0_usb: vcc5v0-usb-regulator {
|
|
regulator-max-microvolt = <5000000>;
|
|
vin-supply = <&vcc5v0_usbdcin>;
|
|
};
|
|
+
|
|
+ vbus5v0_typec: vbus5v0-typec {
|
|
+ compatible = "regulator-fixed";
|
|
+ regulator-name = "vbus5v0_typec";
|
|
+ regulator-min-microvolt = <5000000>;
|
|
+ regulator-max-microvolt = <5000000>;
|
|
+ enable-active-high;
|
|
+ gpio = <&gpio4 RK_PD0 GPIO_ACTIVE_HIGH>;
|
|
+ vin-supply = <&vcc5v0_usb>;
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&typec5v_pwren>;
|
|
+ };
|
|
};
|
|
|
|
&combphy0_ps {
|
|
status = "okay";
|
|
};
|
|
|
|
+&combphy1_ps {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&combphy2_psu {
|
|
status = "okay";
|
|
};
|
|
|
|
&cpu_b0 {
|
|
cpu-supply = <&vdd_cpu_big0_s0>;
|
|
+ mem-supply = <&vdd_cpu_big0_mem_s0>;
|
|
};
|
|
|
|
&cpu_b1 {
|
|
cpu-supply = <&vdd_cpu_big0_s0>;
|
|
+ mem-supply = <&vdd_cpu_big0_mem_s0>;
|
|
};
|
|
|
|
&cpu_b2 {
|
|
cpu-supply = <&vdd_cpu_big1_s0>;
|
|
+ mem-supply = <&vdd_cpu_big1_mem_s0>;
|
|
};
|
|
|
|
&cpu_b3 {
|
|
cpu-supply = <&vdd_cpu_big1_s0>;
|
|
+ mem-supply = <&vdd_cpu_big1_mem_s0>;
|
|
};
|
|
|
|
&cpu_l0 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l1 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l2 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l3 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
+};
|
|
+
|
|
+&display_subsystem {
|
|
+ clocks = <&hdptxphy_hdmi_clk0>;
|
|
+ clock-names = "hdmi0_phy_pll";
|
|
};
|
|
|
|
&gmac0 {
|
|
@@ -227,6 +323,56 @@ &gmac0_rgmii_clk
|
|
&i2c2 {
|
|
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 = <&vbus5v0_typec>;
|
|
+ status = "okay";
|
|
+
|
|
+ usb_con: connector {
|
|
+ compatible = "usb-c-connector";
|
|
+ label = "USB-C";
|
|
+ data-role = "dual";
|
|
+ power-role = "dual";
|
|
+ try-power-role = "sink";
|
|
+ op-sink-microwatt = <1000000>;
|
|
+ sink-pdos =
|
|
+ <PDO_FIXED(5000, 1000, PDO_FIXED_USB_COMM)>;
|
|
+ source-pdos =
|
|
+ <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
|
|
+
|
|
+ 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>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
hym8563: rtc@51 {
|
|
compatible = "haoyu,hym8563";
|
|
reg = <0x51>;
|
|
@@ -240,6 +386,32 @@ hym8563: rtc@51 {
|
|
};
|
|
};
|
|
|
|
+&i2c7 {
|
|
+ status = "okay";
|
|
+
|
|
+ es8388: audio-codec@11 {
|
|
+ compatible = "everest,es8388";
|
|
+ reg = <0x11>;
|
|
+ clocks = <&cru I2S0_8CH_MCLKOUT>;
|
|
+ assigned-clocks = <&cru I2S0_8CH_MCLKOUT>;
|
|
+ assigned-clock-rates = <12288000>;
|
|
+ AVDD-supply = <&avcc_1v8_codec_s0>;
|
|
+ DVDD-supply = <&avcc_1v8_codec_s0>;
|
|
+ HPVDD-supply = <&vcc_3v3_s0>;
|
|
+ PVDD-supply = <&vcc_3v3_s0>;
|
|
+ #sound-dai-cells = <0>;
|
|
+ };
|
|
+};
|
|
+
|
|
+&i2s0_8ch {
|
|
+ pinctrl-0 = <&i2s0_lrck
|
|
+ &i2s0_mclk
|
|
+ &i2s0_sclk
|
|
+ &i2s0_sdi0
|
|
+ &i2s0_sdo0>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&mdio0 {
|
|
rgmii_phy: ethernet-phy@1 {
|
|
/* RTL8211F */
|
|
@@ -253,6 +425,12 @@ rgmii_phy: ethernet-phy@1 {
|
|
};
|
|
};
|
|
|
|
+&pcie2x1l0 {
|
|
+ reset-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_HIGH>;
|
|
+ pinctrl-0 = <&pcie2_0_rst>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&pcie2x1l1 {
|
|
reset-gpios = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>;
|
|
pinctrl-names = "default";
|
|
@@ -273,6 +451,20 @@ &pcie3x4 {
|
|
};
|
|
|
|
&pinctrl {
|
|
+ audio {
|
|
+ hp_detect: headphone-detect {
|
|
+ rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+
|
|
+ headphone_amplifier_en: headphone-amplifier-en {
|
|
+ rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+
|
|
+ speaker_amplifier_en: speaker-amplifier-en {
|
|
+ rockchip,pins = <1 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+ };
|
|
+
|
|
rtl8111 {
|
|
rtl8111_isolate: rtl8111-isolate {
|
|
rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
|
|
@@ -293,6 +485,10 @@ hym8563_int: hym8563-int {
|
|
};
|
|
|
|
pcie2 {
|
|
+ pcie2_0_rst: pcie2-0-rst {
|
|
+ rockchip,pins = <4 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+
|
|
pcie2_1_rst: pcie2-1-rst {
|
|
rockchip,pins = <4 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
};
|
|
@@ -313,6 +509,26 @@ vcc5v0_host_en: vcc5v0-host-en {
|
|
rockchip,pins = <4 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
};
|
|
};
|
|
+
|
|
+ usb-typec {
|
|
+ usbc0_int: usbc0-int {
|
|
+ rockchip,pins = <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>;
|
|
+ };
|
|
+
|
|
+ typec5v_pwren: typec5v-pwren {
|
|
+ rockchip,pins = <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ 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 {
|
|
@@ -943,6 +1159,22 @@ &sata0 {
|
|
status = "okay";
|
|
};
|
|
|
|
+&u2phy0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy0_otg {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy1 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy1_otg {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&u2phy2 {
|
|
status = "okay";
|
|
};
|
|
@@ -981,3 +1213,82 @@ &usb_host1_ehci {
|
|
&usb_host1_ohci {
|
|
status = "okay";
|
|
};
|
|
+
|
|
+&usbdp_phy0 {
|
|
+ orientation-switch;
|
|
+ mode-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>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&usbdp_phy0_u3 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usbdp_phy1 {
|
|
+ rockchip,dp-lane-mux = <2 3>;
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usbdp_phy1_u3 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usb_host0_xhci {
|
|
+ dr_mode = "otg";
|
|
+ usb-role-switch;
|
|
+ status = "okay";
|
|
+
|
|
+ port {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ dwc3_0_role_switch: endpoint@0 {
|
|
+ reg = <0>;
|
|
+ remote-endpoint = <&usbc0_role_sw>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&usb_host1_xhci {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdmi0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdmi0_in_vp0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdptxphy_hdmi0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdptxphy_hdmi_clk0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&vop_mmu {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&vop {
|
|
+ status = "okay";
|
|
+};
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
|
|
index 741f631db..dacf6a4d8 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"
|
|
|
|
/ {
|
|
@@ -59,6 +60,15 @@ fan: pwm-fan {
|
|
#cooling-cells = <2>;
|
|
};
|
|
|
|
+ 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;
|
|
@@ -117,6 +127,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 {
|
|
@@ -138,36 +149,53 @@ &combphy1_ps {
|
|
status = "okay";
|
|
};
|
|
|
|
+&combphy2_psu {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&cpu_b0 {
|
|
cpu-supply = <&vdd_cpu_big0_s0>;
|
|
+ mem-supply = <&vdd_cpu_big0_s0>;
|
|
};
|
|
|
|
&cpu_b1 {
|
|
cpu-supply = <&vdd_cpu_big0_s0>;
|
|
+ mem-supply = <&vdd_cpu_big0_s0>;
|
|
};
|
|
|
|
&cpu_b2 {
|
|
cpu-supply = <&vdd_cpu_big1_s0>;
|
|
+ mem-supply = <&vdd_cpu_big1_s0>;
|
|
};
|
|
|
|
&cpu_b3 {
|
|
cpu-supply = <&vdd_cpu_big1_s0>;
|
|
+ mem-supply = <&vdd_cpu_big1_s0>;
|
|
};
|
|
|
|
&cpu_l0 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l1 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l2 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l3 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
+};
|
|
+
|
|
+&display_subsystem {
|
|
+ clocks = <&hdptxphy_hdmi_clk0>;
|
|
+ clock-names = "hdmi0_phy_pll";
|
|
};
|
|
|
|
&i2c0 {
|
|
@@ -210,6 +238,61 @@ 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>;
|
|
+ status = "okay";
|
|
+
|
|
+ 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_hs: endpoint {
|
|
+ remote-endpoint = <&usb_host0_xhci_drd_sw>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ port@1 {
|
|
+ reg = <1>;
|
|
+ usbc0_ss: endpoint {
|
|
+ remote-endpoint = <&usbdp_phy0_typec_ss>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ port@2 {
|
|
+ reg = <2>;
|
|
+ usbc0_sbu: endpoint {
|
|
+ remote-endpoint = <&usbdp_phy0_typec_sbu>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
&i2c6 {
|
|
status = "okay";
|
|
|
|
@@ -339,6 +422,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>;
|
|
+ };
|
|
};
|
|
};
|
|
|
|
@@ -731,6 +818,22 @@ &uart2 {
|
|
status = "okay";
|
|
};
|
|
|
|
+&u2phy0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy0_otg {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy1 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy1_otg {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&u2phy2 {
|
|
status = "okay";
|
|
};
|
|
@@ -750,6 +853,41 @@ &u2phy3_host {
|
|
status = "okay";
|
|
};
|
|
|
|
+&usbdp_phy1 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usbdp_phy1_u3 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usbdp_phy0 {
|
|
+ orientation-switch;
|
|
+ mode-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_typec_ss: endpoint@0 {
|
|
+ reg = <0>;
|
|
+ remote-endpoint = <&usbc0_ss>;
|
|
+ };
|
|
+
|
|
+ usbdp_phy0_typec_sbu: endpoint@1 {
|
|
+ reg = <1>;
|
|
+ remote-endpoint = <&usbc0_sbu>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
+&usbdp_phy0_u3 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&usb_host0_ehci {
|
|
status = "okay";
|
|
};
|
|
@@ -758,6 +896,20 @@ &usb_host0_ohci {
|
|
status = "okay";
|
|
};
|
|
|
|
+&usb_host0_xhci {
|
|
+ usb-role-switch;
|
|
+ status = "okay";
|
|
+
|
|
+ port {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ usb_host0_xhci_drd_sw: endpoint {
|
|
+ remote-endpoint = <&usbc0_hs>;
|
|
+ };
|
|
+ };
|
|
+};
|
|
+
|
|
&usb_host1_ehci {
|
|
status = "okay";
|
|
};
|
|
@@ -765,3 +917,35 @@ &usb_host1_ehci {
|
|
&usb_host1_ohci {
|
|
status = "okay";
|
|
};
|
|
+
|
|
+&usb_host1_xhci {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usb_host2_xhci {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdmi0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdmi0_in_vp0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdptxphy_hdmi0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&hdptxphy_hdmi_clk0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&vop_mmu {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&vop {
|
|
+ status = "okay";
|
|
+};
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588.dtsi b/arch/arm64/boot/dts/rockchip/rk3588.dtsi
|
|
index 5519c1430..900ac0300 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3588.dtsi
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588.dtsi
|
|
@@ -7,6 +7,26 @@
|
|
#include "rk3588-pinctrl.dtsi"
|
|
|
|
/ {
|
|
+ usb_host1_xhci: usb@fc400000 {
|
|
+ compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
|
|
+ reg = <0x0 0xfc400000 0x0 0x400000>;
|
|
+ interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ clocks = <&cru REF_CLK_USB3OTG1>, <&cru SUSPEND_CLK_USB3OTG1>,
|
|
+ <&cru ACLK_USB3OTG1>;
|
|
+ clock-names = "ref_clk", "suspend_clk", "bus_clk";
|
|
+ dr_mode = "host";
|
|
+ phys = <&u2phy1_otg>, <&usbdp_phy1_u3>;
|
|
+ phy-names = "usb2-phy", "usb3-phy";
|
|
+ phy_type = "utmi_wide";
|
|
+ power-domains = <&power RK3588_PD_USB>;
|
|
+ resets = <&cru SRST_A_USB3OTG1>;
|
|
+ snps,dis_enblslpm_quirk;
|
|
+ snps,dis-u2-freeclk-exists-quirk;
|
|
+ snps,dis-del-phy-power-chg-quirk;
|
|
+ snps,dis-tx-ipgap-linecheck-quirk;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
pcie30_phy_grf: syscon@fd5b8000 {
|
|
compatible = "rockchip,rk3588-pcie3-phy-grf", "syscon";
|
|
reg = <0x0 0xfd5b8000 0x0 0x10000>;
|
|
@@ -17,6 +37,37 @@ pipe_phy1_grf: syscon@fd5c0000 {
|
|
reg = <0x0 0xfd5c0000 0x0 0x100>;
|
|
};
|
|
|
|
+ usbdpphy1_grf: syscon@fd5cc000 {
|
|
+ compatible = "rockchip,rk3588-usbdpphy-grf", "syscon";
|
|
+ reg = <0x0 0xfd5cc000 0x0 0x4000>;
|
|
+ };
|
|
+
|
|
+ usb2phy1_grf: syscon@fd5d4000 {
|
|
+ compatible = "rockchip,rk3588-usb2phy-grf", "syscon",
|
|
+ "simple-mfd";
|
|
+ reg = <0x0 0xfd5d4000 0x0 0x4000>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+
|
|
+ u2phy1: usb2-phy@4000 {
|
|
+ compatible = "rockchip,rk3588-usb2phy";
|
|
+ reg = <0x4000 0x10>;
|
|
+ interrupts = <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ resets = <&cru SRST_OTGPHY_U3_1>, <&cru SRST_P_USB2PHY_U3_1_GRF0>;
|
|
+ reset-names = "phy", "apb";
|
|
+ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>;
|
|
+ clock-names = "phyclk";
|
|
+ clock-output-names = "usb480m_phy1";
|
|
+ #clock-cells = <0>;
|
|
+ status = "disabled";
|
|
+
|
|
+ u2phy1_otg: otg-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
i2s8_8ch: i2s@fddc8000 {
|
|
compatible = "rockchip,rk3588-i2s-tdm";
|
|
reg = <0x0 0xfddc8000 0x0 0x1000>;
|
|
@@ -310,6 +361,37 @@ sata-port@0 {
|
|
};
|
|
};
|
|
|
|
+ usbdp_phy1: phy@fed90000 {
|
|
+ compatible = "rockchip,rk3588-usbdp-phy";
|
|
+ reg = <0x0 0xfed90000 0x0 0x10000>;
|
|
+ rockchip,u2phy-grf = <&usb2phy1_grf>;
|
|
+ rockchip,usb-grf = <&usb_grf>;
|
|
+ rockchip,usbdpphy-grf = <&usbdpphy1_grf>;
|
|
+ rockchip,vo-grf = <&vo0_grf>;
|
|
+ clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>,
|
|
+ <&cru CLK_USBDP_PHY1_IMMORTAL>,
|
|
+ <&cru PCLK_USBDPPHY1>,
|
|
+ <&u2phy1>;
|
|
+ clock-names = "refclk", "immortal", "pclk", "utmi";
|
|
+ resets = <&cru SRST_USBDP_COMBO_PHY1_INIT>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY1_CMN>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY1_LANE>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY1_PCS>,
|
|
+ <&cru SRST_P_USBDPPHY1>;
|
|
+ reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb";
|
|
+ status = "disabled";
|
|
+
|
|
+ usbdp_phy1_dp: dp-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usbdp_phy1_u3: usb3-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+
|
|
combphy1_ps: phy@fee10000 {
|
|
compatible = "rockchip,rk3588-naneng-combphy";
|
|
reg = <0x0 0xfee10000 0x0 0x100>;
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts
|
|
index 8347adcbd..58c58ec03 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts
|
|
@@ -114,36 +114,48 @@ vcc_1v1_nldo_s3: vcc-1v1-nldo-s3-regulator {
|
|
};
|
|
};
|
|
|
|
+&combphy2_psu {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&cpu_b0 {
|
|
cpu-supply = <&vdd_cpu_big0_s0>;
|
|
+ mem-supply = <&vdd_cpu_big0_s0>;
|
|
};
|
|
|
|
&cpu_b1 {
|
|
cpu-supply = <&vdd_cpu_big0_s0>;
|
|
+ mem-supply = <&vdd_cpu_big0_s0>;
|
|
};
|
|
|
|
&cpu_b2 {
|
|
cpu-supply = <&vdd_cpu_big1_s0>;
|
|
+ mem-supply = <&vdd_cpu_big1_s0>;
|
|
};
|
|
|
|
&cpu_b3 {
|
|
cpu-supply = <&vdd_cpu_big1_s0>;
|
|
+ mem-supply = <&vdd_cpu_big1_s0>;
|
|
};
|
|
|
|
&cpu_l0 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l1 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l2 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&cpu_l3 {
|
|
cpu-supply = <&vdd_cpu_lit_s0>;
|
|
+ mem-supply = <&vdd_cpu_lit_mem_s0>;
|
|
};
|
|
|
|
&i2c0 {
|
|
@@ -694,6 +706,14 @@ regulator-state-mem {
|
|
};
|
|
};
|
|
|
|
+&u2phy0 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&u2phy0_otg {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&u2phy2 {
|
|
status = "okay";
|
|
};
|
|
@@ -717,6 +737,15 @@ &uart2 {
|
|
status = "okay";
|
|
};
|
|
|
|
+&usbdp_phy0 {
|
|
+ status = "okay";
|
|
+ rockchip,dp-lane-mux = <2 3>;
|
|
+};
|
|
+
|
|
+&usbdp_phy0_u3 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&usb_host0_ehci {
|
|
status = "okay";
|
|
pinctrl-names = "default";
|
|
@@ -727,6 +756,11 @@ &usb_host0_ohci {
|
|
status = "okay";
|
|
};
|
|
|
|
+&usb_host0_xhci {
|
|
+ dr_mode = "host";
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&usb_host1_ehci {
|
|
status = "okay";
|
|
};
|
|
@@ -734,3 +768,7 @@ &usb_host1_ehci {
|
|
&usb_host1_ohci {
|
|
status = "okay";
|
|
};
|
|
+
|
|
+&usb_host2_xhci {
|
|
+ status = "okay";
|
|
+};
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
|
index 7064c0e91..4481a2e57 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
|
|
@@ -8,8 +8,10 @@
|
|
#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/soc/rockchip,vop2.h>
|
|
#include <dt-bindings/phy/phy.h>
|
|
#include <dt-bindings/ata/ahci.h>
|
|
+#include <dt-bindings/thermal/thermal.h>
|
|
|
|
/ {
|
|
compatible = "rockchip,rk3588";
|
|
@@ -18,6 +20,215 @@ / {
|
|
#address-cells = <2>;
|
|
#size-cells = <2>;
|
|
|
|
+ cluster0_opp_table: opp-table-cluster0 {
|
|
+ compatible = "operating-points-v2";
|
|
+ opp-shared;
|
|
+
|
|
+ opp-408000000 {
|
|
+ opp-hz = /bits/ 64 <408000000>;
|
|
+ opp-microvolt = <750000 750000 950000>,
|
|
+ <750000 750000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ opp-suspend;
|
|
+ };
|
|
+ opp-600000000 {
|
|
+ opp-hz = /bits/ 64 <600000000>;
|
|
+ opp-microvolt = <750000 750000 950000>,
|
|
+ <750000 750000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-816000000 {
|
|
+ opp-hz = /bits/ 64 <816000000>;
|
|
+ opp-microvolt = <750000 750000 950000>,
|
|
+ <750000 750000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1008000000 {
|
|
+ opp-hz = /bits/ 64 <1008000000>;
|
|
+ opp-microvolt = <750000 750000 950000>,
|
|
+ <750000 750000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1200000000 {
|
|
+ opp-hz = /bits/ 64 <1200000000>;
|
|
+ opp-microvolt = <775000 775000 950000>,
|
|
+ <775000 775000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1416000000 {
|
|
+ opp-hz = /bits/ 64 <1416000000>;
|
|
+ opp-microvolt = <825000 825000 950000>,
|
|
+ <825000 825000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1608000000 {
|
|
+ opp-hz = /bits/ 64 <1608000000>;
|
|
+ opp-microvolt = <875000 875000 950000>,
|
|
+ <875000 875000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1800000000 {
|
|
+ opp-hz = /bits/ 64 <1800000000>;
|
|
+ opp-microvolt = <950000 950000 950000>,
|
|
+ <950000 950000 950000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ cluster1_opp_table: opp-table-cluster1 {
|
|
+ compatible = "operating-points-v2";
|
|
+ opp-shared;
|
|
+
|
|
+ rockchip,grf = <&bigcore0_grf>;
|
|
+ rockchip,volt-mem-read-margin = <
|
|
+ 855000 1
|
|
+ 765000 2
|
|
+ 675000 3
|
|
+ 495000 4
|
|
+ >;
|
|
+
|
|
+ rockchip,reboot-freq = <1800000000>;
|
|
+
|
|
+ opp-408000000 {
|
|
+ opp-hz = /bits/ 64 <408000000>;
|
|
+ opp-microvolt = <600000 600000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ opp-suspend;
|
|
+ };
|
|
+ opp-600000000 {
|
|
+ opp-hz = /bits/ 64 <600000000>;
|
|
+ opp-microvolt = <600000 600000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-816000000 {
|
|
+ opp-hz = /bits/ 64 <816000000>;
|
|
+ opp-microvolt = <600000 600000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1008000000 {
|
|
+ opp-hz = /bits/ 64 <1008000000>;
|
|
+ opp-microvolt = <625000 625000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1200000000 {
|
|
+ opp-hz = /bits/ 64 <1200000000>;
|
|
+ opp-microvolt = <650000 650000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1416000000 {
|
|
+ opp-hz = /bits/ 64 <1416000000>;
|
|
+ opp-microvolt = <675000 675000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1608000000 {
|
|
+ opp-hz = /bits/ 64 <1608000000>;
|
|
+ opp-microvolt = <700000 700000 1000000>,
|
|
+ <700000 700000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1800000000 {
|
|
+ opp-hz = /bits/ 64 <1800000000>;
|
|
+ opp-microvolt = <775000 775000 1000000>,
|
|
+ <775000 775000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-2016000000 {
|
|
+ opp-hz = /bits/ 64 <2016000000>;
|
|
+ opp-microvolt = <850000 850000 1000000>,
|
|
+ <850000 850000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-2208000000 {
|
|
+ opp-hz = /bits/ 64 <2208000000>;
|
|
+ opp-microvolt = <925000 925000 1000000>,
|
|
+ <925000 925000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ cluster2_opp_table: opp-table-cluster2 {
|
|
+ compatible = "operating-points-v2";
|
|
+ opp-shared;
|
|
+
|
|
+ rockchip,grf = <&bigcore1_grf>;
|
|
+ rockchip,volt-mem-read-margin = <
|
|
+ 855000 1
|
|
+ 765000 2
|
|
+ 675000 3
|
|
+ 495000 4
|
|
+ >;
|
|
+
|
|
+ rockchip,reboot-freq = <1800000000>;
|
|
+
|
|
+ opp-408000000 {
|
|
+ opp-hz = /bits/ 64 <408000000>;
|
|
+ opp-microvolt = <600000 600000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ opp-suspend;
|
|
+ };
|
|
+ opp-600000000 {
|
|
+ opp-hz = /bits/ 64 <600000000>;
|
|
+ opp-microvolt = <600000 600000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-816000000 {
|
|
+ opp-hz = /bits/ 64 <816000000>;
|
|
+ opp-microvolt = <600000 600000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1008000000 {
|
|
+ opp-hz = /bits/ 64 <1008000000>;
|
|
+ opp-microvolt = <625000 625000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1200000000 {
|
|
+ opp-hz = /bits/ 64 <1200000000>;
|
|
+ opp-microvolt = <650000 650000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1416000000 {
|
|
+ opp-hz = /bits/ 64 <1416000000>;
|
|
+ opp-microvolt = <675000 675000 1000000>,
|
|
+ <675000 675000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1608000000 {
|
|
+ opp-hz = /bits/ 64 <1608000000>;
|
|
+ opp-microvolt = <700000 700000 1000000>,
|
|
+ <700000 700000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-1800000000 {
|
|
+ opp-hz = /bits/ 64 <1800000000>;
|
|
+ opp-microvolt = <775000 775000 1000000>,
|
|
+ <775000 775000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-2016000000 {
|
|
+ opp-hz = /bits/ 64 <2016000000>;
|
|
+ opp-microvolt = <850000 850000 1000000>,
|
|
+ <850000 850000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ opp-2208000000 {
|
|
+ opp-hz = /bits/ 64 <2208000000>;
|
|
+ opp-microvolt = <925000 925000 1000000>,
|
|
+ <925000 925000 1000000>;
|
|
+ clock-latency-ns = <40000>;
|
|
+ };
|
|
+ };
|
|
+
|
|
cpus {
|
|
#address-cells = <1>;
|
|
#size-cells = <0>;
|
|
@@ -64,6 +275,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>;
|
|
@@ -83,6 +295,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>;
|
|
@@ -102,6 +315,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>;
|
|
@@ -121,6 +335,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>;
|
|
@@ -142,6 +357,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>;
|
|
@@ -161,6 +377,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>;
|
|
@@ -182,6 +399,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>;
|
|
@@ -201,6 +419,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>;
|
|
@@ -362,6 +581,235 @@ spll: clock-0 {
|
|
#clock-cells = <0>;
|
|
};
|
|
|
|
+ display_subsystem: display-subsystem {
|
|
+ compatible = "rockchip,display-subsystem";
|
|
+ ports = <&vop_out>;
|
|
+ };
|
|
+
|
|
+ thermal_zones: thermal-zones {
|
|
+ soc_thermal: soc-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ sustainable-power = <2100>; /* milliwatts */
|
|
+
|
|
+ thermal-sensors = <&tsadc 0>;
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ soc_target: trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ cooling-maps {
|
|
+ map0 {
|
|
+ trip = <&soc_target>;
|
|
+ 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>,
|
|
+ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
|
+ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
|
+ <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
|
+ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
|
+ contribution = <1024>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ bigcore0_thermal: bigcore0-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ thermal-sensors = <&tsadc 1>;
|
|
+
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ b0_target: trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ cooling-maps {
|
|
+ map0 {
|
|
+ trip = <&b0_target>;
|
|
+ cooling-device = <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
|
+ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
|
+ contribution = <1024>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ bigcore1_thermal: bigcore1-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ thermal-sensors = <&tsadc 2>;
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ b1_target: trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ cooling-maps {
|
|
+ map0 {
|
|
+ trip = <&b1_target>;
|
|
+ cooling-device = <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
|
|
+ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
|
+ contribution = <1024>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ little_core_thermal: littlecore-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ thermal-sensors = <&tsadc 3>;
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ l0_target: trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ cooling-maps {
|
|
+ map0 {
|
|
+ trip = <&l0_target>;
|
|
+ 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>;
|
|
+ contribution = <1024>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ center_thermal: center-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ thermal-sensors = <&tsadc 4>;
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ gpu_thermal: gpu-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ thermal-sensors = <&tsadc 5>;
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ npu_thermal: npu-thermal {
|
|
+ polling-delay-passive = <20>; /* milliseconds */
|
|
+ polling-delay = <1000>; /* milliseconds */
|
|
+ thermal-sensors = <&tsadc 6>;
|
|
+ trips {
|
|
+ trip-point-0 {
|
|
+ temperature = <75000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-1 {
|
|
+ temperature = <85000>;
|
|
+ hysteresis = <2000>;
|
|
+ type = "passive";
|
|
+ };
|
|
+ trip-point-2 {
|
|
+ /* millicelsius */
|
|
+ temperature = <115000>;
|
|
+ /* millicelsius */
|
|
+ hysteresis = <2000>;
|
|
+ type = "critical";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
timer {
|
|
compatible = "arm,armv8-timer";
|
|
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH 0>,
|
|
@@ -399,6 +847,28 @@ scmi_shmem: sram@0 {
|
|
};
|
|
};
|
|
|
|
+ usb_host0_xhci: usb@fc000000 {
|
|
+ compatible = "rockchip,rk3588-dwc3", "snps,dwc3";
|
|
+ reg = <0x0 0xfc000000 0x0 0x400000>;
|
|
+ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ clocks = <&cru REF_CLK_USB3OTG0>, <&cru SUSPEND_CLK_USB3OTG0>,
|
|
+ <&cru ACLK_USB3OTG0>;
|
|
+ clock-names = "ref_clk", "suspend_clk", "bus_clk";
|
|
+ dr_mode = "otg";
|
|
+ phys = <&u2phy0_otg>, <&usbdp_phy0_u3>;
|
|
+ phy-names = "usb2-phy", "usb3-phy";
|
|
+ phy_type = "utmi_wide";
|
|
+ power-domains = <&power RK3588_PD_USB>;
|
|
+ resets = <&cru SRST_A_USB3OTG0>;
|
|
+ snps,dis_enblslpm_quirk;
|
|
+ snps,dis-u1-entry-quirk;
|
|
+ snps,dis-u2-entry-quirk;
|
|
+ snps,dis-u2-freeclk-exists-quirk;
|
|
+ snps,dis-del-phy-power-chg-quirk;
|
|
+ snps,dis-tx-ipgap-linecheck-quirk;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
usb_host0_ehci: usb@fc800000 {
|
|
compatible = "rockchip,rk3588-ehci", "generic-ehci";
|
|
reg = <0x0 0xfc800000 0x0 0x40000>;
|
|
@@ -474,6 +944,16 @@ sys_grf: syscon@fd58c000 {
|
|
reg = <0x0 0xfd58c000 0x0 0x1000>;
|
|
};
|
|
|
|
+ bigcore0_grf: syscon@fd590000 {
|
|
+ compatible = "rockchip,rk3588-bigcore0-grf", "syscon";
|
|
+ reg = <0x0 0xfd590000 0x0 0x100>;
|
|
+ };
|
|
+
|
|
+ bigcore1_grf: syscon@fd592000 {
|
|
+ compatible = "rockchip,rk3588-bigcore1-grf", "syscon";
|
|
+ reg = <0x0 0xfd592000 0x0 0x100>;
|
|
+ };
|
|
+
|
|
php_grf: syscon@fd5b0000 {
|
|
compatible = "rockchip,rk3588-php-grf", "syscon";
|
|
reg = <0x0 0xfd5b0000 0x0 0x1000>;
|
|
@@ -489,6 +969,37 @@ pipe_phy2_grf: syscon@fd5c4000 {
|
|
reg = <0x0 0xfd5c4000 0x0 0x100>;
|
|
};
|
|
|
|
+ usbdpphy0_grf: syscon@fd5c8000 {
|
|
+ compatible = "rockchip,rk3588-usbdpphy-grf", "syscon";
|
|
+ reg = <0x0 0xfd5c8000 0x0 0x4000>;
|
|
+ };
|
|
+
|
|
+ usb2phy0_grf: syscon@fd5d0000 {
|
|
+ compatible = "rockchip,rk3588-usb2phy-grf", "syscon",
|
|
+ "simple-mfd";
|
|
+ reg = <0x0 0xfd5d0000 0x0 0x4000>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <1>;
|
|
+
|
|
+ u2phy0: usb2-phy@0 {
|
|
+ compatible = "rockchip,rk3588-usb2phy";
|
|
+ reg = <0x0 0x10>;
|
|
+ interrupts = <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ resets = <&cru SRST_OTGPHY_U3_0>, <&cru SRST_P_USB2PHY_U3_0_GRF0>;
|
|
+ reset-names = "phy", "apb";
|
|
+ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>;
|
|
+ clock-names = "phyclk";
|
|
+ clock-output-names = "usb480m_phy0";
|
|
+ #clock-cells = <0>;
|
|
+ status = "disabled";
|
|
+
|
|
+ u2phy0_otg: otg-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
usb2phy2_grf: syscon@fd5d8000 {
|
|
compatible = "rockchip,rk3588-usb2phy-grf", "syscon", "simple-mfd";
|
|
reg = <0x0 0xfd5d8000 0x0 0x4000>;
|
|
@@ -514,6 +1025,28 @@ u2phy2_host: host-port {
|
|
};
|
|
};
|
|
|
|
+ vop_grf: syscon@fd5a4000 {
|
|
+ compatible = "rockchip,rk3588-vop-grf", "syscon";
|
|
+ reg = <0x0 0xfd5a4000 0x0 0x2000>;
|
|
+ };
|
|
+
|
|
+ vo0_grf: syscon@fd5a6000 {
|
|
+ compatible = "rockchip,rk3588-vo-grf", "syscon";
|
|
+ reg = <0x0 0xfd5a6000 0x0 0x2000>;
|
|
+ clocks = <&cru PCLK_VO0GRF>;
|
|
+ };
|
|
+
|
|
+ vo1_grf: syscon@fd5a8000 {
|
|
+ compatible = "rockchip,rk3588-vo-grf", "syscon";
|
|
+ reg = <0x0 0xfd5a8000 0x0 0x100>;
|
|
+ clocks = <&cru PCLK_VO1GRF>;
|
|
+ };
|
|
+
|
|
+ usb_grf: syscon@fd5ac000 {
|
|
+ compatible = "rockchip,rk3588-usb-grf", "syscon";
|
|
+ reg = <0x0 0xfd5ac000 0x0 0x4000>;
|
|
+ };
|
|
+
|
|
usb2phy3_grf: syscon@fd5dc000 {
|
|
compatible = "rockchip,rk3588-usb2phy-grf", "syscon", "simple-mfd";
|
|
reg = <0x0 0xfd5dc000 0x0 0x4000>;
|
|
@@ -539,6 +1072,11 @@ u2phy3_host: host-port {
|
|
};
|
|
};
|
|
|
|
+ hdptxphy0_grf: syscon@fd5e0000 {
|
|
+ compatible = "rockchip,rk3588-hdptxphy-grf", "syscon";
|
|
+ reg = <0x0 0xfd5e0000 0x0 0x100>;
|
|
+ };
|
|
+
|
|
ioc: syscon@fd5f0000 {
|
|
compatible = "rockchip,rk3588-ioc", "syscon";
|
|
reg = <0x0 0xfd5f0000 0x0 0x10000>;
|
|
@@ -962,6 +1500,112 @@ power-domain@RK3588_PD_SDMMC {
|
|
};
|
|
};
|
|
|
|
+ vop: vop@fdd90000 {
|
|
+ compatible = "rockchip,rk3588-vop";
|
|
+ reg = <0x0 0xfdd90000 0x0 0x4200>, <0x0 0xfdd95000 0x0 0x1000>;
|
|
+ reg-names = "vop", "gamma_lut";
|
|
+ interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ clocks = <&cru ACLK_VOP>,
|
|
+ <&cru HCLK_VOP>,
|
|
+ <&cru DCLK_VOP0>,
|
|
+ <&cru DCLK_VOP1>,
|
|
+ <&cru DCLK_VOP2>,
|
|
+ <&cru DCLK_VOP3>,
|
|
+ <&cru PCLK_VOP_ROOT>,
|
|
+ <&cru DCLK_VOP0_SRC>,
|
|
+ <&cru DCLK_VOP1_SRC>,
|
|
+ <&cru DCLK_VOP2_SRC>;
|
|
+ clock-names = "aclk",
|
|
+ "hclk",
|
|
+ "dclk_vp0",
|
|
+ "dclk_vp1",
|
|
+ "dclk_vp2",
|
|
+ "dclk_vp3",
|
|
+ "pclk",
|
|
+ "dclk_src_vp0",
|
|
+ "dclk_src_vp1",
|
|
+ "dclk_src_vp2";
|
|
+ resets = <&cru SRST_A_VOP>,
|
|
+ <&cru SRST_H_VOP>,
|
|
+ <&cru SRST_D_VOP0>,
|
|
+ <&cru SRST_D_VOP1>,
|
|
+ <&cru SRST_D_VOP2>,
|
|
+ <&cru SRST_D_VOP3>;
|
|
+ reset-names = "axi",
|
|
+ "ahb",
|
|
+ "dclk_vp0",
|
|
+ "dclk_vp1",
|
|
+ "dclk_vp2",
|
|
+ "dclk_vp3";
|
|
+ iommus = <&vop_mmu>;
|
|
+ power-domains = <&power RK3588_PD_VOP>;
|
|
+ rockchip,grf = <&sys_grf>;
|
|
+ rockchip,vop-grf = <&vop_grf>;
|
|
+ rockchip,vo1-grf = <&vo1_grf>;
|
|
+ rockchip,pmu = <&pmu>;
|
|
+
|
|
+ status = "disabled";
|
|
+
|
|
+ // [CC: move endpoints to rock5b dts, see rock-3a]
|
|
+ vop_out: ports {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ vp0: port@0 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ reg = <0>;
|
|
+
|
|
+ vp0_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
|
|
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
|
|
+ remote-endpoint = <&hdmi0_in_vp0>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ vp1: port@1 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ reg = <1>;
|
|
+
|
|
+ vp1_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
|
|
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
|
|
+ remote-endpoint = <&hdmi0_in_vp1>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ vp2: port@2 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ reg = <2>;
|
|
+
|
|
+ assigned-clocks = <&cru DCLK_VOP2_SRC>;
|
|
+ assigned-clock-parents = <&cru PLL_V0PLL>;
|
|
+
|
|
+ vp2_out_hdmi0: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
|
|
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
|
|
+ remote-endpoint = <&hdmi0_in_vp2>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ vp3: port@3 {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+ reg = <3>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+ vop_mmu: iommu@fdd97e00 {
|
|
+ compatible = "rockchip,rk3568-iommu";
|
|
+ reg = <0x0 0xfdd97e00 0x0 0x100>, <0x0 0xfdd97f00 0x0 0x100>;
|
|
+ interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH 0>;
|
|
+ interrupt-names = "vop_mmu";
|
|
+ clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>;
|
|
+ clock-names = "aclk", "iface";
|
|
+ #iommu-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
i2s4_8ch: i2s@fddc0000 {
|
|
compatible = "rockchip,rk3588-i2s-tdm";
|
|
reg = <0x0 0xfddc0000 0x0 0x1000>;
|
|
@@ -1013,6 +1657,77 @@ 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 DCLK_VOP0>,
|
|
+ <&cru DCLK_VOP1>,
|
|
+ <&cru DCLK_VOP2>,
|
|
+ <&cru DCLK_VOP3>,
|
|
+ <&cru HCLK_VO1>;
|
|
+ clock-names = "pclk",
|
|
+ "hpd",
|
|
+ "earc",
|
|
+ "hdmitx_ref",
|
|
+ "aud",
|
|
+ "dclk_vp0",
|
|
+ "dclk_vp1",
|
|
+ "dclk_vp2",
|
|
+ "dclk_vp3",
|
|
+ "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>;
|
|
+ reg-io-width = <4>;
|
|
+ rockchip,grf = <&sys_grf>;
|
|
+ rockchip,vo1_grf = <&vo1_grf>;
|
|
+ phys = <&hdptxphy_hdmi0>;
|
|
+ phy-names = "hdmi";
|
|
+ #sound-dai-cells = <0>;
|
|
+ status = "disabled";
|
|
+
|
|
+ ports {
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ hdmi0_in: port@0 {
|
|
+ reg = <0>;
|
|
+ #address-cells = <1>;
|
|
+ #size-cells = <0>;
|
|
+
|
|
+ hdmi0_in_vp0: endpoint@0 {
|
|
+ reg = <0>;
|
|
+ remote-endpoint = <&vp0_out_hdmi0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ hdmi0_in_vp1: endpoint@1 {
|
|
+ reg = <1>;
|
|
+ remote-endpoint = <&vp1_out_hdmi0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ hdmi0_in_vp2: endpoint@2 {
|
|
+ reg = <2>;
|
|
+ remote-endpoint = <&vp2_out_hdmi0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
qos_gpu_m0: qos@fdf35000 {
|
|
compatible = "rockchip,rk3588-qos", "syscon";
|
|
reg = <0x0 0xfdf35000 0x0 0x20>;
|
|
@@ -2110,7 +2825,6 @@ tsadc: tsadc@fec00000 {
|
|
pinctrl-1 = <&tsadc_shut>;
|
|
pinctrl-names = "gpio", "otpout";
|
|
#thermal-sensor-cells = <1>;
|
|
- status = "disabled";
|
|
};
|
|
|
|
saradc: adc@fec10000 {
|
|
@@ -2245,6 +2959,58 @@ dmac2: dma-controller@fed10000 {
|
|
#dma-cells = <1>;
|
|
};
|
|
|
|
+ hdptxphy_hdmi0: hdmiphy@fed60000 {
|
|
+ compatible = "rockchip,rk3588-hdptx-phy-hdmi";
|
|
+ reg = <0x0 0xfed60000 0x0 0x2000>;
|
|
+ clocks = <&cru CLK_USB2PHY_HDPTXRXPHY_REF>, <&cru PCLK_HDPTX0>;
|
|
+ clock-names = "ref", "apb";
|
|
+ resets = <&cru SRST_HDPTX0>, <&cru SRST_P_HDPTX0>,
|
|
+ <&cru SRST_HDPTX0_INIT>, <&cru SRST_HDPTX0_CMN>,
|
|
+ <&cru SRST_HDPTX0_LANE>, <&cru SRST_HDPTX0_ROPLL>,
|
|
+ <&cru SRST_HDPTX0_LCPLL>;
|
|
+ reset-names = "phy", "apb", "init", "cmn", "lane", "ropll",
|
|
+ "lcpll";
|
|
+ rockchip,grf = <&hdptxphy0_grf>;
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+
|
|
+ hdptxphy_hdmi_clk0: clk-port {
|
|
+ #clock-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ usbdp_phy0: phy@fed80000 {
|
|
+ compatible = "rockchip,rk3588-usbdp-phy";
|
|
+ reg = <0x0 0xfed80000 0x0 0x10000>;
|
|
+ rockchip,u2phy-grf = <&usb2phy0_grf>;
|
|
+ rockchip,usb-grf = <&usb_grf>;
|
|
+ rockchip,usbdpphy-grf = <&usbdpphy0_grf>;
|
|
+ rockchip,vo-grf = <&vo0_grf>;
|
|
+ clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>,
|
|
+ <&cru CLK_USBDP_PHY0_IMMORTAL>,
|
|
+ <&cru PCLK_USBDPPHY0>,
|
|
+ <&u2phy0>;
|
|
+ clock-names = "refclk", "immortal", "pclk", "utmi";
|
|
+ resets = <&cru SRST_USBDP_COMBO_PHY0_INIT>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY0_CMN>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY0_LANE>,
|
|
+ <&cru SRST_USBDP_COMBO_PHY0_PCS>,
|
|
+ <&cru SRST_P_USBDPPHY0>;
|
|
+ reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb";
|
|
+ status = "disabled";
|
|
+
|
|
+ usbdp_phy0_dp: dp-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usbdp_phy0_u3: usb3-port {
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+
|
|
combphy0_ps: phy@fee00000 {
|
|
compatible = "rockchip,rk3588-naneng-combphy";
|
|
reg = <0x0 0xfee00000 0x0 0x100>;
|
|
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
|
|
index b60aa1f89..906f2f21a 100644
|
|
--- a/arch/arm64/configs/defconfig
|
|
+++ b/arch/arm64/configs/defconfig
|
|
@@ -1449,8 +1449,9 @@ CONFIG_PHY_ROCKCHIP_EMMC=y
|
|
CONFIG_PHY_ROCKCHIP_INNO_HDMI=m
|
|
CONFIG_PHY_ROCKCHIP_INNO_USB2=y
|
|
CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=m
|
|
-CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=m
|
|
+CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y
|
|
CONFIG_PHY_ROCKCHIP_PCIE=m
|
|
+CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI=m
|
|
CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y
|
|
CONFIG_PHY_ROCKCHIP_TYPEC=y
|
|
CONFIG_PHY_SAMSUNG_UFS=y
|
|
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
|
|
index 66759fe28..478a4e594 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) {
|
|
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
|
|
index a2c2b5203..94b4fb66a 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;
|
|
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
|
|
index 6994165e0..c6b605d6d 100644
|
|
--- a/drivers/clk/rockchip/clk-rk3588.c
|
|
+++ b/drivers/clk/rockchip/clk-rk3588.c
|
|
@@ -1851,8 +1851,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
|
|
RK3588_CLKGATE_CON(56), 0, GFLAGS),
|
|
GATE(PCLK_TRNG0, "pclk_trng0", "pclk_vo0_root", 0,
|
|
RK3588_CLKGATE_CON(56), 1, GFLAGS),
|
|
- GATE(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", CLK_IGNORE_UNUSED,
|
|
- RK3588_CLKGATE_CON(55), 10, GFLAGS),
|
|
COMPOSITE(CLK_I2S4_8CH_TX_SRC, "clk_i2s4_8ch_tx_src", gpll_aupll_p, 0,
|
|
RK3588_CLKSEL_CON(118), 5, 1, MFLAGS, 0, 5, DFLAGS,
|
|
RK3588_CLKGATE_CON(56), 11, GFLAGS),
|
|
@@ -1998,8 +1996,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
|
|
RK3588_CLKGATE_CON(60), 9, GFLAGS),
|
|
GATE(PCLK_TRNG1, "pclk_trng1", "pclk_vo1_root", 0,
|
|
RK3588_CLKGATE_CON(60), 10, GFLAGS),
|
|
- GATE(0, "pclk_vo1grf", "pclk_vo1_root", CLK_IGNORE_UNUSED,
|
|
- RK3588_CLKGATE_CON(59), 12, GFLAGS),
|
|
GATE(PCLK_S_EDP0, "pclk_s_edp0", "pclk_vo1_s_root", 0,
|
|
RK3588_CLKGATE_CON(59), 14, GFLAGS),
|
|
GATE(PCLK_S_EDP1, "pclk_s_edp1", "pclk_vo1_s_root", 0,
|
|
@@ -2447,12 +2443,15 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
|
|
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", 0, RK3588_CLKGATE_CON(55), 5, GFLAGS),
|
|
+ GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", "hclk_vop_root", RK3588_LINKED_CLK, 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", 0, RK3588_CLKGATE_CON(59), 9, 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(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),
|
|
+ GATE_LINK(PCLK_VO0GRF, "pclk_vo0grf", "pclk_vo0_root", "hclk_vo0", CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(55), 10, GFLAGS),
|
|
+ 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)
|
|
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
|
|
index f91160689..1e2552108 100644
|
|
--- a/drivers/cpufreq/Kconfig.arm
|
|
+++ b/drivers/cpufreq/Kconfig.arm
|
|
@@ -189,6 +189,16 @@ config ARM_RASPBERRYPI_CPUFREQ
|
|
|
|
If in doubt, say N.
|
|
|
|
+config ARM_ROCKCHIP_CPUFREQ
|
|
+ tristate "Rockchip CPUfreq driver"
|
|
+ depends on ARCH_ROCKCHIP && CPUFREQ_DT
|
|
+ select PM_OPP
|
|
+ help
|
|
+ This adds the CPUFreq driver support for Rockchip SoCs,
|
|
+ based on cpufreq-dt.
|
|
+
|
|
+ If in doubt, say N.
|
|
+
|
|
config ARM_S3C64XX_CPUFREQ
|
|
bool "Samsung S3C64XX"
|
|
depends on CPU_S3C6410
|
|
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
|
|
index 8d141c71b..14fb48863 100644
|
|
--- a/drivers/cpufreq/Makefile
|
|
+++ b/drivers/cpufreq/Makefile
|
|
@@ -71,6 +71,7 @@ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
|
|
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
|
|
obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o
|
|
obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o
|
|
+obj-$(CONFIG_ARM_ROCKCHIP_CPUFREQ) += rockchip-cpufreq.o
|
|
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
|
|
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
|
|
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
|
|
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
|
|
index bd1e1357c..cfd35aa52 100644
|
|
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
|
|
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
|
|
@@ -168,6 +168,8 @@ static const struct of_device_id blocklist[] __initconst = {
|
|
{ .compatible = "qcom,sm8450", },
|
|
{ .compatible = "qcom,sm8550", },
|
|
|
|
+ { .compatible = "rockchip,rk3588", },
|
|
+
|
|
{ .compatible = "st,stih407", },
|
|
{ .compatible = "st,stih410", },
|
|
{ .compatible = "st,stih418", },
|
|
diff --git a/drivers/cpufreq/rockchip-cpufreq.c b/drivers/cpufreq/rockchip-cpufreq.c
|
|
new file mode 100644
|
|
index 000000000..0bf57ac85
|
|
--- /dev/null
|
|
+++ b/drivers/cpufreq/rockchip-cpufreq.c
|
|
@@ -0,0 +1,645 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * Rockchip CPUFreq Driver. This is similar to the generic DT
|
|
+ * cpufreq driver, but handles the following platform specific
|
|
+ * quirks:
|
|
+ *
|
|
+ * * support for two regulators - one for the CPU core and one
|
|
+ * for the memory interface
|
|
+ * * reboot handler to setup the reboot frequency
|
|
+ * * handling of read margin registers
|
|
+ *
|
|
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
|
|
+ * Copyright (C) 2023 Collabora Ltd.
|
|
+ */
|
|
+
|
|
+#include <linux/cpu.h>
|
|
+#include <linux/cpufreq.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/pm_opp.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/reboot.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+
|
|
+#include "cpufreq-dt.h"
|
|
+
|
|
+#define RK3588_MEMCFG_HSSPRF_LOW 0x20
|
|
+#define RK3588_MEMCFG_HSDPRF_LOW 0x28
|
|
+#define RK3588_MEMCFG_HSDPRF_HIGH 0x2c
|
|
+#define RK3588_CPU_CTRL 0x30
|
|
+
|
|
+#define VOLT_RM_TABLE_END ~1
|
|
+
|
|
+static struct platform_device *cpufreq_pdev;
|
|
+static LIST_HEAD(priv_list);
|
|
+
|
|
+struct volt_rm_table {
|
|
+ uint32_t volt;
|
|
+ uint32_t rm;
|
|
+};
|
|
+
|
|
+struct rockchip_opp_info {
|
|
+ const struct rockchip_opp_data *data;
|
|
+ struct volt_rm_table *volt_rm_tbl;
|
|
+ struct regmap *grf;
|
|
+ u32 current_rm;
|
|
+ u32 reboot_freq;
|
|
+};
|
|
+
|
|
+struct private_data {
|
|
+ struct list_head node;
|
|
+
|
|
+ cpumask_var_t cpus;
|
|
+ struct device *cpu_dev;
|
|
+ struct cpufreq_frequency_table *freq_table;
|
|
+};
|
|
+
|
|
+struct rockchip_opp_data {
|
|
+ int (*set_read_margin)(struct device *dev, struct rockchip_opp_info *opp_info,
|
|
+ unsigned long volt);
|
|
+};
|
|
+
|
|
+struct cluster_info {
|
|
+ struct list_head list_head;
|
|
+ struct rockchip_opp_info opp_info;
|
|
+ cpumask_t cpus;
|
|
+};
|
|
+static LIST_HEAD(cluster_info_list);
|
|
+
|
|
+static int rk3588_cpu_set_read_margin(struct device *dev, struct rockchip_opp_info *opp_info,
|
|
+ unsigned long volt)
|
|
+{
|
|
+ bool is_found = false;
|
|
+ u32 rm;
|
|
+ int i;
|
|
+
|
|
+ if (!opp_info->volt_rm_tbl)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; opp_info->volt_rm_tbl[i].rm != VOLT_RM_TABLE_END; i++) {
|
|
+ if (volt >= opp_info->volt_rm_tbl[i].volt) {
|
|
+ rm = opp_info->volt_rm_tbl[i].rm;
|
|
+ is_found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!is_found)
|
|
+ return 0;
|
|
+ if (rm == opp_info->current_rm)
|
|
+ return 0;
|
|
+ if (!opp_info->grf)
|
|
+ return 0;
|
|
+
|
|
+ dev_dbg(dev, "set rm to %d\n", rm);
|
|
+ regmap_write(opp_info->grf, RK3588_MEMCFG_HSSPRF_LOW, 0x001c0000 | (rm << 2));
|
|
+ regmap_write(opp_info->grf, RK3588_MEMCFG_HSDPRF_LOW, 0x003c0000 | (rm << 2));
|
|
+ regmap_write(opp_info->grf, RK3588_MEMCFG_HSDPRF_HIGH, 0x003c0000 | (rm << 2));
|
|
+ regmap_write(opp_info->grf, RK3588_CPU_CTRL, 0x00200020);
|
|
+ udelay(1);
|
|
+ regmap_write(opp_info->grf, RK3588_CPU_CTRL, 0x00200000);
|
|
+
|
|
+ opp_info->current_rm = rm;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct rockchip_opp_data rk3588_cpu_opp_data = {
|
|
+ .set_read_margin = rk3588_cpu_set_read_margin,
|
|
+};
|
|
+
|
|
+static const struct of_device_id rockchip_cpufreq_of_match[] = {
|
|
+ {
|
|
+ .compatible = "rockchip,rk3588",
|
|
+ .data = (void *)&rk3588_cpu_opp_data,
|
|
+ },
|
|
+ {},
|
|
+};
|
|
+
|
|
+static struct cluster_info *rockchip_cluster_info_lookup(int cpu)
|
|
+{
|
|
+ struct cluster_info *cluster;
|
|
+
|
|
+ list_for_each_entry(cluster, &cluster_info_list, list_head) {
|
|
+ if (cpumask_test_cpu(cpu, &cluster->cpus))
|
|
+ return cluster;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int rockchip_cpufreq_set_volt(struct device *dev,
|
|
+ struct regulator *reg,
|
|
+ struct dev_pm_opp_supply *supply)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
|
|
+ supply->u_volt, supply->u_volt_max);
|
|
+ if (ret)
|
|
+ dev_err(dev, "%s: failed to set voltage (%lu %lu %lu uV): %d\n",
|
|
+ __func__, supply->u_volt_min, supply->u_volt,
|
|
+ supply->u_volt_max, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rockchip_cpufreq_set_read_margin(struct device *dev,
|
|
+ struct rockchip_opp_info *opp_info,
|
|
+ unsigned long volt)
|
|
+{
|
|
+ if (opp_info->data && opp_info->data->set_read_margin) {
|
|
+ opp_info->data->set_read_margin(dev, opp_info, volt);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rk_opp_config_regulators(struct device *dev,
|
|
+ struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
|
|
+ struct regulator **regulators, unsigned int count)
|
|
+{
|
|
+ struct dev_pm_opp_supply old_supplies[2];
|
|
+ struct dev_pm_opp_supply new_supplies[2];
|
|
+ struct regulator *vdd_reg = regulators[0];
|
|
+ struct regulator *mem_reg = regulators[1];
|
|
+ struct rockchip_opp_info *opp_info;
|
|
+ struct cluster_info *cluster;
|
|
+ int ret = 0;
|
|
+ unsigned long old_freq = dev_pm_opp_get_freq(old_opp);
|
|
+ unsigned long new_freq = dev_pm_opp_get_freq(new_opp);
|
|
+
|
|
+ /* We must have two regulators here */
|
|
+ WARN_ON(count != 2);
|
|
+
|
|
+ ret = dev_pm_opp_get_supplies(old_opp, old_supplies);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = dev_pm_opp_get_supplies(new_opp, new_supplies);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ cluster = rockchip_cluster_info_lookup(dev->id);
|
|
+ if (!cluster)
|
|
+ return -EINVAL;
|
|
+ opp_info = &cluster->opp_info;
|
|
+
|
|
+ if (new_freq >= old_freq) {
|
|
+ ret = rockchip_cpufreq_set_volt(dev, mem_reg, &new_supplies[1]);
|
|
+ if (ret)
|
|
+ goto error;
|
|
+ ret = rockchip_cpufreq_set_volt(dev, vdd_reg, &new_supplies[0]);
|
|
+ if (ret)
|
|
+ goto error;
|
|
+ rockchip_cpufreq_set_read_margin(dev, opp_info, new_supplies[0].u_volt);
|
|
+ } else {
|
|
+ rockchip_cpufreq_set_read_margin(dev, opp_info, new_supplies[0].u_volt);
|
|
+ ret = rockchip_cpufreq_set_volt(dev, vdd_reg, &new_supplies[0]);
|
|
+ if (ret)
|
|
+ goto error;
|
|
+ ret = rockchip_cpufreq_set_volt(dev, mem_reg, &new_supplies[1]);
|
|
+ if (ret)
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ rockchip_cpufreq_set_read_margin(dev, opp_info, old_supplies[0].u_volt);
|
|
+ rockchip_cpufreq_set_volt(dev, mem_reg, &old_supplies[1]);
|
|
+ rockchip_cpufreq_set_volt(dev, vdd_reg, &old_supplies[0]);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void rockchip_get_opp_data(const struct of_device_id *matches,
|
|
+ struct rockchip_opp_info *info)
|
|
+{
|
|
+ const struct of_device_id *match;
|
|
+ struct device_node *node;
|
|
+
|
|
+ node = of_find_node_by_path("/");
|
|
+ match = of_match_node(matches, node);
|
|
+ if (match && match->data)
|
|
+ info->data = match->data;
|
|
+ of_node_put(node);
|
|
+}
|
|
+
|
|
+static int rockchip_get_volt_rm_table(struct device *dev, struct device_node *np,
|
|
+ char *porp_name, struct volt_rm_table **table)
|
|
+{
|
|
+ struct volt_rm_table *rm_table;
|
|
+ const struct property *prop;
|
|
+ int count, i;
|
|
+
|
|
+ prop = of_find_property(np, porp_name, NULL);
|
|
+ if (!prop)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!prop->value)
|
|
+ return -ENODATA;
|
|
+
|
|
+ count = of_property_count_u32_elems(np, porp_name);
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (count % 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ rm_table = devm_kzalloc(dev, sizeof(*rm_table) * (count / 2 + 1),
|
|
+ GFP_KERNEL);
|
|
+ if (!rm_table)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < count / 2; i++) {
|
|
+ of_property_read_u32_index(np, porp_name, 2 * i,
|
|
+ &rm_table[i].volt);
|
|
+ of_property_read_u32_index(np, porp_name, 2 * i + 1,
|
|
+ &rm_table[i].rm);
|
|
+ }
|
|
+
|
|
+ rm_table[i].volt = 0;
|
|
+ rm_table[i].rm = VOLT_RM_TABLE_END;
|
|
+
|
|
+ *table = rm_table;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rockchip_cpufreq_reboot(struct notifier_block *notifier, unsigned long event, void *cmd)
|
|
+{
|
|
+ struct cluster_info *cluster;
|
|
+ struct device *dev;
|
|
+ int freq, ret, cpu;
|
|
+
|
|
+ if (event != SYS_RESTART)
|
|
+ return NOTIFY_DONE;
|
|
+
|
|
+ for_each_possible_cpu(cpu) {
|
|
+ cluster = rockchip_cluster_info_lookup(cpu);
|
|
+ if (!cluster)
|
|
+ continue;
|
|
+
|
|
+ dev = get_cpu_device(cpu);
|
|
+ if (!dev)
|
|
+ continue;
|
|
+
|
|
+ freq = cluster->opp_info.reboot_freq;
|
|
+
|
|
+ if (freq) {
|
|
+ ret = dev_pm_opp_set_rate(dev, freq);
|
|
+ if (ret)
|
|
+ dev_err(dev, "Failed setting reboot freq for cpu %d to %d: %d\n",
|
|
+ cpu, freq, ret);
|
|
+ dev_pm_opp_remove_table(dev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster)
|
|
+{
|
|
+ struct rockchip_opp_info *opp_info = &cluster->opp_info;
|
|
+ int reg_table_token = -EINVAL;
|
|
+ int opp_table_token = -EINVAL;
|
|
+ struct device_node *np;
|
|
+ struct device *dev;
|
|
+ const char * const reg_names[] = { "cpu", "mem", NULL };
|
|
+ int ret = 0;
|
|
+
|
|
+ dev = get_cpu_device(cpu);
|
|
+ if (!dev)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (!of_find_property(dev->of_node, "cpu-supply", NULL))
|
|
+ return -ENOENT;
|
|
+
|
|
+ np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
|
|
+ if (!np) {
|
|
+ dev_warn(dev, "OPP-v2 not supported\n");
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
+ reg_table_token = dev_pm_opp_set_regulators(dev, reg_names);
|
|
+ if (reg_table_token < 0) {
|
|
+ ret = reg_table_token;
|
|
+ dev_err_probe(dev, ret, "Failed to set opp regulators\n");
|
|
+ goto np_err;
|
|
+ }
|
|
+
|
|
+ ret = dev_pm_opp_of_get_sharing_cpus(dev, &cluster->cpus);
|
|
+ if (ret) {
|
|
+ dev_err_probe(dev, ret, "Failed to get sharing cpus\n");
|
|
+ goto np_err;
|
|
+ }
|
|
+
|
|
+ rockchip_get_opp_data(rockchip_cpufreq_of_match, opp_info);
|
|
+ if (opp_info->data && opp_info->data->set_read_margin) {
|
|
+ opp_info->current_rm = UINT_MAX;
|
|
+ opp_info->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
|
+ if (IS_ERR(opp_info->grf))
|
|
+ opp_info->grf = NULL;
|
|
+ rockchip_get_volt_rm_table(dev, np, "rockchip,volt-mem-read-margin", &opp_info->volt_rm_tbl);
|
|
+
|
|
+ of_property_read_u32(np, "rockchip,reboot-freq", &opp_info->reboot_freq);
|
|
+ }
|
|
+
|
|
+ opp_table_token = dev_pm_opp_set_config_regulators(dev, rk_opp_config_regulators);
|
|
+ if (opp_table_token < 0) {
|
|
+ ret = opp_table_token;
|
|
+ dev_err(dev, "Failed to set opp config regulators\n");
|
|
+ goto reg_opp_table;
|
|
+ }
|
|
+
|
|
+ of_node_put(np);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+reg_opp_table:
|
|
+ if (reg_table_token >= 0)
|
|
+ dev_pm_opp_put_regulators(reg_table_token);
|
|
+np_err:
|
|
+ of_node_put(np);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct notifier_block rockchip_cpufreq_reboot_notifier = {
|
|
+ .notifier_call = rockchip_cpufreq_reboot,
|
|
+ .priority = 0,
|
|
+};
|
|
+
|
|
+static struct freq_attr *cpufreq_rockchip_attr[] = {
|
|
+ &cpufreq_freq_attr_scaling_available_freqs,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static int cpufreq_online(struct cpufreq_policy *policy)
|
|
+{
|
|
+ /* We did light-weight tear down earlier, nothing to do here */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int cpufreq_offline(struct cpufreq_policy *policy)
|
|
+{
|
|
+ /*
|
|
+ * Preserve policy->driver_data and don't free resources on light-weight
|
|
+ * tear down.
|
|
+ */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct private_data *rockchip_cpufreq_find_data(int cpu)
|
|
+{
|
|
+ struct private_data *priv;
|
|
+
|
|
+ list_for_each_entry(priv, &priv_list, node) {
|
|
+ if (cpumask_test_cpu(cpu, priv->cpus))
|
|
+ return priv;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int cpufreq_init(struct cpufreq_policy *policy)
|
|
+{
|
|
+ struct private_data *priv;
|
|
+ struct device *cpu_dev;
|
|
+ struct clk *cpu_clk;
|
|
+ unsigned int transition_latency;
|
|
+ int ret;
|
|
+
|
|
+ priv = rockchip_cpufreq_find_data(policy->cpu);
|
|
+ if (!priv) {
|
|
+ pr_err("failed to find data for cpu%d\n", policy->cpu);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ cpu_dev = priv->cpu_dev;
|
|
+
|
|
+ cpu_clk = clk_get(cpu_dev, NULL);
|
|
+ if (IS_ERR(cpu_clk)) {
|
|
+ ret = PTR_ERR(cpu_clk);
|
|
+ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
|
|
+ if (!transition_latency)
|
|
+ transition_latency = CPUFREQ_ETERNAL;
|
|
+
|
|
+ cpumask_copy(policy->cpus, priv->cpus);
|
|
+ policy->driver_data = priv;
|
|
+ policy->clk = cpu_clk;
|
|
+ policy->freq_table = priv->freq_table;
|
|
+ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
|
|
+ policy->cpuinfo.transition_latency = transition_latency;
|
|
+ policy->dvfs_possible_from_any_cpu = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int cpufreq_exit(struct cpufreq_policy *policy)
|
|
+{
|
|
+ clk_put(policy->clk);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int set_target(struct cpufreq_policy *policy, unsigned int index)
|
|
+{
|
|
+ struct private_data *priv = policy->driver_data;
|
|
+ unsigned long freq = policy->freq_table[index].frequency;
|
|
+
|
|
+ return dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
|
|
+}
|
|
+
|
|
+static struct cpufreq_driver rockchip_cpufreq_driver = {
|
|
+ .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
|
|
+ CPUFREQ_IS_COOLING_DEV |
|
|
+ CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
|
|
+ .verify = cpufreq_generic_frequency_table_verify,
|
|
+ .target_index = set_target,
|
|
+ .get = cpufreq_generic_get,
|
|
+ .init = cpufreq_init,
|
|
+ .exit = cpufreq_exit,
|
|
+ .online = cpufreq_online,
|
|
+ .offline = cpufreq_offline,
|
|
+ .register_em = cpufreq_register_em_with_opp,
|
|
+ .name = "rockchip-cpufreq",
|
|
+ .attr = cpufreq_rockchip_attr,
|
|
+ .suspend = cpufreq_generic_suspend,
|
|
+};
|
|
+
|
|
+static int rockchip_cpufreq_init(struct device *dev, int cpu)
|
|
+{
|
|
+ struct private_data *priv;
|
|
+ struct device *cpu_dev;
|
|
+ int ret;
|
|
+
|
|
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
+ if (!priv)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cpumask_set_cpu(cpu, priv->cpus);
|
|
+
|
|
+ cpu_dev = get_cpu_device(cpu);
|
|
+ if (!cpu_dev)
|
|
+ return -EPROBE_DEFER;
|
|
+ priv->cpu_dev = cpu_dev;
|
|
+
|
|
+ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = dev_pm_opp_of_cpumask_add_table(priv->cpus);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = dev_pm_opp_get_opp_count(cpu_dev);
|
|
+ if (ret <= 0)
|
|
+ return dev_err_probe(cpu_dev, -ENODEV, "OPP table can't be empty\n");
|
|
+
|
|
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
|
|
+ if (ret)
|
|
+ return dev_err_probe(cpu_dev, ret, "failed to init cpufreq table\n");
|
|
+
|
|
+ list_add(&priv->node, &priv_list);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rockchip_cpufreq_free_list(void *data)
|
|
+{
|
|
+ struct cluster_info *cluster, *pos;
|
|
+
|
|
+ list_for_each_entry_safe(cluster, pos, &cluster_info_list, list_head) {
|
|
+ list_del(&cluster->list_head);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int rockchip_cpufreq_init_list(struct device *dev)
|
|
+{
|
|
+ struct cluster_info *cluster;
|
|
+ int cpu, ret;
|
|
+
|
|
+ for_each_possible_cpu(cpu) {
|
|
+ cluster = rockchip_cluster_info_lookup(cpu);
|
|
+ if (cluster)
|
|
+ continue;
|
|
+
|
|
+ cluster = devm_kzalloc(dev, sizeof(*cluster), GFP_KERNEL);
|
|
+ if (!cluster) {
|
|
+ ret = -ENOMEM;
|
|
+ goto release_cluster_info;
|
|
+ }
|
|
+
|
|
+ ret = rockchip_cpufreq_cluster_init(cpu, cluster);
|
|
+ if (ret) {
|
|
+ dev_err_probe(dev, ret, "Failed to initialize dvfs info cpu%d\n", cpu);
|
|
+ goto release_cluster_info;
|
|
+ }
|
|
+ list_add(&cluster->list_head, &cluster_info_list);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+release_cluster_info:
|
|
+ rockchip_cpufreq_free_list(NULL);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void rockchip_cpufreq_unregister(void *data)
|
|
+{
|
|
+ cpufreq_unregister_driver(&rockchip_cpufreq_driver);
|
|
+}
|
|
+
|
|
+static int rockchip_cpufreq_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int ret, cpu;
|
|
+
|
|
+ ret = rockchip_cpufreq_init_list(&pdev->dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = devm_add_action_or_reset(&pdev->dev, rockchip_cpufreq_free_list, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = devm_register_reboot_notifier(&pdev->dev, &rockchip_cpufreq_reboot_notifier);
|
|
+ if (ret)
|
|
+ return dev_err_probe(&pdev->dev, ret, "Failed to register reboot handler\n");
|
|
+
|
|
+ for_each_possible_cpu(cpu) {
|
|
+ ret = rockchip_cpufreq_init(&pdev->dev, cpu);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = cpufreq_register_driver(&rockchip_cpufreq_driver);
|
|
+ if (ret)
|
|
+ return dev_err_probe(&pdev->dev, ret, "failed register driver\n");
|
|
+
|
|
+ ret = devm_add_action_or_reset(&pdev->dev, rockchip_cpufreq_unregister, NULL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver rockchip_cpufreq_platdrv = {
|
|
+ .driver = {
|
|
+ .name = "rockchip-cpufreq",
|
|
+ },
|
|
+ .probe = rockchip_cpufreq_probe,
|
|
+};
|
|
+
|
|
+static int __init rockchip_cpufreq_driver_init(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!of_machine_is_compatible("rockchip,rk3588") &&
|
|
+ !of_machine_is_compatible("rockchip,rk3588s")) {
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ ret = platform_driver_register(&rockchip_cpufreq_platdrv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ cpufreq_pdev = platform_device_register_data(NULL, "rockchip-cpufreq", -1,
|
|
+ NULL, 0);
|
|
+ if (IS_ERR(cpufreq_pdev)) {
|
|
+ pr_err("failed to register rockchip-cpufreq platform device\n");
|
|
+ ret = PTR_ERR(cpufreq_pdev);
|
|
+ goto unregister_platform_driver;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+unregister_platform_driver:
|
|
+ platform_driver_unregister(&rockchip_cpufreq_platdrv);
|
|
+ return ret;
|
|
+}
|
|
+module_init(rockchip_cpufreq_driver_init);
|
|
+
|
|
+static void __exit rockchip_cpufreq_driver_exit(void)
|
|
+{
|
|
+ platform_device_unregister(cpufreq_pdev);
|
|
+ platform_driver_unregister(&rockchip_cpufreq_platdrv);
|
|
+}
|
|
+module_exit(rockchip_cpufreq_driver_exit)
|
|
+
|
|
+MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
|
|
+MODULE_DESCRIPTION("Rockchip cpufreq driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile
|
|
index ce715562e..8354e4879 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 000000000..935b116c2
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
|
|
@@ -0,0 +1,2997 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+/*
|
|
+ * Copyright (C) Rockchip Electronics Co.Ltd
|
|
+ * Author:
|
|
+ * Algea Cao <algea.cao@rock-chips.com>
|
|
+ */
|
|
+#include <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/extcon-provider.h>
|
|
+#include <linux/extcon.h>
|
|
+#include <linux/hdmi.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/spinlock.h>
|
|
+
|
|
+#include <drm/drm_atomic.h>
|
|
+#include <drm/drm_atomic_helper.h>
|
|
+#include <drm/drm_crtc_helper.h>
|
|
+#include <drm/display/drm_dsc.h>
|
|
+#include <drm/display/drm_hdmi_helper.h>
|
|
+#include <drm/drm_edid.h>
|
|
+#include <drm/drm_encoder_slave.h>
|
|
+#include <drm/drm_of.h>
|
|
+#include <drm/drm_print.h>
|
|
+#include <drm/drm_probe_helper.h>
|
|
+#include <drm/display/drm_scdc_helper.h>
|
|
+#include <drm/bridge/dw_hdmi.h>
|
|
+
|
|
+#include <uapi/linux/media-bus-format.h>
|
|
+#include <uapi/linux/videodev2.h>
|
|
+
|
|
+#include "dw-hdmi-qp.h"
|
|
+// #include "dw-hdmi-qp-audio.h"
|
|
+// #include "dw-hdmi-qp-cec.h"
|
|
+
|
|
+#include <media/cec-notifier.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
|
|
+#define HDMI20_MAX_TMDSCLK_KHZ 600000
|
|
+
|
|
+static const unsigned int dw_hdmi_cable[] = {
|
|
+ EXTCON_DISP_HDMI,
|
|
+ EXTCON_NONE,
|
|
+};
|
|
+
|
|
+static const struct drm_display_mode dw_hdmi_default_modes[] = {
|
|
+ /* 16 - 1920x1080@60Hz 16:9 */
|
|
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
|
|
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
|
+ /* 2 - 720x480@60Hz 4:3 */
|
|
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
|
|
+ 798, 858, 0, 480, 489, 495, 525, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
|
|
+ /* 4 - 1280x720@60Hz 16:9 */
|
|
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
|
|
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
|
+ /* 31 - 1920x1080@50Hz 16:9 */
|
|
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
|
|
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
|
+ /* 19 - 1280x720@50Hz 16:9 */
|
|
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
|
|
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
|
|
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
|
|
+ /* 17 - 720x576@50Hz 4:3 */
|
|
+ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
|
|
+ 796, 864, 0, 576, 581, 586, 625, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
|
|
+ /* 2 - 720x480@60Hz 4:3 */
|
|
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
|
|
+ 798, 858, 0, 480, 489, 495, 525, 0,
|
|
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
|
|
+ .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
|
|
+};
|
|
+
|
|
+enum frl_mask {
|
|
+ FRL_3GBPS_3LANE = 1,
|
|
+ FRL_6GBPS_3LANE,
|
|
+ FRL_6GBPS_4LANE,
|
|
+ FRL_8GBPS_4LANE,
|
|
+ FRL_10GBPS_4LANE,
|
|
+ FRL_12GBPS_4LANE,
|
|
+};
|
|
+
|
|
+struct hdmi_vmode_qp {
|
|
+ bool mdataenablepolarity;
|
|
+
|
|
+ unsigned int previous_pixelclock;
|
|
+ unsigned long mpixelclock;
|
|
+ unsigned int mpixelrepetitioninput;
|
|
+ unsigned int mpixelrepetitionoutput;
|
|
+ unsigned long previous_tmdsclock;
|
|
+ unsigned int mtmdsclock;
|
|
+};
|
|
+
|
|
+struct hdmi_qp_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 quant_range;
|
|
+ unsigned int pix_repet_factor;
|
|
+ struct hdmi_vmode_qp video_mode;
|
|
+ bool update;
|
|
+};
|
|
+
|
|
+struct dw_hdmi_qp_i2c {
|
|
+ struct i2c_adapter adap;
|
|
+
|
|
+ struct mutex lock; /* used to serialize data transfers */
|
|
+ struct completion cmp;
|
|
+ u32 stat;
|
|
+
|
|
+ u8 slave_reg;
|
|
+ bool is_regaddr;
|
|
+ bool is_segment;
|
|
+
|
|
+ unsigned int scl_high_ns;
|
|
+ unsigned int scl_low_ns;
|
|
+};
|
|
+
|
|
+// 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_qp *hdmi,
|
|
+// const struct dw_hdmi_plat_data *pdata,
|
|
+// unsigned long mpixelclock);
|
|
+// };
|
|
+
|
|
+struct dw_hdmi_qp {
|
|
+ struct drm_connector connector;
|
|
+ struct drm_bridge bridge;
|
|
+ struct platform_device *hdcp_dev;
|
|
+ struct platform_device *audio;
|
|
+ struct platform_device *cec;
|
|
+ struct device *dev;
|
|
+ struct dw_hdmi_qp_i2c *i2c;
|
|
+
|
|
+ struct hdmi_qp_data_info hdmi_data;
|
|
+ const struct dw_hdmi_plat_data *plat_data;
|
|
+
|
|
+ int vic;
|
|
+ int main_irq;
|
|
+ int avp_irq;
|
|
+ int earc_irq;
|
|
+
|
|
+ u8 edid[HDMI_EDID_LEN];
|
|
+
|
|
+ struct {
|
|
+ const struct dw_hdmi_qp_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;
|
|
+ bool dclk_en;
|
|
+
|
|
+ struct mutex mutex; /* for state below and previous_mode */
|
|
+ //[CC:] curr_conn should be removed
|
|
+ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */
|
|
+ enum drm_connector_force force; /* mutex-protected force state */
|
|
+ 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 */
|
|
+
|
|
+ u32 scdc_intr;
|
|
+ u32 flt_intr;
|
|
+ u32 earc_intr;
|
|
+
|
|
+ struct mutex audio_mutex;
|
|
+ unsigned int sample_rate;
|
|
+ unsigned int audio_cts;
|
|
+ unsigned int audio_n;
|
|
+ bool audio_enable;
|
|
+ void (*enable_audio)(struct dw_hdmi_qp *hdmi);
|
|
+ void (*disable_audio)(struct dw_hdmi_qp *hdmi);
|
|
+
|
|
+ struct dentry *debugfs_dir;
|
|
+ bool scramble_low_rates;
|
|
+
|
|
+ struct extcon_dev *extcon;
|
|
+
|
|
+ struct regmap *regm;
|
|
+
|
|
+ bool initialized; /* hdmi is enabled before bind */
|
|
+ struct completion flt_cmp;
|
|
+ struct completion earc_cmp;
|
|
+
|
|
+ struct cec_notifier *cec_notifier;
|
|
+ struct cec_adapter *cec_adap;
|
|
+ struct mutex cec_notifier_mutex;
|
|
+
|
|
+ hdmi_codec_plugged_cb plugged_cb;
|
|
+ struct device *codec_dev;
|
|
+ enum drm_connector_status last_connector_result;
|
|
+};
|
|
+
|
|
+static inline void hdmi_writel(struct dw_hdmi_qp *hdmi, u32 val, int offset)
|
|
+{
|
|
+ regmap_write(hdmi->regm, offset, val);
|
|
+}
|
|
+
|
|
+static inline u32 hdmi_readl(struct dw_hdmi_qp *hdmi, int offset)
|
|
+{
|
|
+ unsigned int val = 0;
|
|
+
|
|
+ regmap_read(hdmi->regm, offset, &val);
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void handle_plugged_change(struct dw_hdmi_qp *hdmi, bool plugged)
|
|
+{
|
|
+ if (hdmi->plugged_cb && hdmi->codec_dev)
|
|
+ hdmi->plugged_cb(hdmi->codec_dev, plugged);
|
|
+}
|
|
+
|
|
+int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn,
|
|
+ struct device *codec_dev)
|
|
+{
|
|
+ bool plugged;
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+ hdmi->plugged_cb = fn;
|
|
+ hdmi->codec_dev = codec_dev;
|
|
+ plugged = hdmi->last_connector_result == connector_status_connected;
|
|
+ handle_plugged_change(hdmi, plugged);
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_plugged_cb);
|
|
+
|
|
+static void hdmi_modb(struct dw_hdmi_qp *hdmi, u32 data, u32 mask, u32 reg)
|
|
+{
|
|
+ regmap_update_bits(hdmi->regm, reg, mask, data);
|
|
+}
|
|
+
|
|
+static void hdmi_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts,
|
|
+ unsigned int n)
|
|
+{
|
|
+ /* Set N */
|
|
+ hdmi_modb(hdmi, n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0);
|
|
+
|
|
+ /* Set CTS */
|
|
+ if (cts)
|
|
+ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK,
|
|
+ AUDPKT_ACR_CONTROL1);
|
|
+ else
|
|
+ hdmi_modb(hdmi, 0, AUDPKT_ACR_CTS_OVR_EN_MSK,
|
|
+ AUDPKT_ACR_CONTROL1);
|
|
+
|
|
+ hdmi_modb(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK,
|
|
+ AUDPKT_ACR_CONTROL1);
|
|
+}
|
|
+
|
|
+// static int hdmi_match_tmds_n_table(struct dw_hdmi_qp *hdmi,
|
|
+// unsigned long pixel_clk,
|
|
+// unsigned long freq)
|
|
+// {
|
|
+// const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
|
|
+// const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
|
|
+// int i;
|
|
+//
|
|
+// if (plat_data->tmds_n_table) {
|
|
+// for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) {
|
|
+// if (pixel_clk == plat_data->tmds_n_table[i].tmds) {
|
|
+// tmds_n = &plat_data->tmds_n_table[i];
|
|
+// break;
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+//
|
|
+// if (tmds_n == NULL) {
|
|
+// for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
|
|
+// if (pixel_clk == common_tmds_n_table[i].tmds) {
|
|
+// tmds_n = &common_tmds_n_table[i];
|
|
+// break;
|
|
+// }
|
|
+// }
|
|
+// }
|
|
+//
|
|
+// if (tmds_n == NULL)
|
|
+// return -ENOENT;
|
|
+//
|
|
+// switch (freq) {
|
|
+// case 32000:
|
|
+// return tmds_n->n_32k;
|
|
+// case 44100:
|
|
+// case 88200:
|
|
+// case 176400:
|
|
+// return (freq / 44100) * tmds_n->n_44k1;
|
|
+// case 48000:
|
|
+// case 96000:
|
|
+// case 192000:
|
|
+// return (freq / 48000) * tmds_n->n_48k;
|
|
+// default:
|
|
+// return -ENOENT;
|
|
+// }
|
|
+// }
|
|
+//
|
|
+static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n,
|
|
+ unsigned int pixel_clk)
|
|
+{
|
|
+ u64 final, diff;
|
|
+ u64 cts;
|
|
+
|
|
+ final = (u64)pixel_clk * n;
|
|
+
|
|
+ cts = final;
|
|
+ do_div(cts, 128 * freq);
|
|
+
|
|
+ diff = final - (u64)cts * (128 * freq);
|
|
+
|
|
+ return diff;
|
|
+}
|
|
+
|
|
+static unsigned int hdmi_compute_n(struct dw_hdmi_qp *hdmi,
|
|
+ unsigned long pixel_clk,
|
|
+ unsigned long freq)
|
|
+{
|
|
+ unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
|
|
+ unsigned int max_n = (128 * freq) / 300;
|
|
+ unsigned int ideal_n = (128 * freq) / 1000;
|
|
+ unsigned int best_n_distance = ideal_n;
|
|
+ unsigned int best_n = 0;
|
|
+ u64 best_diff = U64_MAX;
|
|
+ int n;
|
|
+
|
|
+ /* If the ideal N could satisfy the audio math, then just take it */
|
|
+ if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
|
|
+ return ideal_n;
|
|
+
|
|
+ for (n = min_n; n <= max_n; n++) {
|
|
+ u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk);
|
|
+
|
|
+ if (diff < best_diff || (diff == best_diff &&
|
|
+ abs(n - ideal_n) < best_n_distance)) {
|
|
+ best_n = n;
|
|
+ best_diff = diff;
|
|
+ best_n_distance = abs(best_n - ideal_n);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The best N already satisfy the audio math, and also be
|
|
+ * the closest value to ideal N, so just cut the loop.
|
|
+ */
|
|
+ if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return best_n;
|
|
+}
|
|
+
|
|
+static unsigned int hdmi_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk,
|
|
+ unsigned long sample_rate)
|
|
+{
|
|
+ int n;
|
|
+
|
|
+ // n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
|
|
+ n = 0;
|
|
+ if (n > 0)
|
|
+ return n;
|
|
+
|
|
+ dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n",
|
|
+ pixel_clk);
|
|
+
|
|
+ return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * When transmitting IEC60958 linear PCM audio, these registers allow to
|
|
+ * configure the channel status information of all the channel status
|
|
+ * bits in the IEC60958 frame. For the moment this configuration is only
|
|
+ * used when the I2S audio interface, General Purpose Audio (GPA),
|
|
+ * or AHB audio DMA (AHBAUDDMA) interface is active
|
|
+ * (for S/PDIF interface this information comes from the stream).
|
|
+ */
|
|
+void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi,
|
|
+ u8 *channel_status, bool ref2stream)
|
|
+{
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (!hdmi->dclk_en) {
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Set channel status */
|
|
+ hdmi_writel(hdmi, channel_status[3] | (channel_status[4] << 8),
|
|
+ AUDPKT_CHSTATUS_OVR1);
|
|
+
|
|
+ if (ref2stream)
|
|
+ hdmi_modb(hdmi, 0,
|
|
+ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK,
|
|
+ AUDPKT_CONTROL0);
|
|
+ else
|
|
+ hdmi_modb(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN,
|
|
+ AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK,
|
|
+ AUDPKT_CONTROL0);
|
|
+
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_status);
|
|
+
|
|
+static void hdmi_set_clk_regenerator(struct dw_hdmi_qp *hdmi,
|
|
+ unsigned long pixel_clk, unsigned int sample_rate)
|
|
+{
|
|
+ unsigned int n = 0, cts = 0;
|
|
+
|
|
+ n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
|
|
+
|
|
+ hdmi->audio_n = n;
|
|
+ hdmi->audio_cts = cts;
|
|
+ hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
|
|
+}
|
|
+
|
|
+static void hdmi_init_clk_regenerator(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->dclk_en)
|
|
+ hdmi_set_clk_regenerator(hdmi, 74250000, hdmi->sample_rate);
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+
|
|
+static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->dclk_en)
|
|
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
|
|
+ hdmi->sample_rate);
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+
|
|
+void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate)
|
|
+{
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->dclk_en) {
|
|
+ hdmi->sample_rate = rate;
|
|
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
|
|
+ hdmi->sample_rate);
|
|
+ }
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_sample_rate);
|
|
+
|
|
+void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt)
|
|
+{
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_count);
|
|
+
|
|
+void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca)
|
|
+{
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_channel_allocation);
|
|
+
|
|
+void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi,
|
|
+ struct hdmi_codec_params *hparms)
|
|
+{
|
|
+ u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)];
|
|
+ int ret = 0;
|
|
+
|
|
+ ret = hdmi_audio_infoframe_pack(&hparms->cea, infoframe_buf,
|
|
+ sizeof(infoframe_buf));
|
|
+ if (!ret) {
|
|
+ dev_err(hdmi->dev, "%s: Failed to pack audio infoframe: %d\n",
|
|
+ __func__, ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (!hdmi->dclk_en) {
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV }
|
|
+ * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 }
|
|
+ * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 }
|
|
+ *
|
|
+ * PB0: CheckSum
|
|
+ * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 |
|
|
+ * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 |
|
|
+ * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 |
|
|
+ * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 |
|
|
+ * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 |
|
|
+ * PB6~PB10: Reserved
|
|
+ *
|
|
+ * AUDI_CONTENTS0 default value defined by HDMI specification,
|
|
+ * and shall only be changed for debug purposes.
|
|
+ * So, we only configure payload byte from PB0~PB7(2 word total).
|
|
+ */
|
|
+ regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &infoframe_buf[3], 2);
|
|
+
|
|
+ /* Enable ACR, AUDI, AMD */
|
|
+ hdmi_modb(hdmi,
|
|
+ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN,
|
|
+ PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN,
|
|
+ PKTSCHED_PKT_EN);
|
|
+
|
|
+ /* Enable AUDS */
|
|
+ hdmi_modb(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN);
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_audio_infoframe);
|
|
+
|
|
+static void hdmi_enable_audio_clk(struct dw_hdmi_qp *hdmi, bool enable)
|
|
+{
|
|
+ if (enable)
|
|
+ hdmi_modb(hdmi, 0,
|
|
+ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE);
|
|
+ else
|
|
+ hdmi_modb(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE,
|
|
+ AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE);
|
|
+}
|
|
+
|
|
+// static void dw_hdmi_i2s_audio_enable(struct dw_hdmi_qp *hdmi)
|
|
+// {
|
|
+// hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
|
|
+// hdmi_enable_audio_clk(hdmi, true);
|
|
+// }
|
|
+//
|
|
+// static void dw_hdmi_i2s_audio_disable(struct dw_hdmi_qp *hdmi)
|
|
+// {
|
|
+// /* Disable AUDS, ACR, AUDI, AMD */
|
|
+// hdmi_modb(hdmi, 0,
|
|
+// PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN |
|
|
+// PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN,
|
|
+// PKTSCHED_PKT_EN);
|
|
+//
|
|
+// hdmi_enable_audio_clk(hdmi, false);
|
|
+// }
|
|
+
|
|
+void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->dclk_en) {
|
|
+ hdmi->audio_enable = true;
|
|
+ if (hdmi->enable_audio)
|
|
+ hdmi->enable_audio(hdmi);
|
|
+ }
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_enable);
|
|
+
|
|
+void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->dclk_en) {
|
|
+ hdmi->audio_enable = false;
|
|
+ if (hdmi->disable_audio)
|
|
+ hdmi->disable_audio(hdmi);
|
|
+ }
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_audio_disable);
|
|
+
|
|
+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_RGB888_1X24:
|
|
+ case MEDIA_BUS_FMT_RGB101010_1X30:
|
|
+ case MEDIA_BUS_FMT_RGB121212_1X36:
|
|
+ case MEDIA_BUS_FMT_RGB161616_1X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_YUV8_1X24:
|
|
+ case MEDIA_BUS_FMT_YUV10_1X30:
|
|
+ case MEDIA_BUS_FMT_YUV12_1X36:
|
|
+ case MEDIA_BUS_FMT_YUV16_1X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_UYVY8_1X16:
|
|
+ case MEDIA_BUS_FMT_UYVY10_1X20:
|
|
+ case MEDIA_BUS_FMT_UYVY12_1X24:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
|
|
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_RGB888_1X24:
|
|
+ case MEDIA_BUS_FMT_YUV8_1X24:
|
|
+ case MEDIA_BUS_FMT_UYVY8_1X16:
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ return 8;
|
|
+
|
|
+ case MEDIA_BUS_FMT_RGB101010_1X30:
|
|
+ case MEDIA_BUS_FMT_YUV10_1X30:
|
|
+ case MEDIA_BUS_FMT_UYVY10_1X20:
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ return 10;
|
|
+
|
|
+ case MEDIA_BUS_FMT_RGB121212_1X36:
|
|
+ case MEDIA_BUS_FMT_YUV12_1X36:
|
|
+ case MEDIA_BUS_FMT_UYVY12_1X24:
|
|
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
|
|
+ return 12;
|
|
+
|
|
+ case MEDIA_BUS_FMT_RGB161616_1X48:
|
|
+ case MEDIA_BUS_FMT_YUV16_1X48:
|
|
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
|
|
+ return 16;
|
|
+
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ /* Software reset */
|
|
+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
|
|
+
|
|
+ hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
|
|
+
|
|
+ hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
|
|
+
|
|
+ /* Clear DONE and ERROR interrupts */
|
|
+ hdmi_writel(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR,
|
|
+ MAINUNIT_1_INT_CLEAR);
|
|
+}
|
|
+
|
|
+static int dw_hdmi_i2c_read(struct dw_hdmi_qp *hdmi,
|
|
+ unsigned char *buf, unsigned int length)
|
|
+{
|
|
+ struct dw_hdmi_qp_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);
|
|
+
|
|
+ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
|
|
+ I2CM_INTERFACE_CONTROL0);
|
|
+
|
|
+ hdmi_modb(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 time out!\n");
|
|
+ hdmi_writel(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 err!\n");
|
|
+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ *buf++ = hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff;
|
|
+ // dev_dbg(hdmi->dev, "i2c read done! i2c->stat:%02x 0x%02x\n",
|
|
+ // i2c->stat, hdmi_readl(hdmi, I2CM_INTERFACE_RDDATA_0_3));
|
|
+ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
|
|
+ }
|
|
+ i2c->is_segment = false;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_i2c_write(struct dw_hdmi_qp *hdmi,
|
|
+ unsigned char *buf, unsigned int length)
|
|
+{
|
|
+ struct dw_hdmi_qp_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);
|
|
+
|
|
+ hdmi_writel(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3);
|
|
+ hdmi_modb(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR,
|
|
+ I2CM_INTERFACE_CONTROL0);
|
|
+ hdmi_modb(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");
|
|
+ hdmi_writel(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");
|
|
+ hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
|
|
+ return -EIO;
|
|
+ }
|
|
+ hdmi_modb(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0);
|
|
+ }
|
|
+ // dev_dbg(hdmi->dev, "i2c write done!\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
|
|
+ struct i2c_msg *msgs, int num)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap);
|
|
+ struct dw_hdmi_qp_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;
|
|
+
|
|
+ // dev_dbg(hdmi->dev, "i2c xfer: num: %d, addr: %#x\n", num, addr);
|
|
+
|
|
+ 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 */
|
|
+ hdmi_modb(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;
|
|
+
|
|
+ hdmi_modb(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++) {
|
|
+ // dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
|
|
+ // i + 1, num, msgs[i].len, msgs[i].flags);
|
|
+
|
|
+ if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) {
|
|
+ i2c->is_segment = true;
|
|
+ hdmi_modb(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR,
|
|
+ I2CM_INTERFACE_CONTROL1);
|
|
+ hdmi_modb(hdmi, *msgs[i].buf, I2CM_SEG_PTR,
|
|
+ I2CM_INTERFACE_CONTROL1);
|
|
+ } else {
|
|
+ if (msgs[i].flags & I2C_M_RD)
|
|
+ ret = dw_hdmi_i2c_read(hdmi, msgs[i].buf,
|
|
+ msgs[i].len);
|
|
+ else
|
|
+ ret = dw_hdmi_i2c_write(hdmi, msgs[i].buf,
|
|
+ msgs[i].len);
|
|
+ }
|
|
+ if (ret < 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!ret)
|
|
+ ret = num;
|
|
+
|
|
+ /* Mute DONE and ERROR interrupts */
|
|
+ hdmi_modb(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_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_i2c_xfer,
|
|
+ .functionality = dw_hdmi_i2c_func,
|
|
+};
|
|
+
|
|
+static struct i2c_adapter *dw_hdmi_i2c_adapter(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ struct i2c_adapter *adap;
|
|
+ struct dw_hdmi_qp_i2c *i2c;
|
|
+ int ret;
|
|
+
|
|
+ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
|
|
+ if (!i2c)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ mutex_init(&i2c->lock);
|
|
+ init_completion(&i2c->cmp);
|
|
+
|
|
+ adap = &i2c->adap;
|
|
+ adap->class = I2C_CLASS_DDC;
|
|
+ adap->owner = THIS_MODULE;
|
|
+ adap->dev.parent = hdmi->dev;
|
|
+ adap->algo = &dw_hdmi_algorithm;
|
|
+ strscpy(adap->name, "ddc", sizeof(adap->name));
|
|
+ i2c_set_adapdata(adap, hdmi);
|
|
+
|
|
+ ret = i2c_add_adapter(adap);
|
|
+ if (ret) {
|
|
+ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
|
|
+ devm_kfree(hdmi->dev, i2c);
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
+ hdmi->i2c = i2c;
|
|
+
|
|
+ dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
|
|
+
|
|
+ return adap;
|
|
+}
|
|
+
|
|
+#define HDMI_PHY_EARC_MASK BIT(29)
|
|
+
|
|
+int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ u32 stat, ret;
|
|
+
|
|
+ /* set hdmi phy earc mode */
|
|
+ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_PHY_EARC_MASK,
|
|
+ true);
|
|
+
|
|
+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
|
|
+ &hdmi->previous_mode);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ reinit_completion(&hdmi->earc_cmp);
|
|
+
|
|
+ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ |
|
|
+ EARCRX_CMDC_DISCOVERY_DONE_IRQ,
|
|
+ EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ |
|
|
+ EARCRX_CMDC_DISCOVERY_DONE_IRQ, EARCRX_0_INT_MASK_N);
|
|
+
|
|
+ /* start discovery */
|
|
+ hdmi_modb(hdmi, EARCRX_CMDC_DISCOVERY_EN, EARCRX_CMDC_DISCOVERY_EN,
|
|
+ EARCRX_CMDC_CONTROL);
|
|
+
|
|
+ /*
|
|
+ * The eARC TX device drives a logic-high-voltage-level
|
|
+ * pulse on the physical HPD connector pin, after
|
|
+ * at least 100 ms of low voltage level to start the
|
|
+ * eARC Discovery process.
|
|
+ */
|
|
+ hdmi_modb(hdmi, EARCRX_CONNECTOR_HPD, EARCRX_CONNECTOR_HPD,
|
|
+ EARCRX_CMDC_CONTROL);
|
|
+
|
|
+ stat = wait_for_completion_timeout(&hdmi->earc_cmp, HZ / 10);
|
|
+ if (!stat)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ) {
|
|
+ dev_err(hdmi->dev, "discovery timeout\n");
|
|
+ return -ETIMEDOUT;
|
|
+ } else if (hdmi->earc_intr & EARCRX_CMDC_DISCOVERY_DONE_IRQ) {
|
|
+ dev_info(hdmi->dev, "discovery done\n");
|
|
+ } else {
|
|
+ dev_err(hdmi->dev, "discovery failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ hdmi_writel(hdmi, 1, EARCRX_DMAC_PHY_CONTROL);
|
|
+ hdmi_modb(hdmi, EARCRX_CMDC_SWINIT_P, EARCRX_CMDC_SWINIT_P,
|
|
+ EARCRX_CMDC_CONFIG0);
|
|
+
|
|
+ hdmi_writel(hdmi, 0xf3, EARCRX_DMAC_CONFIG);
|
|
+ hdmi_writel(hdmi, 0x63, EARCRX_DMAC_CONTROL0);
|
|
+ hdmi_writel(hdmi, 0xff, EARCRX_DMAC_CONTROL1);
|
|
+
|
|
+ hdmi_modb(hdmi, EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG |
|
|
+ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN,
|
|
+ EARCRX_XACTREAD_STOP_CFG | EARCRX_XACTREAD_RETRY_CFG |
|
|
+ EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 | EARCRX_CMDC_XACT_RESTART_EN,
|
|
+ EARCRX_CMDC_CONFIG0);
|
|
+
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER0);
|
|
+ hdmi_writel(hdmi, 0x1b0e, EARCRX_DMAC_CHSTATUS_STREAMER1);
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER2);
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER3);
|
|
+ hdmi_writel(hdmi, 0xf2000000, EARCRX_DMAC_CHSTATUS_STREAMER4);
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER5);
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER6);
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER7);
|
|
+ hdmi_writel(hdmi, 0, EARCRX_DMAC_CHSTATUS_STREAMER8);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_earc);
|
|
+
|
|
+/* -----------------------------------------------------------------------------
|
|
+ * 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_qp *hdmi,
|
|
+ const struct drm_connector *connector,
|
|
+ const struct drm_display_mode *mode)
|
|
+{
|
|
+ struct hdmi_avi_infoframe frame;
|
|
+ u32 val, i, j;
|
|
+ u8 buff[17];
|
|
+ enum hdmi_quantization_range rgb_quant_range =
|
|
+ hdmi->hdmi_data.quant_range;
|
|
+
|
|
+ /* Initialise info frame from DRM mode */
|
|
+ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
|
|
+
|
|
+ /*
|
|
+ * Ignore monitor selectable quantization, use quantization set
|
|
+ * by the user
|
|
+ */
|
|
+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, rgb_quant_range);
|
|
+ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ frame.colorspace = HDMI_COLORSPACE_YUV444;
|
|
+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ frame.colorspace = HDMI_COLORSPACE_YUV422;
|
|
+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ frame.colorspace = HDMI_COLORSPACE_YUV420;
|
|
+ else
|
|
+ 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;
|
|
+ else
|
|
+ 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;
|
|
+ else
|
|
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
|
|
+ frame.extended_colorimetry =
|
|
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
|
|
+ break;
|
|
+ case V4L2_YCBCR_ENC_BT2020:
|
|
+ if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020)
|
|
+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
|
|
+ else
|
|
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
|
|
+ frame.extended_colorimetry =
|
|
+ HDMI_EXTENDED_COLORIMETRY_BT2020;
|
|
+ break;
|
|
+ default: /* Carries no data */
|
|
+ frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
|
|
+ frame.extended_colorimetry =
|
|
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
|
|
+ frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
|
|
+ frame.extended_colorimetry =
|
|
+ HDMI_EXTENDED_COLORIMETRY_BT2020;
|
|
+ } else {
|
|
+ frame.colorimetry = HDMI_COLORIMETRY_NONE;
|
|
+ frame.extended_colorimetry =
|
|
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ frame.scan_mode = HDMI_SCAN_MODE_NONE;
|
|
+ frame.video_code = hdmi->vic;
|
|
+
|
|
+ hdmi_avi_infoframe_pack_only(&frame, buff, 17);
|
|
+
|
|
+ /* mode which vic >= 128 must use avi version 3 */
|
|
+ if (hdmi->vic >= 128) {
|
|
+ frame.version = 3;
|
|
+ buff[1] = frame.version;
|
|
+ buff[4] &= 0x1f;
|
|
+ buff[4] |= ((frame.colorspace & 0x7) << 5);
|
|
+ buff[7] = frame.video_code;
|
|
+ hdmi_infoframe_set_checksum(buff, 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);
|
|
+ hdmi_writel(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 = buff[i * 4 + j + 3];
|
|
+ val |= buff[i * 4 + j + 3] << (8 * j);
|
|
+ }
|
|
+
|
|
+ hdmi_writel(hdmi, val, PKT_AVI_CONTENTS1 + i * 4);
|
|
+ }
|
|
+
|
|
+ hdmi_modb(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
|
|
+
|
|
+ hdmi_modb(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
|
|
+ PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
|
|
+ PKTSCHED_PKT_EN);
|
|
+}
|
|
+
|
|
+static void hdmi_config_CVTEM(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ u8 ds_type = 0;
|
|
+ u8 sync = 1;
|
|
+ u8 vfr = 1;
|
|
+ u8 afr = 0;
|
|
+ u8 new = 1;
|
|
+ u8 end = 0;
|
|
+ u8 data_set_length = 136;
|
|
+ u8 hb1[6] = { 0x80, 0, 0, 0, 0, 0x40 };
|
|
+ u8 *pps_body;
|
|
+ u32 val, i, reg;
|
|
+ struct drm_display_mode *mode = &hdmi->previous_mode;
|
|
+ int hsync, hfront, hback;
|
|
+ struct dw_hdmi_link_config *link_cfg;
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+
|
|
+ hdmi_modb(hdmi, 0, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_PKT_EN);
|
|
+
|
|
+ if (hdmi->plat_data->get_link_cfg) {
|
|
+ link_cfg = hdmi->plat_data->get_link_cfg(data);
|
|
+ } else {
|
|
+ dev_err(hdmi->dev, "can't get frl link cfg\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!link_cfg->dsc_mode) {
|
|
+ dev_info(hdmi->dev, "don't use dsc mode\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pps_body = link_cfg->pps_payload;
|
|
+
|
|
+ hsync = mode->hsync_end - mode->hsync_start;
|
|
+ hback = mode->htotal - mode->hsync_end;
|
|
+ hfront = mode->hsync_start - mode->hdisplay;
|
|
+
|
|
+ for (i = 0; i < 6; i++) {
|
|
+ val = i << 16 | hb1[i] << 8;
|
|
+ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS0 + i * 0x20);
|
|
+ }
|
|
+
|
|
+ val = new << 7 | end << 6 | ds_type << 4 | afr << 3 |
|
|
+ vfr << 2 | sync << 1;
|
|
+ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS1);
|
|
+
|
|
+ val = data_set_length << 16 | pps_body[0] << 24;
|
|
+ hdmi_writel(hdmi, val, PKT0_EMP_CVTEM_CONTENTS2);
|
|
+
|
|
+ reg = PKT0_EMP_CVTEM_CONTENTS3;
|
|
+ for (i = 1; i < 125; i++) {
|
|
+ if (reg == PKT1_EMP_CVTEM_CONTENTS0 ||
|
|
+ reg == PKT2_EMP_CVTEM_CONTENTS0 ||
|
|
+ reg == PKT3_EMP_CVTEM_CONTENTS0 ||
|
|
+ reg == PKT4_EMP_CVTEM_CONTENTS0 ||
|
|
+ reg == PKT5_EMP_CVTEM_CONTENTS0) {
|
|
+ reg += 4;
|
|
+ i--;
|
|
+ continue;
|
|
+ }
|
|
+ if (i % 4 == 1)
|
|
+ val = pps_body[i];
|
|
+ if (i % 4 == 2)
|
|
+ val |= pps_body[i] << 8;
|
|
+ if (i % 4 == 3)
|
|
+ val |= pps_body[i] << 16;
|
|
+ if (!(i % 4)) {
|
|
+ val |= pps_body[i] << 24;
|
|
+ hdmi_writel(hdmi, val, reg);
|
|
+ reg += 4;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ val = (hfront & 0xff) << 24 | pps_body[127] << 16 |
|
|
+ pps_body[126] << 8 | pps_body[125];
|
|
+ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS6);
|
|
+
|
|
+ val = (hback & 0xff) << 24 | ((hsync >> 8) & 0xff) << 16 |
|
|
+ (hsync & 0xff) << 8 | ((hfront >> 8) & 0xff);
|
|
+ hdmi_writel(hdmi, val, PKT4_EMP_CVTEM_CONTENTS7);
|
|
+
|
|
+ val = link_cfg->hcactive << 8 | ((hback >> 8) & 0xff);
|
|
+ hdmi_writel(hdmi, val, PKT5_EMP_CVTEM_CONTENTS1);
|
|
+
|
|
+ for (i = PKT5_EMP_CVTEM_CONTENTS2; i <= PKT5_EMP_CVTEM_CONTENTS7; i += 4)
|
|
+ hdmi_writel(hdmi, 0, i);
|
|
+
|
|
+ hdmi_modb(hdmi, PKTSCHED_EMP_CVTEM_TX_EN, PKTSCHED_EMP_CVTEM_TX_EN,
|
|
+ PKTSCHED_PKT_EN);
|
|
+}
|
|
+
|
|
+static void hdmi_config_drm_infoframe(struct dw_hdmi_qp *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;
|
|
+
|
|
+ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
|
|
+
|
|
+ if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) {
|
|
+ DRM_DEBUG("No need to set HDR metadata in infoframe\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!conn_state->hdr_output_metadata) {
|
|
+ DRM_DEBUG("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))) {
|
|
+ DRM_ERROR("Not support EOTF %d\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);
|
|
+ hdmi_writel(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)))
|
|
+ hdmi_writel(hdmi, val, PKT_DRMI_CONTENTS1 + ((i / 4) * 4));
|
|
+ }
|
|
+
|
|
+ hdmi_modb(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1);
|
|
+
|
|
+ hdmi_modb(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
|
|
+
|
|
+ DRM_DEBUG("%s eotf %d end\n", __func__,
|
|
+ hdr_metadata->hdmi_metadata_type1.eotf);
|
|
+}
|
|
+
|
|
+/* Filter out invalid setups to avoid configuring SCDC and scrambling */
|
|
+static bool dw_hdmi_support_scdc(struct dw_hdmi_qp *hdmi,
|
|
+ const struct drm_display_info *display)
|
|
+{
|
|
+ /* Disable if no DDC bus */
|
|
+ if (!hdmi->ddc)
|
|
+ return false;
|
|
+
|
|
+ /* Disable if SCDC is not supported, or if an HF-VSDB block is absent */
|
|
+ if (!display->hdmi.scdc.supported ||
|
|
+ !display->hdmi.scdc.scrambling.supported)
|
|
+ return false;
|
|
+
|
|
+ /*
|
|
+ * Disable if display only support low TMDS rates and scrambling
|
|
+ * for low rates is not supported either
|
|
+ */
|
|
+ if (!display->hdmi.scdc.scrambling.low_rates &&
|
|
+ display->max_tmds_clock <= 340000)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static int hdmi_set_frl_mask(int frl_rate)
|
|
+{
|
|
+ switch (frl_rate) {
|
|
+ case 48:
|
|
+ return FRL_12GBPS_4LANE;
|
|
+ case 40:
|
|
+ return FRL_10GBPS_4LANE;
|
|
+ case 32:
|
|
+ return FRL_8GBPS_4LANE;
|
|
+ case 24:
|
|
+ return FRL_6GBPS_4LANE;
|
|
+ case 18:
|
|
+ return FRL_6GBPS_3LANE;
|
|
+ case 9:
|
|
+ return FRL_3GBPS_3LANE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hdmi_start_flt(struct dw_hdmi_qp *hdmi, u8 rate)
|
|
+{
|
|
+ u8 val;
|
|
+ u8 ffe_lv = 0;
|
|
+ int i = 0, stat;
|
|
+
|
|
+ /* FLT_READY & FFE_LEVELS read */
|
|
+ for (i = 0; i < 20; i++) {
|
|
+ drm_scdc_readb(hdmi->ddc, SCDC_STATUS_FLAGS_0, &val);
|
|
+ if (val & BIT(6))
|
|
+ break;
|
|
+ msleep(20);
|
|
+ }
|
|
+
|
|
+ if (i == 20) {
|
|
+ dev_err(hdmi->dev, "sink flt isn't ready\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ hdmi_modb(hdmi, SCDC_UPD_FLAGS_RD_IRQ, SCDC_UPD_FLAGS_RD_IRQ,
|
|
+ MAINUNIT_1_INT_MASK_N);
|
|
+ hdmi_modb(hdmi, SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR,
|
|
+ SCDC_UPD_FLAGS_POLL_EN | SCDC_UPD_FLAGS_AUTO_CLR,
|
|
+ SCDC_CONFIG0);
|
|
+
|
|
+ /* max ffe level 3 */
|
|
+ val = 3 << 4 | hdmi_set_frl_mask(rate);
|
|
+ drm_scdc_writeb(hdmi->ddc, 0x31, val);
|
|
+
|
|
+ /* select FRL_RATE & FFE_LEVELS */
|
|
+ hdmi_writel(hdmi, ffe_lv, FLT_CONFIG0);
|
|
+
|
|
+ /* Start LTS_3 state in source DUT */
|
|
+ reinit_completion(&hdmi->flt_cmp);
|
|
+ hdmi_modb(hdmi, FLT_EXIT_TO_LTSP_IRQ, FLT_EXIT_TO_LTSP_IRQ,
|
|
+ MAINUNIT_1_INT_MASK_N);
|
|
+ hdmi_writel(hdmi, 1, FLT_CONTROL0);
|
|
+
|
|
+ /* wait for completed link training at source side */
|
|
+ stat = wait_for_completion_timeout(&hdmi->flt_cmp, HZ * 2);
|
|
+ if (!stat) {
|
|
+ dev_err(hdmi->dev, "wait lts3 finish time out\n");
|
|
+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN |
|
|
+ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0);
|
|
+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ,
|
|
+ MAINUNIT_1_INT_MASK_N);
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+
|
|
+ if (!(hdmi->flt_intr & FLT_EXIT_TO_LTSP_IRQ)) {
|
|
+ dev_err(hdmi->dev, "not to ltsp\n");
|
|
+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN |
|
|
+ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0);
|
|
+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ,
|
|
+ MAINUNIT_1_INT_MASK_N);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define HDMI_MODE_FRL_MASK BIT(30)
|
|
+
|
|
+static void hdmi_set_op_mode(struct dw_hdmi_qp *hdmi,
|
|
+ struct dw_hdmi_link_config *link_cfg,
|
|
+ const struct drm_connector *connector)
|
|
+{
|
|
+ int frl_rate;
|
|
+ int i;
|
|
+
|
|
+ /* set sink frl mode disable and wait sink ready */
|
|
+ hdmi_writel(hdmi, 0, FLT_CONFIG0);
|
|
+ if (dw_hdmi_support_scdc(hdmi, &connector->display_info))
|
|
+ drm_scdc_writeb(hdmi->ddc, 0x31, 0);
|
|
+ /*
|
|
+ * some TVs must wait a while before switching frl mode resolution,
|
|
+ * or the signal may not be recognized.
|
|
+ */
|
|
+ msleep(200);
|
|
+
|
|
+ if (!link_cfg->frl_mode) {
|
|
+ dev_info(hdmi->dev, "dw hdmi qp use tmds mode\n");
|
|
+ hdmi_modb(hdmi, 0, OPMODE_FRL, LINK_CONFIG0);
|
|
+ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (link_cfg->frl_lanes == 4)
|
|
+ hdmi_modb(hdmi, OPMODE_FRL_4LANES, OPMODE_FRL_4LANES,
|
|
+ LINK_CONFIG0);
|
|
+ else
|
|
+ hdmi_modb(hdmi, 0, OPMODE_FRL_4LANES, LINK_CONFIG0);
|
|
+
|
|
+ hdmi_modb(hdmi, 1, OPMODE_FRL, LINK_CONFIG0);
|
|
+
|
|
+ frl_rate = link_cfg->frl_lanes * link_cfg->rate_per_lane;
|
|
+ hdmi_start_flt(hdmi, frl_rate);
|
|
+
|
|
+ for (i = 0; i < 50; i++) {
|
|
+ hdmi_modb(hdmi, PKTSCHED_NULL_TX_EN, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN);
|
|
+ mdelay(1);
|
|
+ hdmi_modb(hdmi, 0, PKTSCHED_NULL_TX_EN, PKTSCHED_PKT_EN);
|
|
+ }
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock)
|
|
+{
|
|
+ unsigned long tmdsclock = mpixelclock;
|
|
+ unsigned int depth =
|
|
+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
|
|
+
|
|
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
|
|
+ switch (depth) {
|
|
+ case 16:
|
|
+ tmdsclock = mpixelclock * 2;
|
|
+ break;
|
|
+ case 12:
|
|
+ tmdsclock = mpixelclock * 3 / 2;
|
|
+ break;
|
|
+ case 10:
|
|
+ tmdsclock = mpixelclock * 5 / 4;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return tmdsclock;
|
|
+}
|
|
+
|
|
+//[CC:] is connector param different from hdmi->connector?
|
|
+//[CC:] probably it possible to hook the whole implementation into dw-hdmi.c
|
|
+static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi,
|
|
+ struct drm_connector *connector,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ int ret;
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode;
|
|
+ struct dw_hdmi_link_config *link_cfg;
|
|
+ u8 bytes = 0;
|
|
+
|
|
+ hdmi->vic = drm_match_cea_mode(mode);
|
|
+ if (!hdmi->vic)
|
|
+ dev_dbg(hdmi->dev, "Non-CEA mode used in HDMI\n");
|
|
+ else
|
|
+ dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
|
|
+
|
|
+ if (hdmi->plat_data->get_enc_out_encoding)
|
|
+ hdmi->hdmi_data.enc_out_encoding =
|
|
+ hdmi->plat_data->get_enc_out_encoding(data);
|
|
+ else if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
|
|
+ (hdmi->vic == 21) || (hdmi->vic == 22) ||
|
|
+ (hdmi->vic == 2) || (hdmi->vic == 3) ||
|
|
+ (hdmi->vic == 17) || (hdmi->vic == 18))
|
|
+ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
|
|
+ else
|
|
+ hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
|
|
+
|
|
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
|
|
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
|
|
+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
|
|
+ } else {
|
|
+ hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
|
|
+ hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
|
|
+ }
|
|
+ /* Get input format from plat data or fallback to RGB888 */
|
|
+ if (hdmi->plat_data->get_input_bus_format)
|
|
+ hdmi->hdmi_data.enc_in_bus_format =
|
|
+ hdmi->plat_data->get_input_bus_format(data);
|
|
+ else if (hdmi->plat_data->input_bus_format)
|
|
+ hdmi->hdmi_data.enc_in_bus_format =
|
|
+ hdmi->plat_data->input_bus_format;
|
|
+ else
|
|
+ hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
+
|
|
+ /* Default to RGB888 output format */
|
|
+ if (hdmi->plat_data->get_output_bus_format)
|
|
+ hdmi->hdmi_data.enc_out_bus_format =
|
|
+ hdmi->plat_data->get_output_bus_format(data);
|
|
+ else
|
|
+ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
+
|
|
+ /* Get input encoding from plat data or fallback to none */
|
|
+ if (hdmi->plat_data->get_enc_in_encoding)
|
|
+ hdmi->hdmi_data.enc_in_encoding =
|
|
+ hdmi->plat_data->get_enc_in_encoding(data);
|
|
+ else if (hdmi->plat_data->input_bus_encoding)
|
|
+ hdmi->hdmi_data.enc_in_encoding =
|
|
+ hdmi->plat_data->input_bus_encoding;
|
|
+ else
|
|
+ hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
|
|
+
|
|
+ if (hdmi->plat_data->get_quant_range)
|
|
+ hdmi->hdmi_data.quant_range =
|
|
+ hdmi->plat_data->get_quant_range(data);
|
|
+ else
|
|
+ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
|
|
+
|
|
+ if (hdmi->plat_data->get_link_cfg)
|
|
+ link_cfg = hdmi->plat_data->get_link_cfg(data);
|
|
+ else
|
|
+ return -EINVAL;
|
|
+
|
|
+ hdmi->phy.ops->set_mode(hdmi, hdmi->phy.data, HDMI_MODE_FRL_MASK,
|
|
+ link_cfg->frl_mode);
|
|
+
|
|
+ /*
|
|
+ * 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)
|
|
+ */
|
|
+ hdmi->hdmi_data.pix_repet_factor =
|
|
+ (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
|
|
+ hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
|
|
+
|
|
+ vmode->previous_pixelclock = vmode->mpixelclock;
|
|
+ //[CC:] no split mode
|
|
+ // if (hdmi->plat_data->split_mode)
|
|
+ // mode->crtc_clock /= 2;
|
|
+ vmode->mpixelclock = mode->crtc_clock * 1000;
|
|
+ if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
|
+ vmode->mpixelclock *= 2;
|
|
+ dev_dbg(hdmi->dev, "final pixclk = %ld\n", vmode->mpixelclock);
|
|
+ vmode->previous_tmdsclock = vmode->mtmdsclock;
|
|
+ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ vmode->mtmdsclock /= 2;
|
|
+ dev_info(hdmi->dev, "final tmdsclk = %d\n", vmode->mtmdsclock);
|
|
+
|
|
+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (hdmi->plat_data->set_grf_cfg)
|
|
+ hdmi->plat_data->set_grf_cfg(data);
|
|
+
|
|
+ if (hdmi->sink_has_audio) {
|
|
+ dev_dbg(hdmi->dev, "sink has audio support\n");
|
|
+
|
|
+ /* HDMI Initialization Step E - Configure audio */
|
|
+ hdmi_clk_regenerator_update_pixel_clock(hdmi);
|
|
+ hdmi_enable_audio_clk(hdmi, hdmi->audio_enable);
|
|
+ }
|
|
+
|
|
+ /* not for DVI mode */
|
|
+ if (hdmi->sink_is_hdmi) {
|
|
+ dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
|
|
+ hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0);
|
|
+ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
|
+ if (!link_cfg->frl_mode) {
|
|
+ if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK) {
|
|
+ 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));
|
|
+ //[CC:] use dw_hdmi_set_high_tmds_clock_ratio()
|
|
+ drm_scdc_set_high_tmds_clock_ratio(connector, 1);
|
|
+ drm_scdc_set_scrambling(connector, 1);
|
|
+ hdmi_writel(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);
|
|
+ }
|
|
+ hdmi_writel(hdmi, 0, SCRAMB_CONFIG0);
|
|
+ }
|
|
+ }
|
|
+ /* HDMI Initialization Step F - Configure AVI InfoFrame */
|
|
+ hdmi_config_AVI(hdmi, connector, mode);
|
|
+ hdmi_config_CVTEM(hdmi);
|
|
+ hdmi_config_drm_infoframe(hdmi, connector);
|
|
+ hdmi_set_op_mode(hdmi, link_cfg, connector);
|
|
+ } else {
|
|
+ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
|
+ hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0);
|
|
+ dev_info(hdmi->dev, "%s DVI mode\n", __func__);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+ struct dw_hdmi_qp *secondary = NULL;
|
|
+ enum drm_connector_status result, result_secondary;
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+ hdmi->force = DRM_FORCE_UNSPECIFIED;
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+
|
|
+ if (hdmi->plat_data->left)
|
|
+ secondary = hdmi->plat_data->left;
|
|
+ else if (hdmi->plat_data->right)
|
|
+ secondary = hdmi->plat_data->right;
|
|
+
|
|
+ result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
|
|
+
|
|
+ if (secondary) {
|
|
+ result_secondary = secondary->phy.ops->read_hpd(secondary, secondary->phy.data);
|
|
+ if (result == connector_status_connected &&
|
|
+ result_secondary == connector_status_connected)
|
|
+ result = connector_status_connected;
|
|
+ else
|
|
+ result = connector_status_disconnected;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+ if (result != hdmi->last_connector_result) {
|
|
+ dev_dbg(hdmi->dev, "read_hpd result: %d", result);
|
|
+ handle_plugged_change(hdmi,
|
|
+ result == connector_status_connected);
|
|
+ hdmi->last_connector_result = result;
|
|
+ }
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_update_hdr_property(struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct dw_hdmi_qp *hdmi = container_of(connector, struct dw_hdmi_qp,
|
|
+ connector);
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+ const struct hdr_static_metadata *metadata =
|
|
+ &connector->hdr_sink_metadata.hdmi_type1;
|
|
+ size_t size = sizeof(*metadata);
|
|
+ struct drm_property *property;
|
|
+ struct drm_property_blob *blob;
|
|
+ int ret;
|
|
+
|
|
+ if (hdmi->plat_data->get_hdr_property)
|
|
+ property = hdmi->plat_data->get_hdr_property(data);
|
|
+ else
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (hdmi->plat_data->get_hdr_blob)
|
|
+ blob = hdmi->plat_data->get_hdr_blob(data);
|
|
+ else
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = drm_property_replace_global_blob(dev, &blob, size, metadata,
|
|
+ &connector->base, property);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+ struct hdr_static_metadata *metedata =
|
|
+ &connector->hdr_sink_metadata.hdmi_type1;
|
|
+ struct edid *edid;
|
|
+ struct drm_display_mode *mode;
|
|
+ struct drm_display_info *info = &connector->display_info;
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+ int i, ret = 0;
|
|
+
|
|
+ if (!hdmi->ddc)
|
|
+ return 0;
|
|
+
|
|
+ memset(metedata, 0, sizeof(*metedata));
|
|
+ edid = drm_get_edid(connector, hdmi->ddc);
|
|
+ if (edid) {
|
|
+ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
|
|
+ edid->width_cm, edid->height_cm);
|
|
+
|
|
+ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
|
|
+ hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
|
|
+ drm_connector_update_edid_property(connector, edid);
|
|
+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
|
|
+ if (hdmi->plat_data->get_edid_dsc_info)
|
|
+ hdmi->plat_data->get_edid_dsc_info(data, edid);
|
|
+ ret = drm_add_edid_modes(connector, edid);
|
|
+ dw_hdmi_update_hdr_property(connector);
|
|
+ if (ret > 0 && hdmi->plat_data->split_mode) {
|
|
+ struct dw_hdmi_qp *secondary = NULL;
|
|
+ void *secondary_data;
|
|
+
|
|
+ if (hdmi->plat_data->left)
|
|
+ secondary = hdmi->plat_data->left;
|
|
+ else if (hdmi->plat_data->right)
|
|
+ secondary = hdmi->plat_data->right;
|
|
+
|
|
+ if (!secondary)
|
|
+ return -ENOMEM;
|
|
+ secondary_data = secondary->plat_data->phy_data;
|
|
+
|
|
+ list_for_each_entry(mode, &connector->probed_modes, head)
|
|
+ hdmi->plat_data->convert_to_split_mode(mode);
|
|
+
|
|
+ secondary->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
|
|
+ secondary->sink_has_audio = drm_detect_monitor_audio(edid);
|
|
+ cec_notifier_set_phys_addr_from_edid(secondary->cec_notifier, edid);
|
|
+ if (secondary->plat_data->get_edid_dsc_info)
|
|
+ secondary->plat_data->get_edid_dsc_info(secondary_data, edid);
|
|
+ }
|
|
+ kfree(edid);
|
|
+ } else {
|
|
+ hdmi->sink_is_hdmi = true;
|
|
+ hdmi->sink_has_audio = true;
|
|
+
|
|
+ if (hdmi->plat_data->split_mode) {
|
|
+ if (hdmi->plat_data->left) {
|
|
+ hdmi->plat_data->left->sink_is_hdmi = true;
|
|
+ hdmi->plat_data->left->sink_has_audio = true;
|
|
+ } else if (hdmi->plat_data->right) {
|
|
+ hdmi->plat_data->right->sink_is_hdmi = true;
|
|
+ hdmi->plat_data->right->sink_has_audio = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) {
|
|
+ const struct drm_display_mode *ptr =
|
|
+ &dw_hdmi_default_modes[i];
|
|
+
|
|
+ mode = drm_mode_duplicate(connector->dev, ptr);
|
|
+ if (mode) {
|
|
+ if (!i)
|
|
+ mode->type = DRM_MODE_TYPE_PREFERRED;
|
|
+ drm_mode_probed_add(connector, mode);
|
|
+ ret++;
|
|
+ }
|
|
+ }
|
|
+ if (ret > 0 && hdmi->plat_data->split_mode) {
|
|
+ struct drm_display_mode *mode;
|
|
+
|
|
+ list_for_each_entry(mode, &connector->probed_modes, head)
|
|
+ hdmi->plat_data->convert_to_split_mode(mode);
|
|
+ }
|
|
+ info->edid_hdmi_rgb444_dc_modes = 0;
|
|
+ info->hdmi.y420_dc_modes = 0;
|
|
+ info->color_formats = 0;
|
|
+
|
|
+ dev_info(hdmi->dev, "failed to get edid\n");
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_atomic_connector_set_property(struct drm_connector *connector,
|
|
+ struct drm_connector_state *state,
|
|
+ struct drm_property *property,
|
|
+ uint64_t val)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops;
|
|
+
|
|
+ if (ops && ops->set_property)
|
|
+ return ops->set_property(connector, state, property,
|
|
+ val, hdmi->plat_data->phy_data);
|
|
+ else
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_atomic_connector_get_property(struct drm_connector *connector,
|
|
+ const struct drm_connector_state *state,
|
|
+ struct drm_property *property,
|
|
+ uint64_t *val)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+ const struct dw_hdmi_property_ops *ops = hdmi->plat_data->property_ops;
|
|
+
|
|
+ if (ops && ops->get_property)
|
|
+ return ops->get_property(connector, state, property,
|
|
+ val, hdmi->plat_data->phy_data);
|
|
+ else
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_connector_set_property(struct drm_connector *connector,
|
|
+ struct drm_property *property, uint64_t val)
|
|
+{
|
|
+ return dw_hdmi_atomic_connector_set_property(connector, NULL,
|
|
+ property, val);
|
|
+}
|
|
+
|
|
+static void dw_hdmi_attach_properties(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24;
|
|
+ const struct dw_hdmi_property_ops *ops =
|
|
+ hdmi->plat_data->property_ops;
|
|
+
|
|
+ if (ops && ops->attach_properties)
|
|
+ return ops->attach_properties(&hdmi->connector, color, 0,
|
|
+ hdmi->plat_data->phy_data);
|
|
+}
|
|
+
|
|
+static void dw_hdmi_destroy_properties(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ const struct dw_hdmi_property_ops *ops =
|
|
+ hdmi->plat_data->property_ops;
|
|
+
|
|
+ if (ops && ops->destroy_properties)
|
|
+ return ops->destroy_properties(&hdmi->connector,
|
|
+ hdmi->plat_data->phy_data);
|
|
+}
|
|
+
|
|
+static struct drm_encoder *
|
|
+dw_hdmi_connector_best_encoder(struct drm_connector *connector)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+
|
|
+ return hdmi->bridge.encoder;
|
|
+}
|
|
+
|
|
+static bool dw_hdmi_color_changed(struct drm_connector *connector,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+ struct drm_connector_state *old_state =
|
|
+ drm_atomic_get_old_connector_state(state, connector);
|
|
+ struct drm_connector_state *new_state =
|
|
+ drm_atomic_get_new_connector_state(state, connector);
|
|
+ bool ret = false;
|
|
+
|
|
+ if (hdmi->plat_data->get_color_changed)
|
|
+ ret = hdmi->plat_data->get_color_changed(data);
|
|
+
|
|
+ if (new_state->colorspace != old_state->colorspace)
|
|
+ ret = true;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static bool hdr_metadata_equal(const struct drm_connector_state *old_state,
|
|
+ const struct drm_connector_state *new_state)
|
|
+{
|
|
+ struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
|
|
+ struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
|
|
+
|
|
+ if (!old_blob || !new_blob)
|
|
+ return old_blob == new_blob;
|
|
+
|
|
+ if (old_blob->length != new_blob->length)
|
|
+ return false;
|
|
+
|
|
+ return !memcmp(old_blob->data, new_blob->data, old_blob->length);
|
|
+}
|
|
+
|
|
+static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+ struct drm_connector_state *old_state =
|
|
+ drm_atomic_get_old_connector_state(state, connector);
|
|
+ struct drm_connector_state *new_state =
|
|
+ drm_atomic_get_new_connector_state(state, connector);
|
|
+ struct drm_crtc *crtc = new_state->crtc;
|
|
+ struct drm_crtc_state *crtc_state;
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+ struct drm_display_mode *mode = NULL;
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+ struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode;
|
|
+
|
|
+ if (!crtc)
|
|
+ return 0;
|
|
+
|
|
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
+ if (IS_ERR(crtc_state))
|
|
+ return PTR_ERR(crtc_state);
|
|
+
|
|
+ /*
|
|
+ * If HDMI is enabled in uboot, it's need to record
|
|
+ * drm_display_mode and set phy status to enabled.
|
|
+ */
|
|
+ if (!vmode->mpixelclock) {
|
|
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
+ if (hdmi->plat_data->get_enc_in_encoding)
|
|
+ hdmi->hdmi_data.enc_in_encoding =
|
|
+ hdmi->plat_data->get_enc_in_encoding(data);
|
|
+ if (hdmi->plat_data->get_enc_out_encoding)
|
|
+ hdmi->hdmi_data.enc_out_encoding =
|
|
+ hdmi->plat_data->get_enc_out_encoding(data);
|
|
+ if (hdmi->plat_data->get_input_bus_format)
|
|
+ hdmi->hdmi_data.enc_in_bus_format =
|
|
+ hdmi->plat_data->get_input_bus_format(data);
|
|
+ if (hdmi->plat_data->get_output_bus_format)
|
|
+ hdmi->hdmi_data.enc_out_bus_format =
|
|
+ hdmi->plat_data->get_output_bus_format(data);
|
|
+
|
|
+ mode = &crtc_state->mode;
|
|
+ if (hdmi->plat_data->split_mode) {
|
|
+ hdmi->plat_data->convert_to_origin_mode(mode);
|
|
+ mode->crtc_clock /= 2;
|
|
+ }
|
|
+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
|
|
+ vmode->mpixelclock = mode->crtc_clock * 1000;
|
|
+ vmode->previous_pixelclock = mode->clock;
|
|
+ vmode->previous_tmdsclock = mode->clock;
|
|
+ vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi,
|
|
+ vmode->mpixelclock);
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ vmode->mtmdsclock /= 2;
|
|
+
|
|
+ /*
|
|
+ * If uboot logo enabled, atomic_enable won't be called,
|
|
+ * but atomic_disable will be called when hdmi plug out.
|
|
+ * That will cause dclk enable count is incorrect. So
|
|
+ * we should check ipi/link/video clk to determine whether
|
|
+ * uboot logo is enabled.
|
|
+ */
|
|
+ if (hdmi->initialized && !hdmi->dclk_en) {
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->plat_data->dclk_set)
|
|
+ hdmi->plat_data->dclk_set(data, true);
|
|
+ hdmi->dclk_en = true;
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!hdr_metadata_equal(old_state, new_state) ||
|
|
+ dw_hdmi_color_changed(connector, state)) {
|
|
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
+ if (IS_ERR(crtc_state))
|
|
+ return PTR_ERR(crtc_state);
|
|
+
|
|
+ crtc_state->mode_changed = true;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dw_hdmi_connector_force(struct drm_connector *connector)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ container_of(connector, struct dw_hdmi_qp, connector);
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+
|
|
+ if (hdmi->force != connector->force) {
|
|
+ if (!hdmi->disabled && connector->force == DRM_FORCE_OFF)
|
|
+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI,
|
|
+ false);
|
|
+ else if (hdmi->disabled && connector->force == DRM_FORCE_ON)
|
|
+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI,
|
|
+ true);
|
|
+ }
|
|
+
|
|
+ hdmi->force = connector->force;
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+}
|
|
+
|
|
+static int dw_hdmi_qp_fill_modes(struct drm_connector *connector, u32 max_x,
|
|
+ u32 max_y)
|
|
+{
|
|
+ return drm_helper_probe_single_connector_modes(connector, 9000, 9000);
|
|
+}
|
|
+
|
|
+static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
|
|
+ .fill_modes = dw_hdmi_qp_fill_modes,
|
|
+ .detect = dw_hdmi_connector_detect,
|
|
+ .destroy = drm_connector_cleanup,
|
|
+ .force = dw_hdmi_connector_force,
|
|
+ .reset = drm_atomic_helper_connector_reset,
|
|
+ .set_property = dw_hdmi_connector_set_property,
|
|
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
|
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
|
+ .atomic_set_property = dw_hdmi_atomic_connector_set_property,
|
|
+ .atomic_get_property = dw_hdmi_atomic_connector_get_property,
|
|
+};
|
|
+
|
|
+static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
|
|
+ .get_modes = dw_hdmi_connector_get_modes,
|
|
+ .best_encoder = dw_hdmi_connector_best_encoder,
|
|
+ .atomic_check = dw_hdmi_connector_atomic_check,
|
|
+};
|
|
+
|
|
+static int dw_hdmi_qp_bridge_attach(struct drm_bridge *bridge,
|
|
+ enum drm_bridge_attach_flags flags)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
|
+ struct drm_encoder *encoder = bridge->encoder;
|
|
+ struct drm_connector *connector = &hdmi->connector;
|
|
+ struct cec_connector_info conn_info;
|
|
+ struct cec_notifier *notifier;
|
|
+
|
|
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
|
+ return 0;
|
|
+
|
|
+ connector->interlace_allowed = 1;
|
|
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
|
|
+
|
|
+ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
|
|
+
|
|
+ // [CC:] use drm_connector_init_with_ddc or drmm_connector_init
|
|
+ // to provide ddc reference
|
|
+ drm_connector_init_with_ddc(bridge->dev, connector,
|
|
+ &dw_hdmi_connector_funcs,
|
|
+ DRM_MODE_CONNECTOR_HDMIA,
|
|
+ hdmi->ddc);
|
|
+
|
|
+ drm_connector_attach_encoder(connector, encoder);
|
|
+ dw_hdmi_attach_properties(hdmi);
|
|
+
|
|
+ cec_fill_conn_info_from_drm(&conn_info, connector);
|
|
+ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
|
|
+ if (!notifier)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_lock(&hdmi->cec_notifier_mutex);
|
|
+ hdmi->cec_notifier = notifier;
|
|
+ mutex_unlock(&hdmi->cec_notifier_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dw_hdmi_qp_bridge_detach(struct drm_bridge *bridge)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
|
+
|
|
+ mutex_lock(&hdmi->cec_notifier_mutex);
|
|
+ cec_notifier_conn_unregister(hdmi->cec_notifier);
|
|
+ hdmi->cec_notifier = NULL;
|
|
+ mutex_unlock(&hdmi->cec_notifier_mutex);
|
|
+}
|
|
+
|
|
+static void dw_hdmi_qp_bridge_mode_set(struct drm_bridge *bridge,
|
|
+ const struct drm_display_mode *orig_mode,
|
|
+ const struct drm_display_mode *mode)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+
|
|
+ /* Store the display mode for plugin/DKMS poweron events */
|
|
+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
|
|
+ if (hdmi->plat_data->split_mode)
|
|
+ hdmi->plat_data->convert_to_origin_mode(&hdmi->previous_mode);
|
|
+
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+}
|
|
+
|
|
+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)
|
|
+{
|
|
+ return MODE_OK;
|
|
+}
|
|
+
|
|
+static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *old_state)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
|
+ struct drm_atomic_state *state = old_state->base.state;
|
|
+ struct drm_connector *connector;
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+
|
|
+ connector = drm_atomic_get_new_connector_for_encoder(state,
|
|
+ bridge->encoder);
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+ hdmi->curr_conn = connector;
|
|
+ dw_hdmi_qp_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
|
|
+ hdmi->disabled = false;
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+
|
|
+ if (!hdmi->dclk_en) {
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->plat_data->dclk_set)
|
|
+ hdmi->plat_data->dclk_set(data, true);
|
|
+ hdmi->dclk_en = true;
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+ }
|
|
+
|
|
+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);
|
|
+ handle_plugged_change(hdmi, true);
|
|
+}
|
|
+
|
|
+static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
|
+ struct drm_bridge_state *old_state)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
|
+ void *data = hdmi->plat_data->phy_data;
|
|
+
|
|
+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
|
|
+ handle_plugged_change(hdmi, false);
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+
|
|
+ hdmi->curr_conn = NULL;
|
|
+
|
|
+ if (hdmi->dclk_en) {
|
|
+ mutex_lock(&hdmi->audio_mutex);
|
|
+ if (hdmi->plat_data->dclk_set)
|
|
+ hdmi->plat_data->dclk_set(data, false);
|
|
+ hdmi->dclk_en = false;
|
|
+ mutex_unlock(&hdmi->audio_mutex);
|
|
+ };
|
|
+
|
|
+ if (hdmi->phy.ops->disable)
|
|
+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
|
|
+ hdmi->disabled = true;
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+}
|
|
+
|
|
+static const struct drm_bridge_funcs dw_hdmi_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_qp_bridge_detach,
|
|
+ .mode_set = dw_hdmi_qp_bridge_mode_set,
|
|
+ .mode_valid = dw_hdmi_qp_bridge_mode_valid,
|
|
+ .atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
|
|
+ .atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
|
|
+};
|
|
+
|
|
+void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap)
|
|
+{
|
|
+ hdmi->cec_adap = adap;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_cec_adap);
|
|
+
|
|
+static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = dev_id;
|
|
+ struct dw_hdmi_qp_i2c *i2c = hdmi->i2c;
|
|
+ u32 stat;
|
|
+
|
|
+ stat = hdmi_readl(hdmi, MAINUNIT_1_INT_STATUS);
|
|
+
|
|
+ i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ |
|
|
+ I2CM_NACK_RCVD_IRQ);
|
|
+ hdmi->scdc_intr = stat & (SCDC_UPD_FLAGS_RD_IRQ |
|
|
+ SCDC_UPD_FLAGS_CHG_IRQ |
|
|
+ SCDC_UPD_FLAGS_CLR_IRQ |
|
|
+ SCDC_RR_REPLY_STOP_IRQ |
|
|
+ SCDC_NACK_RCVD_IRQ);
|
|
+ hdmi->flt_intr = stat & (FLT_EXIT_TO_LTSP_IRQ |
|
|
+ FLT_EXIT_TO_LTS4_IRQ |
|
|
+ FLT_EXIT_TO_LTSL_IRQ);
|
|
+
|
|
+ // dev_dbg(hdmi->dev, "i2c main unit irq:%#x\n", stat);
|
|
+ if (i2c->stat) {
|
|
+ hdmi_writel(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR);
|
|
+ complete(&i2c->cmp);
|
|
+ }
|
|
+
|
|
+ if (hdmi->flt_intr) {
|
|
+ dev_dbg(hdmi->dev, "i2c flt irq:%#x\n", hdmi->flt_intr);
|
|
+ hdmi_writel(hdmi, hdmi->flt_intr, MAINUNIT_1_INT_CLEAR);
|
|
+ complete(&hdmi->flt_cmp);
|
|
+ }
|
|
+
|
|
+ if (hdmi->scdc_intr) {
|
|
+ u8 val;
|
|
+
|
|
+ dev_dbg(hdmi->dev, "i2c scdc irq:%#x\n", hdmi->scdc_intr);
|
|
+ hdmi_writel(hdmi, hdmi->scdc_intr, MAINUNIT_1_INT_CLEAR);
|
|
+ val = hdmi_readl(hdmi, SCDC_STATUS0);
|
|
+
|
|
+ /* frl start */
|
|
+ if (val & BIT(4)) {
|
|
+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_POLL_EN |
|
|
+ SCDC_UPD_FLAGS_AUTO_CLR, SCDC_CONFIG0);
|
|
+ hdmi_modb(hdmi, 0, SCDC_UPD_FLAGS_RD_IRQ,
|
|
+ MAINUNIT_1_INT_MASK_N);
|
|
+ dev_info(hdmi->dev, "frl start\n");
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ if (stat)
|
|
+ return IRQ_HANDLED;
|
|
+
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = dev_id;
|
|
+ u32 stat;
|
|
+
|
|
+ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
|
|
+ if (stat) {
|
|
+ dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat);
|
|
+ stat &= ~stat;
|
|
+ hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N);
|
|
+ return IRQ_WAKE_THREAD;
|
|
+ }
|
|
+
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = dev_id;
|
|
+ u32 stat;
|
|
+
|
|
+ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS);
|
|
+ if (stat) {
|
|
+ dev_dbg(hdmi->dev, "earc irq %#x\n", stat);
|
|
+ stat &= ~stat;
|
|
+ hdmi_writel(hdmi, stat, EARCRX_0_INT_MASK_N);
|
|
+ return IRQ_WAKE_THREAD;
|
|
+ }
|
|
+
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = dev_id;
|
|
+ u32 stat;
|
|
+
|
|
+ stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
|
|
+
|
|
+ if (!stat)
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = dev_id;
|
|
+ u32 stat;
|
|
+
|
|
+ stat = hdmi_readl(hdmi, EARCRX_0_INT_STATUS);
|
|
+
|
|
+ if (!stat)
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ hdmi_writel(hdmi, stat, EARCRX_0_INT_CLEAR);
|
|
+
|
|
+ hdmi->earc_intr = stat;
|
|
+ complete(&hdmi->earc_cmp);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_detect_phy(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ u8 phy_type;
|
|
+
|
|
+ phy_type = hdmi->plat_data->phy_force_vendor ?
|
|
+ DW_HDMI_PHY_VENDOR_PHY : 0;
|
|
+
|
|
+ if (phy_type == DW_HDMI_PHY_VENDOR_PHY) {
|
|
+ /* Vendor PHYs require support from the glue layer. */
|
|
+ if (!hdmi->plat_data->qp_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->qp_phy_ops;
|
|
+ hdmi->phy.data = hdmi->plat_data->phy_data;
|
|
+ hdmi->phy.name = hdmi->plat_data->phy_name;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change)
|
|
+{
|
|
+ enum drm_connector_status status = plug_in ?
|
|
+ connector_status_connected : connector_status_disconnected;
|
|
+
|
|
+ if (!plug_in)
|
|
+ cec_notifier_set_phys_addr(hdmi->cec_notifier,
|
|
+ CEC_PHYS_ADDR_INVALID);
|
|
+
|
|
+ if (hdmi->bridge.dev) {
|
|
+ if (change && hdmi->cec_adap && hdmi->cec_adap->devnode.registered)
|
|
+ cec_queue_pin_hpd_event(hdmi->cec_adap, plug_in, ktime_get());
|
|
+ drm_bridge_hpd_notify(&hdmi->bridge, status);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_cec_set_hpd);
|
|
+
|
|
+// static void dw_hdmi_qp_cec_enable(struct dw_hdmi_qp *hdmi)
|
|
+// {
|
|
+// mutex_lock(&hdmi->mutex);
|
|
+// hdmi_modb(hdmi, 0, CEC_SWDISABLE, GLOBAL_SWDISABLE);
|
|
+// mutex_unlock(&hdmi->mutex);
|
|
+// }
|
|
+//
|
|
+// static void dw_hdmi_qp_cec_disable(struct dw_hdmi_qp *hdmi)
|
|
+// {
|
|
+// mutex_lock(&hdmi->mutex);
|
|
+// hdmi_modb(hdmi, CEC_SWDISABLE, CEC_SWDISABLE, GLOBAL_SWDISABLE);
|
|
+// mutex_unlock(&hdmi->mutex);
|
|
+// }
|
|
+//
|
|
+// static const struct dw_hdmi_qp_cec_ops dw_hdmi_qp_cec_ops = {
|
|
+// .enable = dw_hdmi_qp_cec_enable,
|
|
+// .disable = dw_hdmi_qp_cec_disable,
|
|
+// .write = hdmi_writel,
|
|
+// .read = hdmi_readl,
|
|
+// };
|
|
+
|
|
+static const struct regmap_config hdmi_regmap_config = {
|
|
+ .reg_bits = 32,
|
|
+ .val_bits = 32,
|
|
+ .reg_stride = 4,
|
|
+ .max_register = EARCRX_1_INT_FORCE,
|
|
+};
|
|
+
|
|
+struct dw_hdmi_qp_reg_table {
|
|
+ int reg_base;
|
|
+ int reg_end;
|
|
+};
|
|
+
|
|
+static const struct dw_hdmi_qp_reg_table hdmi_reg_table[] = {
|
|
+ {0x0, 0xc},
|
|
+ {0x14, 0x1c},
|
|
+ {0x44, 0x48},
|
|
+ {0x50, 0x58},
|
|
+ {0x80, 0x84},
|
|
+ {0xa0, 0xc4},
|
|
+ {0xe0, 0xe8},
|
|
+ {0xf0, 0x118},
|
|
+ {0x140, 0x140},
|
|
+ {0x150, 0x150},
|
|
+ {0x160, 0x168},
|
|
+ {0x180, 0x180},
|
|
+ {0x800, 0x800},
|
|
+ {0x808, 0x808},
|
|
+ {0x814, 0x814},
|
|
+ {0x81c, 0x824},
|
|
+ {0x834, 0x834},
|
|
+ {0x840, 0x864},
|
|
+ {0x86c, 0x86c},
|
|
+ {0x880, 0x89c},
|
|
+ {0x8e0, 0x8e8},
|
|
+ {0x900, 0x900},
|
|
+ {0x908, 0x90c},
|
|
+ {0x920, 0x938},
|
|
+ {0x920, 0x938},
|
|
+ {0x960, 0x960},
|
|
+ {0x968, 0x968},
|
|
+ {0xa20, 0xa20},
|
|
+ {0xa30, 0xa30},
|
|
+ {0xa40, 0xa40},
|
|
+ {0xa54, 0xa54},
|
|
+ {0xa80, 0xaac},
|
|
+ {0xab4, 0xab8},
|
|
+ {0xb00, 0xcbc},
|
|
+ {0xce0, 0xce0},
|
|
+ {0xd00, 0xddc},
|
|
+ {0xe20, 0xe24},
|
|
+ {0xe40, 0xe44},
|
|
+ {0xe4c, 0xe4c},
|
|
+ {0xe60, 0xe80},
|
|
+ {0xea0, 0xf24},
|
|
+ {0x1004, 0x100c},
|
|
+ {0x1020, 0x1030},
|
|
+ {0x1040, 0x1050},
|
|
+ {0x1060, 0x1068},
|
|
+ {0x1800, 0x1820},
|
|
+ {0x182c, 0x182c},
|
|
+ {0x1840, 0x1940},
|
|
+ {0x1960, 0x1a60},
|
|
+ {0x1b00, 0x1b00},
|
|
+ {0x1c00, 0x1c00},
|
|
+ {0x3000, 0x3000},
|
|
+ {0x3010, 0x3014},
|
|
+ {0x3020, 0x3024},
|
|
+ {0x3800, 0x3800},
|
|
+ {0x3810, 0x3814},
|
|
+ {0x3820, 0x3824},
|
|
+ {0x3830, 0x3834},
|
|
+ {0x3840, 0x3844},
|
|
+ {0x3850, 0x3854},
|
|
+ {0x3860, 0x3864},
|
|
+ {0x3870, 0x3874},
|
|
+ {0x4000, 0x4004},
|
|
+ {0x4800, 0x4800},
|
|
+ {0x4810, 0x4814},
|
|
+};
|
|
+
|
|
+static int dw_hdmi_ctrl_show(struct seq_file *s, void *v)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = s->private;
|
|
+ u32 i = 0, j = 0, val = 0;
|
|
+
|
|
+ seq_puts(s, "\n---------------------------------------------------");
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
|
|
+ for (j = hdmi_reg_table[i].reg_base;
|
|
+ j <= hdmi_reg_table[i].reg_end; j += 4) {
|
|
+ val = hdmi_readl(hdmi, j);
|
|
+
|
|
+ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0)
|
|
+ seq_printf(s, "\n>>>hdmi_ctl %04x:", j);
|
|
+ seq_printf(s, " %08x", val);
|
|
+ }
|
|
+ }
|
|
+ seq_puts(s, "\n---------------------------------------------------\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, dw_hdmi_ctrl_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static ssize_t
|
|
+dw_hdmi_ctrl_write(struct file *file, const char __user *buf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi =
|
|
+ ((struct seq_file *)file->private_data)->private;
|
|
+ u32 reg, val;
|
|
+ char kbuf[25];
|
|
+
|
|
+ if (count > 24) {
|
|
+ dev_err(hdmi->dev, "out of buf range\n");
|
|
+ return count;
|
|
+ }
|
|
+
|
|
+ if (copy_from_user(kbuf, buf, count))
|
|
+ return -EFAULT;
|
|
+ kbuf[count - 1] = '\0';
|
|
+
|
|
+ if (sscanf(kbuf, "%x %x", ®, &val) == -1)
|
|
+ return -EFAULT;
|
|
+ if (reg > EARCRX_1_INT_FORCE) {
|
|
+ dev_err(hdmi->dev, "it is no a hdmi register\n");
|
|
+ return count;
|
|
+ }
|
|
+ dev_info(hdmi->dev, "/**********hdmi register config******/");
|
|
+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
|
|
+ hdmi_writel(hdmi, val, reg);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const struct file_operations dw_hdmi_ctrl_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = dw_hdmi_ctrl_open,
|
|
+ .read = seq_read,
|
|
+ .write = dw_hdmi_ctrl_write,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+};
|
|
+
|
|
+static int dw_hdmi_status_show(struct seq_file *s, void *v)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi = s->private;
|
|
+ u32 val;
|
|
+
|
|
+ seq_puts(s, "PHY: ");
|
|
+ if (hdmi->disabled) {
|
|
+ seq_puts(s, "disabled\n");
|
|
+ return 0;
|
|
+ }
|
|
+ seq_puts(s, "enabled\t\t\tMode: ");
|
|
+ if (hdmi->sink_is_hdmi)
|
|
+ seq_puts(s, "HDMI\n");
|
|
+ else
|
|
+ seq_puts(s, "DVI\n");
|
|
+
|
|
+ if (hdmi->hdmi_data.video_mode.mpixelclock > 600000000) {
|
|
+ seq_printf(s, "FRL Mode Pixel Clk: %luHz\n",
|
|
+ hdmi->hdmi_data.video_mode.mpixelclock);
|
|
+ } else {
|
|
+ if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000)
|
|
+ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4;
|
|
+ else
|
|
+ val = hdmi->hdmi_data.video_mode.mtmdsclock;
|
|
+ seq_printf(s, "TMDS Mode Pixel Clk: %luHz\t\tTMDS Clk: %uHz\n",
|
|
+ hdmi->hdmi_data.video_mode.mpixelclock, val);
|
|
+ }
|
|
+
|
|
+ seq_puts(s, "Color Format: ");
|
|
+ if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ seq_puts(s, "RGB");
|
|
+ else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ seq_puts(s, "YUV444");
|
|
+ else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ seq_puts(s, "YUV422");
|
|
+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ seq_puts(s, "YUV420");
|
|
+ else
|
|
+ seq_puts(s, "UNKNOWN");
|
|
+ val = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
|
|
+ seq_printf(s, "\t\tColor Depth: %d bit\n", val);
|
|
+ seq_puts(s, "Colorimetry: ");
|
|
+ switch (hdmi->hdmi_data.enc_out_encoding) {
|
|
+ case V4L2_YCBCR_ENC_601:
|
|
+ seq_puts(s, "ITU.BT601");
|
|
+ break;
|
|
+ case V4L2_YCBCR_ENC_709:
|
|
+ seq_puts(s, "ITU.BT709");
|
|
+ break;
|
|
+ case V4L2_YCBCR_ENC_BT2020:
|
|
+ seq_puts(s, "ITU.BT2020");
|
|
+ break;
|
|
+ default: /* Carries no data */
|
|
+ seq_puts(s, "ITU.BT601");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ seq_puts(s, "\t\tEOTF: ");
|
|
+
|
|
+ val = hdmi_readl(hdmi, PKTSCHED_PKT_EN);
|
|
+ if (!(val & PKTSCHED_DRMI_TX_EN)) {
|
|
+ seq_puts(s, "Off\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1);
|
|
+ val = (val >> 8) & 0x7;
|
|
+ switch (val) {
|
|
+ case HDMI_EOTF_TRADITIONAL_GAMMA_SDR:
|
|
+ seq_puts(s, "SDR");
|
|
+ break;
|
|
+ case HDMI_EOTF_TRADITIONAL_GAMMA_HDR:
|
|
+ seq_puts(s, "HDR");
|
|
+ break;
|
|
+ case HDMI_EOTF_SMPTE_ST2084:
|
|
+ seq_puts(s, "ST2084");
|
|
+ break;
|
|
+ case HDMI_EOTF_BT_2100_HLG:
|
|
+ seq_puts(s, "HLG");
|
|
+ break;
|
|
+ default:
|
|
+ seq_puts(s, "Not Defined\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS1);
|
|
+ val = (val >> 16) & 0xffff;
|
|
+ seq_printf(s, "\nx0: %d", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2);
|
|
+ val = val & 0xffff;
|
|
+ seq_printf(s, "\t\t\t\ty0: %d\n", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS2);
|
|
+ val = (val >> 16) & 0xffff;
|
|
+ seq_printf(s, "x1: %d", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3);
|
|
+ val = val & 0xffff;
|
|
+ seq_printf(s, "\t\t\t\ty1: %d\n", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS3);
|
|
+ val = (val >> 16) & 0xffff;
|
|
+ seq_printf(s, "x2: %d", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4);
|
|
+ val = val & 0xffff;
|
|
+ seq_printf(s, "\t\t\t\ty2: %d\n", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS4);
|
|
+ val = (val >> 16) & 0xffff;
|
|
+ seq_printf(s, "white x: %d", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5);
|
|
+ val = val & 0xffff;
|
|
+ seq_printf(s, "\t\t\twhite y: %d\n", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS5);
|
|
+ val = (val >> 16) & 0xffff;
|
|
+ seq_printf(s, "max lum: %d", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6);
|
|
+ val = val & 0xffff;
|
|
+ seq_printf(s, "\t\t\tmin lum: %d\n", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS6);
|
|
+ val = (val >> 16) & 0xffff;
|
|
+ seq_printf(s, "max cll: %d", val);
|
|
+ val = hdmi_readl(hdmi, PKT_DRMI_CONTENTS7);
|
|
+ val = val & 0xffff;
|
|
+ seq_printf(s, "\t\t\tmax fall: %d\n", val);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_status_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, dw_hdmi_status_show, inode->i_private);
|
|
+}
|
|
+
|
|
+static const struct file_operations dw_hdmi_status_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = dw_hdmi_status_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+};
|
|
+
|
|
+static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ u8 buf[11];
|
|
+
|
|
+ snprintf(buf, sizeof(buf), "dw-hdmi%d", hdmi->plat_data->id);
|
|
+ hdmi->debugfs_dir = debugfs_create_dir(buf, NULL);
|
|
+ if (IS_ERR(hdmi->debugfs_dir)) {
|
|
+ dev_err(dev, "failed to create debugfs dir!\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ debugfs_create_file("status", 0400, hdmi->debugfs_dir,
|
|
+ hdmi, &dw_hdmi_status_fops);
|
|
+ debugfs_create_file("ctrl", 0600, hdmi->debugfs_dir,
|
|
+ hdmi, &dw_hdmi_ctrl_fops);
|
|
+}
|
|
+
|
|
+static struct dw_hdmi_qp *
|
|
+__dw_hdmi_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_qp *hdmi;
|
|
+ // struct dw_hdmi_qp_i2s_audio_data audio;
|
|
+ // struct platform_device_info pdevinfo;
|
|
+ // struct dw_hdmi_qp_cec_data cec;
|
|
+ struct resource *iores = NULL;
|
|
+ int irq;
|
|
+ int ret;
|
|
+
|
|
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
|
|
+ if (!hdmi)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ hdmi->connector.stereo_allowed = 1;
|
|
+ hdmi->plat_data = plat_data;
|
|
+ hdmi->dev = dev;
|
|
+ hdmi->sample_rate = 48000;
|
|
+ hdmi->disabled = true;
|
|
+
|
|
+ mutex_init(&hdmi->mutex);
|
|
+ mutex_init(&hdmi->audio_mutex);
|
|
+ mutex_init(&hdmi->cec_notifier_mutex);
|
|
+
|
|
+ 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 = &hdmi_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;
|
|
+ }
|
|
+
|
|
+ ret = dw_hdmi_detect_phy(hdmi);
|
|
+ if (ret < 0)
|
|
+ goto err_res;
|
|
+
|
|
+ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N);
|
|
+ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N);
|
|
+ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0);
|
|
+ if ((hdmi_readl(hdmi, CMU_STATUS) & DISPLAY_CLK_MONITOR) == DISPLAY_CLK_LOCKED) {
|
|
+ hdmi->initialized = true;
|
|
+ hdmi->disabled = false;
|
|
+ }
|
|
+
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
+ if (irq < 0) {
|
|
+ ret = irq;
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ hdmi->avp_irq = irq;
|
|
+ ret = devm_request_threaded_irq(dev, hdmi->avp_irq,
|
|
+ dw_hdmi_qp_avp_hardirq,
|
|
+ dw_hdmi_qp_avp_irq, IRQF_SHARED,
|
|
+ dev_name(dev), hdmi);
|
|
+ if (ret)
|
|
+ goto err_res;
|
|
+
|
|
+ irq = platform_get_irq(pdev, 1);
|
|
+ if (irq < 0) {
|
|
+ ret = irq;
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ // cec.irq = irq;
|
|
+
|
|
+ irq = platform_get_irq(pdev, 2);
|
|
+ if (irq < 0) {
|
|
+ ret = irq;
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ hdmi->earc_irq = irq;
|
|
+ ret = devm_request_threaded_irq(dev, hdmi->earc_irq,
|
|
+ dw_hdmi_qp_earc_hardirq,
|
|
+ dw_hdmi_qp_earc_irq, IRQF_SHARED,
|
|
+ dev_name(dev), hdmi);
|
|
+ if (ret)
|
|
+ goto err_res;
|
|
+
|
|
+ irq = platform_get_irq(pdev, 3);
|
|
+ if (irq < 0) {
|
|
+ ret = irq;
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ hdmi->main_irq = irq;
|
|
+ ret = devm_request_threaded_irq(dev, hdmi->main_irq,
|
|
+ dw_hdmi_qp_main_hardirq, NULL,
|
|
+ IRQF_SHARED, dev_name(dev), hdmi);
|
|
+ if (ret)
|
|
+ goto err_res;
|
|
+
|
|
+ hdmi_init_clk_regenerator(hdmi);
|
|
+
|
|
+ /* If DDC bus is not specified, try to register HDMI I2C bus */
|
|
+ if (!hdmi->ddc) {
|
|
+ hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
|
|
+ if (IS_ERR(hdmi->ddc))
|
|
+ hdmi->ddc = NULL;
|
|
+ /*
|
|
+ * Read high and low time from device tree. If not available use
|
|
+ * the default timing scl clock rate is about 99.6KHz.
|
|
+ */
|
|
+ if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns",
|
|
+ &hdmi->i2c->scl_high_ns))
|
|
+ hdmi->i2c->scl_high_ns = 4708;
|
|
+ if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns",
|
|
+ &hdmi->i2c->scl_low_ns))
|
|
+ hdmi->i2c->scl_low_ns = 4916;
|
|
+ }
|
|
+
|
|
+ hdmi->bridge.driver_private = hdmi;
|
|
+ hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
|
|
+#ifdef CONFIG_OF
|
|
+ hdmi->bridge.of_node = pdev->dev.of_node;
|
|
+#endif
|
|
+
|
|
+ if (hdmi->phy.ops->setup_hpd)
|
|
+ hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
|
|
+
|
|
+ hdmi->connector.ycbcr_420_allowed = hdmi->plat_data->ycbcr_420_allowed;
|
|
+
|
|
+ // audio.hdmi = hdmi;
|
|
+ // audio.eld = hdmi->connector.eld;
|
|
+ // audio.write = hdmi_writel;
|
|
+ // audio.read = hdmi_readl;
|
|
+ // audio.mod = hdmi_modb;
|
|
+ // hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
|
|
+ // hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
|
|
+
|
|
+ // memset(&pdevinfo, 0, sizeof(pdevinfo));
|
|
+ // pdevinfo.parent = dev;
|
|
+ // pdevinfo.id = PLATFORM_DEVID_AUTO;
|
|
+ // pdevinfo.name = "dw-hdmi-qp-i2s-audio";
|
|
+ // pdevinfo.data = &audio;
|
|
+ // pdevinfo.size_data = sizeof(audio);
|
|
+ // pdevinfo.dma_mask = DMA_BIT_MASK(32);
|
|
+ // hdmi->audio = platform_device_register_full(&pdevinfo);
|
|
+
|
|
+ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable);
|
|
+ if (IS_ERR(hdmi->extcon)) {
|
|
+ dev_err(hdmi->dev, "allocate extcon failed\n");
|
|
+ ret = PTR_ERR(hdmi->extcon);
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "failed to register extcon: %d\n", ret);
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI,
|
|
+ EXTCON_PROP_DISP_HPD);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev,
|
|
+ "failed to set USB property capability: %d\n", ret);
|
|
+ goto err_res;
|
|
+ }
|
|
+
|
|
+ // cec.hdmi = hdmi;
|
|
+ // cec.ops = &dw_hdmi_qp_cec_ops;
|
|
+ // pdevinfo.name = "dw-hdmi-qp-cec";
|
|
+ // pdevinfo.data = &cec;
|
|
+ // pdevinfo.size_data = sizeof(cec);
|
|
+ // pdevinfo.dma_mask = 0;
|
|
+ // hdmi->cec = platform_device_register_full(&pdevinfo);
|
|
+
|
|
+ /* Reset HDMI DDC I2C master controller and mute I2CM interrupts */
|
|
+ if (hdmi->i2c)
|
|
+ dw_hdmi_i2c_init(hdmi);
|
|
+
|
|
+ init_completion(&hdmi->flt_cmp);
|
|
+ init_completion(&hdmi->earc_cmp);
|
|
+
|
|
+ if (of_property_read_bool(np, "scramble-low-rates"))
|
|
+ hdmi->scramble_low_rates = true;
|
|
+
|
|
+ dw_hdmi_register_debugfs(dev, hdmi);
|
|
+
|
|
+ return hdmi;
|
|
+
|
|
+err_res:
|
|
+ if (hdmi->i2c)
|
|
+ i2c_del_adapter(&hdmi->i2c->adap);
|
|
+ else
|
|
+ i2c_put_adapter(hdmi->ddc);
|
|
+
|
|
+ return ERR_PTR(ret);
|
|
+}
|
|
+
|
|
+static void __dw_hdmi_remove(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ if (hdmi->avp_irq)
|
|
+ disable_irq(hdmi->avp_irq);
|
|
+
|
|
+ if (hdmi->main_irq)
|
|
+ disable_irq(hdmi->main_irq);
|
|
+
|
|
+ if (hdmi->earc_irq)
|
|
+ disable_irq(hdmi->earc_irq);
|
|
+
|
|
+ debugfs_remove_recursive(hdmi->debugfs_dir);
|
|
+
|
|
+ if (!hdmi->plat_data->first_screen) {
|
|
+ dw_hdmi_destroy_properties(hdmi);
|
|
+ hdmi->connector.funcs->destroy(&hdmi->connector);
|
|
+ }
|
|
+
|
|
+ if (hdmi->audio && !IS_ERR(hdmi->audio))
|
|
+ platform_device_unregister(hdmi->audio);
|
|
+
|
|
+ // [CC:] dw_hdmi_rockchip_unbind() also calls drm_encoder_cleanup()
|
|
+ // and causes a seg fault due to NULL ptr dererence
|
|
+ // if (hdmi->bridge.encoder && !hdmi->plat_data->first_screen)
|
|
+ // hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
|
|
+ //
|
|
+ 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);
|
|
+}
|
|
+
|
|
+/* -----------------------------------------------------------------------------
|
|
+ * Bind/unbind API, used from platforms based on the component framework.
|
|
+ */
|
|
+struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
|
+ struct drm_encoder *encoder,
|
|
+ struct dw_hdmi_plat_data *plat_data)
|
|
+{
|
|
+ struct dw_hdmi_qp *hdmi;
|
|
+ int ret;
|
|
+
|
|
+ hdmi = __dw_hdmi_probe(pdev, plat_data);
|
|
+ if (IS_ERR(hdmi))
|
|
+ return hdmi;
|
|
+
|
|
+ if (!plat_data->first_screen) {
|
|
+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
|
|
+ if (ret) {
|
|
+ __dw_hdmi_remove(hdmi);
|
|
+ dev_err(hdmi->dev, "Failed to initialize bridge with drm\n");
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
+ plat_data->connector = &hdmi->connector;
|
|
+ }
|
|
+
|
|
+ if (plat_data->split_mode && !hdmi->plat_data->first_screen) {
|
|
+ struct dw_hdmi_qp *secondary = NULL;
|
|
+
|
|
+ if (hdmi->plat_data->left)
|
|
+ secondary = hdmi->plat_data->left;
|
|
+ else if (hdmi->plat_data->right)
|
|
+ secondary = hdmi->plat_data->right;
|
|
+
|
|
+ if (!secondary)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ ret = drm_bridge_attach(encoder, &secondary->bridge, &hdmi->bridge,
|
|
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
|
+ if (ret)
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
+ return hdmi;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind);
|
|
+
|
|
+void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ __dw_hdmi_remove(hdmi);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_unbind);
|
|
+
|
|
+void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ if (!hdmi) {
|
|
+ dev_warn(dev, "Hdmi has not been initialized\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+
|
|
+ /*
|
|
+ * When system shutdown, hdmi should be disabled.
|
|
+ * When system suspend, dw_hdmi_qp_bridge_disable will disable hdmi first.
|
|
+ * To prevent duplicate operation, we should determine whether hdmi
|
|
+ * has been disabled.
|
|
+ */
|
|
+ if (!hdmi->disabled)
|
|
+ hdmi->disabled = true;
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+
|
|
+ if (hdmi->avp_irq)
|
|
+ disable_irq(hdmi->avp_irq);
|
|
+
|
|
+ if (hdmi->main_irq)
|
|
+ disable_irq(hdmi->main_irq);
|
|
+
|
|
+ if (hdmi->earc_irq)
|
|
+ disable_irq(hdmi->earc_irq);
|
|
+
|
|
+ pinctrl_pm_select_sleep_state(dev);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend);
|
|
+
|
|
+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi)
|
|
+{
|
|
+ if (!hdmi) {
|
|
+ dev_warn(dev, "Hdmi has not been initialized\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ hdmi_writel(hdmi, 0, MAINUNIT_0_INT_MASK_N);
|
|
+ hdmi_writel(hdmi, 0, MAINUNIT_1_INT_MASK_N);
|
|
+ hdmi_writel(hdmi, 428571429, TIMER_BASE_CONFIG0);
|
|
+
|
|
+ pinctrl_pm_select_default_state(dev);
|
|
+
|
|
+ hdmi->cec_adap->ops->adap_enable(hdmi->cec_adap, true);
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+ if (hdmi->i2c)
|
|
+ dw_hdmi_i2c_init(hdmi);
|
|
+ if (hdmi->avp_irq)
|
|
+ enable_irq(hdmi->avp_irq);
|
|
+
|
|
+ if (hdmi->main_irq)
|
|
+ enable_irq(hdmi->main_irq);
|
|
+
|
|
+ if (hdmi->earc_irq)
|
|
+ enable_irq(hdmi->earc_irq);
|
|
+
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume);
|
|
+
|
|
+MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.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 000000000..4cac70f2d
|
|
--- /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/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
index 52d91a0df..0d6fd9578 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -162,6 +162,8 @@ struct dw_hdmi {
|
|
void __iomem *regs;
|
|
bool sink_is_hdmi;
|
|
bool sink_has_audio;
|
|
+ bool support_hdmi;
|
|
+ int force_output;
|
|
|
|
struct pinctrl *pinctrl;
|
|
struct pinctrl_state *default_state;
|
|
@@ -254,6 +256,25 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
|
|
hdmi_modb(hdmi, data << shift, mask, reg);
|
|
}
|
|
|
|
+static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi)
|
|
+{
|
|
+ bool sink_hdmi;
|
|
+
|
|
+ sink_hdmi = hdmi->sink_is_hdmi;
|
|
+
|
|
+ if (hdmi->force_output == 1)
|
|
+ hdmi->sink_is_hdmi = true;
|
|
+ else if (hdmi->force_output == 2)
|
|
+ hdmi->sink_is_hdmi = false;
|
|
+ else
|
|
+ hdmi->sink_is_hdmi = hdmi->support_hdmi;
|
|
+
|
|
+ if (sink_hdmi != hdmi->sink_is_hdmi)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
|
|
{
|
|
hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
|
|
@@ -2532,6 +2553,45 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
|
|
return 0;
|
|
}
|
|
|
|
+void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi)
|
|
+{
|
|
+ if (!hdmi->bridge_is_on)
|
|
+ return;
|
|
+
|
|
+ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
|
|
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
|
|
+ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range);
|
|
+
|
|
+void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val)
|
|
+{
|
|
+ hdmi->force_output = val;
|
|
+
|
|
+ if (!dw_hdmi_check_output_type_changed(hdmi))
|
|
+ return;
|
|
+
|
|
+ if (!hdmi->bridge_is_on)
|
|
+ return;
|
|
+
|
|
+ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
|
|
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
|
|
+ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type);
|
|
+
|
|
+bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi)
|
|
+{
|
|
+ return hdmi->sink_is_hdmi;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi);
|
|
+
|
|
+int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi)
|
|
+{
|
|
+ return hdmi->support_hdmi;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap);
|
|
+
|
|
static void dw_hdmi_connector_force(struct drm_connector *connector)
|
|
{
|
|
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
|
|
@@ -3683,6 +3743,35 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi)
|
|
}
|
|
EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
|
|
|
|
+void dw_hdmi_suspend(struct dw_hdmi *hdmi)
|
|
+{
|
|
+ if (!hdmi)
|
|
+ return;
|
|
+
|
|
+ mutex_lock(&hdmi->mutex);
|
|
+
|
|
+ /*
|
|
+ * When system shutdown, hdmi should be disabled.
|
|
+ * When system suspend, dw_hdmi_bridge_disable will disable hdmi first.
|
|
+ * To prevent duplicate operation, we should determine whether hdmi
|
|
+ * has been disabled.
|
|
+ */
|
|
+ if (!hdmi->disabled) {
|
|
+ hdmi->disabled = true;
|
|
+ dw_hdmi_update_power(hdmi);
|
|
+ dw_hdmi_update_phy_mask(hdmi);
|
|
+ }
|
|
+ mutex_unlock(&hdmi->mutex);
|
|
+
|
|
+ //[CC: needed?]
|
|
+ // if (hdmi->irq)
|
|
+ // disable_irq(hdmi->irq);
|
|
+ // cancel_delayed_work(&hdmi->work);
|
|
+ // flush_workqueue(hdmi->workqueue);
|
|
+ pinctrl_pm_select_sleep_state(hdmi->dev);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_suspend);
|
|
+
|
|
void dw_hdmi_resume(struct dw_hdmi *hdmi)
|
|
{
|
|
dw_hdmi_init_hw(hdmi);
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
|
|
index af43a0414..8ebdec725 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
|
|
@@ -851,6 +851,10 @@ enum {
|
|
HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
|
|
HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
|
|
|
|
+/* HDMI_FC_GCP */
|
|
+ HDMI_FC_GCP_SET_AVMUTE = 0x2,
|
|
+ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1,
|
|
+
|
|
/* FC_DBGFORCE field values */
|
|
HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
|
|
HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
|
|
diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c
|
|
index 9edc111be..a578f918f 100644
|
|
--- a/drivers/gpu/drm/drm_displayid.c
|
|
+++ b/drivers/gpu/drm/drm_displayid.c
|
|
@@ -3,6 +3,7 @@
|
|
* Copyright © 2021 Intel Corporation
|
|
*/
|
|
|
|
+#include "linux/export.h"
|
|
#include <drm/drm_displayid.h>
|
|
#include <drm/drm_edid.h>
|
|
#include <drm/drm_print.h>
|
|
@@ -79,6 +80,7 @@ void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
|
|
|
|
iter->drm_edid = drm_edid;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(displayid_iter_edid_begin);
|
|
|
|
static const struct displayid_block *
|
|
displayid_iter_block(const struct displayid_iter *iter)
|
|
@@ -154,11 +156,13 @@ __displayid_iter_next(struct displayid_iter *iter)
|
|
return block;
|
|
}
|
|
}
|
|
+EXPORT_SYMBOL_GPL(__displayid_iter_next);
|
|
|
|
void displayid_iter_end(struct displayid_iter *iter)
|
|
{
|
|
memset(iter, 0, sizeof(*iter));
|
|
}
|
|
+EXPORT_SYMBOL_GPL(displayid_iter_end);
|
|
|
|
/* DisplayID Structure Version/Revision from the Base Section. */
|
|
u8 displayid_version(const struct displayid_iter *iter)
|
|
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
|
index 341550199..52f457b16 100644
|
|
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
|
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
|
|
@@ -4,22 +4,33 @@
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/media-bus-format.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/phy/phy.h>
|
|
+#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
+#include <drm/drm_of.h>
|
|
+#include <drm/drm_crtc_helper.h>
|
|
+#include <drm/display/drm_dsc.h>
|
|
+#include <drm/drm_edid.h>
|
|
#include <drm/bridge/dw_hdmi.h>
|
|
#include <drm/drm_edid.h>
|
|
#include <drm/drm_of.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
#include <drm/drm_simple_kms_helper.h>
|
|
|
|
+#include <uapi/linux/videodev2.h>
|
|
+
|
|
#include "rockchip_drm_drv.h"
|
|
#include "rockchip_drm_vop.h"
|
|
|
|
+#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
|
|
+
|
|
#define RK3228_GRF_SOC_CON2 0x0408
|
|
#define RK3228_HDMI_SDAIN_MSK BIT(14)
|
|
#define RK3228_HDMI_SCLIN_MSK BIT(13)
|
|
@@ -30,8 +41,11 @@
|
|
|
|
#define RK3288_GRF_SOC_CON6 0x025C
|
|
#define RK3288_HDMI_LCDC_SEL BIT(4)
|
|
-#define RK3328_GRF_SOC_CON2 0x0408
|
|
+#define RK3288_GRF_SOC_CON16 0x03a8
|
|
+#define RK3288_HDMI_LCDC0_YUV420 BIT(2)
|
|
+#define RK3288_HDMI_LCDC1_YUV420 BIT(3)
|
|
|
|
+#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)
|
|
@@ -55,32 +69,154 @@
|
|
#define RK3568_HDMI_SDAIN_MSK BIT(15)
|
|
#define RK3568_HDMI_SCLIN_MSK BIT(14)
|
|
|
|
-#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
|
|
+#define RK3588_GRF_SOC_CON2 0x0308
|
|
+#define RK3588_HDMI1_HPD_INT_MSK BIT(15)
|
|
+#define RK3588_HDMI1_HPD_INT_CLR BIT(14)
|
|
+#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_LOW_MORETHAN100MS BIT(20)
|
|
+#define RK3588_HDMI0_HPD_PORT_LEVEL BIT(19)
|
|
+#define RK3588_HDMI0_IHPD_PORT BIT(18)
|
|
+#define RK3588_HDMI0_OHPD_INT BIT(17)
|
|
+#define RK3588_HDMI0_LEVEL_INT BIT(16)
|
|
+#define RK3588_HDMI0_INTR_CHANGE_CNT (0x7 << 13)
|
|
+#define RK3588_HDMI1_LOW_MORETHAN100MS BIT(28)
|
|
+#define RK3588_HDMI1_HPD_PORT_LEVEL BIT(27)
|
|
+#define RK3588_HDMI1_IHPD_PORT BIT(26)
|
|
+#define RK3588_HDMI1_OHPD_INT BIT(25)
|
|
+#define RK3588_HDMI1_LEVEL_INT BIT(24)
|
|
+#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21)
|
|
+
|
|
+#define RK3588_GRF_VO1_CON3 0x000c
|
|
+#define RK3588_COLOR_FORMAT_MASK 0xf
|
|
+#define RK3588_YUV444 0x2
|
|
+#define RK3588_YUV420 0x3
|
|
+#define RK3588_COMPRESSED_DATA 0xb
|
|
+#define RK3588_COLOR_DEPTH_MASK (0xf << 4)
|
|
+#define RK3588_8BPC (0x5 << 4)
|
|
+#define RK3588_10BPC (0x6 << 4)
|
|
+#define RK3588_CECIN_MASK BIT(8)
|
|
+#define RK3588_SCLIN_MASK BIT(9)
|
|
+#define RK3588_SDAIN_MASK BIT(10)
|
|
+#define RK3588_MODE_MASK BIT(11)
|
|
+#define RK3588_COMPRESS_MODE_MASK BIT(12)
|
|
+#define RK3588_I2S_SEL_MASK BIT(13)
|
|
+#define RK3588_SPDIF_SEL_MASK BIT(14)
|
|
+#define RK3588_GRF_VO1_CON4 0x0010
|
|
+#define RK3588_HDMI21_MASK BIT(0)
|
|
+#define RK3588_GRF_VO1_CON9 0x0024
|
|
+#define RK3588_HDMI0_GRANT_SEL BIT(10)
|
|
+#define RK3588_HDMI0_GRANT_SW BIT(11)
|
|
+#define RK3588_HDMI1_GRANT_SEL BIT(12)
|
|
+#define RK3588_HDMI1_GRANT_SW BIT(13)
|
|
+#define RK3588_GRF_VO1_CON6 0x0018
|
|
+#define RK3588_GRF_VO1_CON7 0x001c
|
|
+
|
|
+#define COLOR_DEPTH_10BIT BIT(31)
|
|
+#define HDMI_FRL_MODE BIT(30)
|
|
+#define HDMI_EARC_MODE BIT(29)
|
|
+
|
|
+#define HDMI20_MAX_RATE 600000
|
|
+#define HDMI_8K60_RATE 2376000
|
|
|
|
/**
|
|
* struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips
|
|
* @lcdsel_grf_reg: grf register offset of lcdc select
|
|
+ * @ddc_en_reg: grf register offset of hdmi ddc enable
|
|
* @lcdsel_big: reg value of selecting vop big for HDMI
|
|
* @lcdsel_lit: reg value of selecting vop little for HDMI
|
|
*/
|
|
struct rockchip_hdmi_chip_data {
|
|
int lcdsel_grf_reg;
|
|
+ int ddc_en_reg;
|
|
u32 lcdsel_big;
|
|
u32 lcdsel_lit;
|
|
+ bool split_mode;
|
|
+};
|
|
+
|
|
+enum hdmi_frl_rate_per_lane {
|
|
+ FRL_12G_PER_LANE = 12,
|
|
+ FRL_10G_PER_LANE = 10,
|
|
+ FRL_8G_PER_LANE = 8,
|
|
+ FRL_6G_PER_LANE = 6,
|
|
+ FRL_3G_PER_LANE = 3,
|
|
};
|
|
|
|
struct rockchip_hdmi {
|
|
struct device *dev;
|
|
struct regmap *regmap;
|
|
+ struct regmap *vo1_regmap;
|
|
struct rockchip_encoder encoder;
|
|
+ struct drm_device *drm_dev;
|
|
const struct rockchip_hdmi_chip_data *chip_data;
|
|
- const struct dw_hdmi_plat_data *plat_data;
|
|
+ struct dw_hdmi_plat_data *plat_data;
|
|
+ struct clk *aud_clk;
|
|
struct clk *ref_clk;
|
|
struct clk *grf_clk;
|
|
+ struct clk *hclk_vio;
|
|
+ struct clk *hclk_vo1;
|
|
+ struct clk *hclk_vop;
|
|
+ struct clk *hpd_clk;
|
|
+ struct clk *pclk;
|
|
+ struct clk *earc_clk;
|
|
+ struct clk *hdmitx_ref;
|
|
struct dw_hdmi *hdmi;
|
|
+ struct dw_hdmi_qp *hdmi_qp;
|
|
+
|
|
struct regulator *avdd_0v9;
|
|
struct regulator *avdd_1v8;
|
|
struct phy *phy;
|
|
+
|
|
+ u32 max_tmdsclk;
|
|
+ bool unsupported_yuv_input;
|
|
+ bool unsupported_deep_color;
|
|
+ bool skip_check_420_mode;
|
|
+ u8 force_output;
|
|
+ u8 id;
|
|
+ bool hpd_stat;
|
|
+ bool is_hdmi_qp;
|
|
+ bool user_split_mode;
|
|
+
|
|
+ unsigned long bus_format;
|
|
+ unsigned long output_bus_format;
|
|
+ unsigned long enc_out_encoding;
|
|
+ int color_changed;
|
|
+ int hpd_irq;
|
|
+ int vp_id;
|
|
+
|
|
+ struct drm_property *color_depth_property;
|
|
+ struct drm_property *hdmi_output_property;
|
|
+ struct drm_property *colordepth_capacity;
|
|
+ struct drm_property *outputmode_capacity;
|
|
+ struct drm_property *quant_range;
|
|
+ struct drm_property *hdr_panel_metadata_property;
|
|
+ struct drm_property *next_hdr_sink_data_property;
|
|
+ struct drm_property *output_hdmi_dvi;
|
|
+ struct drm_property *output_type_capacity;
|
|
+ struct drm_property *user_split_mode_prop;
|
|
+
|
|
+ struct drm_property_blob *hdr_panel_blob_ptr;
|
|
+ struct drm_property_blob *next_hdr_data_ptr;
|
|
+
|
|
+ unsigned int colordepth;
|
|
+ unsigned int colorimetry;
|
|
+ unsigned int hdmi_quant_range;
|
|
+ unsigned int phy_bus_width;
|
|
+ enum rk_if_color_format hdmi_output;
|
|
+ struct rockchip_drm_sub_dev sub_dev;
|
|
+
|
|
+ u8 max_frl_rate_per_lane;
|
|
+ u8 max_lanes;
|
|
+ struct rockchip_drm_dsc_cap dsc_cap;
|
|
+ struct next_hdr_sink_data next_hdr_data;
|
|
+ struct dw_hdmi_link_config link_cfg;
|
|
+ struct gpio_desc *enable_gpio;
|
|
+
|
|
+ struct delayed_work work;
|
|
+ struct workqueue_struct *workqueue;
|
|
};
|
|
|
|
static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
|
|
@@ -203,13 +339,834 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
|
|
/*pixelclk symbol term vlev*/
|
|
{ 74250000, 0x8009, 0x0004, 0x0272},
|
|
{ 148500000, 0x802b, 0x0004, 0x028d},
|
|
+ { 165000000, 0x802b, 0x0004, 0x0209},
|
|
{ 297000000, 0x8039, 0x0005, 0x028d},
|
|
+ { 594000000, 0x8039, 0x0000, 0x019d},
|
|
{ ~0UL, 0x0000, 0x0000, 0x0000}
|
|
};
|
|
|
|
+enum ROW_INDEX_BPP {
|
|
+ ROW_INDEX_6BPP = 0,
|
|
+ ROW_INDEX_8BPP,
|
|
+ ROW_INDEX_10BPP,
|
|
+ ROW_INDEX_12BPP,
|
|
+ ROW_INDEX_23BPP,
|
|
+ MAX_ROW_INDEX
|
|
+};
|
|
+
|
|
+enum COLUMN_INDEX_BPC {
|
|
+ COLUMN_INDEX_8BPC = 0,
|
|
+ COLUMN_INDEX_10BPC,
|
|
+ COLUMN_INDEX_12BPC,
|
|
+ COLUMN_INDEX_14BPC,
|
|
+ COLUMN_INDEX_16BPC,
|
|
+ MAX_COLUMN_INDEX
|
|
+};
|
|
+
|
|
+#define PPS_TABLE_LEN 8
|
|
+#define PPS_BPP_LEN 4
|
|
+#define PPS_BPC_LEN 2
|
|
+
|
|
+struct pps_data {
|
|
+ u32 pic_width;
|
|
+ u32 pic_height;
|
|
+ u32 slice_width;
|
|
+ u32 slice_height;
|
|
+ bool convert_rgb;
|
|
+ u8 bpc;
|
|
+ u8 bpp;
|
|
+ u8 raw_pps[128];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Selected Rate Control Related Parameter Recommended Values
|
|
+ * from DSC_v1.11 spec & C Model release: DSC_model_20161212
|
|
+ */
|
|
+static struct pps_data pps_datas[PPS_TABLE_LEN] = {
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 8bpc 12bpp */
|
|
+ 7680, 4320, 960, 96, 1, 8, 192,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0,
|
|
+ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9,
|
|
+ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa,
|
|
+ 0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00,
|
|
+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00,
|
|
+ 0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8,
|
|
+ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76,
|
|
+ 0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 8bpc 11bpp */
|
|
+ 7680, 4320, 960, 96, 1, 8, 176,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28,
|
|
+ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0,
|
|
+ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33,
|
|
+ 0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00,
|
|
+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40,
|
|
+ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
|
|
+ 0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76,
|
|
+ 0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 8bpc 10bpp */
|
|
+ 7680, 4320, 960, 96, 1, 8, 160,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0,
|
|
+ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0,
|
|
+ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb,
|
|
+ 0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00,
|
|
+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40,
|
|
+ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
|
|
+ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6,
|
|
+ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 8bpc 9bpp */
|
|
+ 7680, 4320, 960, 96, 1, 8, 144,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38,
|
|
+ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7,
|
|
+ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa,
|
|
+ 0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00,
|
|
+ 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40,
|
|
+ 0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
|
|
+ 0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6,
|
|
+ 0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 10bpc 12bpp */
|
|
+ 7680, 4320, 960, 96, 1, 10, 192,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0,
|
|
+ 0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9,
|
|
+ 0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa,
|
|
+ 0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00,
|
|
+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00,
|
|
+ 0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8,
|
|
+ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76,
|
|
+ 0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 10bpc 11bpp */
|
|
+ 7680, 4320, 960, 96, 1, 10, 176,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28,
|
|
+ 0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0,
|
|
+ 0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33,
|
|
+ 0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00,
|
|
+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40,
|
|
+ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
|
|
+ 0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76,
|
|
+ 0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 10bpc 10bpp */
|
|
+ 7680, 4320, 960, 96, 1, 10, 160,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0,
|
|
+ 0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0,
|
|
+ 0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb,
|
|
+ 0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00,
|
|
+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40,
|
|
+ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
|
|
+ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6,
|
|
+ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* 7680x4320/960X96 rgb 10bpc 9bpp */
|
|
+ 7680, 4320, 960, 96, 1, 10, 144,
|
|
+ {
|
|
+ 0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0,
|
|
+ 0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38,
|
|
+ 0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7,
|
|
+ 0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa,
|
|
+ 0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00,
|
|
+ 0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
|
|
+ 0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
|
|
+ 0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40,
|
|
+ 0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
|
|
+ 0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6,
|
|
+ 0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74,
|
|
+ 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
|
|
+ },
|
|
+ },
|
|
+};
|
|
+
|
|
+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_RGB888_1X24:
|
|
+ case MEDIA_BUS_FMT_RGB101010_1X30:
|
|
+ case MEDIA_BUS_FMT_RGB121212_1X36:
|
|
+ case MEDIA_BUS_FMT_RGB161616_1X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_YUV8_1X24:
|
|
+ case MEDIA_BUS_FMT_YUV10_1X30:
|
|
+ case MEDIA_BUS_FMT_YUV12_1X36:
|
|
+ case MEDIA_BUS_FMT_YUV16_1X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_UYVY8_1X16:
|
|
+ case MEDIA_BUS_FMT_UYVY10_1X20:
|
|
+ case MEDIA_BUS_FMT_UYVY12_1X24:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
|
|
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_RGB888_1X24:
|
|
+ case MEDIA_BUS_FMT_YUV8_1X24:
|
|
+ case MEDIA_BUS_FMT_UYVY8_1X16:
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ return 8;
|
|
+
|
|
+ case MEDIA_BUS_FMT_RGB101010_1X30:
|
|
+ case MEDIA_BUS_FMT_YUV10_1X30:
|
|
+ case MEDIA_BUS_FMT_UYVY10_1X20:
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ return 10;
|
|
+
|
|
+ case MEDIA_BUS_FMT_RGB121212_1X36:
|
|
+ case MEDIA_BUS_FMT_YUV12_1X36:
|
|
+ case MEDIA_BUS_FMT_UYVY12_1X24:
|
|
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
|
|
+ return 12;
|
|
+
|
|
+ case MEDIA_BUS_FMT_RGB161616_1X48:
|
|
+ case MEDIA_BUS_FMT_YUV16_1X48:
|
|
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
|
|
+ return 16;
|
|
+
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static unsigned int
|
|
+hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock)
|
|
+{
|
|
+ unsigned int tmdsclock = pixelclock;
|
|
+ unsigned int depth =
|
|
+ hdmi_bus_fmt_color_depth(hdmi->output_bus_format);
|
|
+
|
|
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) {
|
|
+ switch (depth) {
|
|
+ case 16:
|
|
+ tmdsclock = pixelclock * 2;
|
|
+ break;
|
|
+ case 12:
|
|
+ tmdsclock = pixelclock * 3 / 2;
|
|
+ break;
|
|
+ case 10:
|
|
+ tmdsclock = pixelclock * 5 / 4;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return tmdsclock;
|
|
+}
|
|
+
|
|
+static int rockchip_hdmi_match_by_id(struct device *dev, const void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
|
|
+ const unsigned int *id = data;
|
|
+
|
|
+ return hdmi->id == *id;
|
|
+}
|
|
+
|
|
+static struct rockchip_hdmi *
|
|
+rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id)
|
|
+{
|
|
+ struct device *dev;
|
|
+
|
|
+ dev = driver_find_device(drv, NULL, &id, rockchip_hdmi_match_by_id);
|
|
+ if (!dev)
|
|
+ return NULL;
|
|
+
|
|
+ return dev_get_drvdata(dev);
|
|
+}
|
|
+
|
|
+static void hdmi_select_link_config(struct rockchip_hdmi *hdmi,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ unsigned int tmdsclk)
|
|
+{
|
|
+ struct drm_display_mode mode;
|
|
+ int max_lanes, max_rate_per_lane;
|
|
+ int max_dsc_lanes, max_dsc_rate_per_lane;
|
|
+ unsigned long max_frl_rate;
|
|
+
|
|
+ drm_mode_copy(&mode, &crtc_state->mode);
|
|
+ if (hdmi->plat_data->split_mode)
|
|
+ drm_mode_convert_to_origin_mode(&mode);
|
|
+
|
|
+ max_lanes = hdmi->max_lanes;
|
|
+ max_rate_per_lane = hdmi->max_frl_rate_per_lane;
|
|
+ max_frl_rate = max_lanes * max_rate_per_lane * 1000000;
|
|
+
|
|
+ hdmi->link_cfg.dsc_mode = false;
|
|
+ hdmi->link_cfg.frl_lanes = max_lanes;
|
|
+ hdmi->link_cfg.rate_per_lane = max_rate_per_lane;
|
|
+
|
|
+ if (!max_frl_rate || (tmdsclk < HDMI20_MAX_RATE && mode.clock < HDMI20_MAX_RATE)) {
|
|
+ dev_info(hdmi->dev, "use tmds mode\n");
|
|
+ hdmi->link_cfg.frl_mode = false;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ hdmi->link_cfg.frl_mode = true;
|
|
+
|
|
+ if (!hdmi->dsc_cap.v_1p2)
|
|
+ return;
|
|
+
|
|
+ max_dsc_lanes = hdmi->dsc_cap.max_lanes;
|
|
+ max_dsc_rate_per_lane =
|
|
+ hdmi->dsc_cap.max_frl_rate_per_lane;
|
|
+
|
|
+ if (mode.clock >= HDMI_8K60_RATE &&
|
|
+ !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) &&
|
|
+ !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) {
|
|
+ hdmi->link_cfg.dsc_mode = true;
|
|
+ hdmi->link_cfg.frl_lanes = max_dsc_lanes;
|
|
+ hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane;
|
|
+ } else {
|
|
+ hdmi->link_cfg.dsc_mode = false;
|
|
+ hdmi->link_cfg.frl_lanes = max_lanes;
|
|
+ hdmi->link_cfg.rate_per_lane = max_rate_per_lane;
|
|
+ }
|
|
+}
|
|
+
|
|
+/////////////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+static int hdmi_dsc_get_slice_height(int vactive)
|
|
+{
|
|
+ int slice_height;
|
|
+
|
|
+ /*
|
|
+ * Slice Height determination : HDMI2.1 Section 7.7.5.2
|
|
+ * Select smallest slice height >=96, that results in a valid PPS and
|
|
+ * requires minimum padding lines required for final slice.
|
|
+ *
|
|
+ * Assumption : Vactive is even.
|
|
+ */
|
|
+ for (slice_height = 96; slice_height <= vactive; slice_height += 2)
|
|
+ if (vactive % slice_height == 0)
|
|
+ return slice_height;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ int src_max_slices, int src_max_slice_width,
|
|
+ int hdmi_max_slices, int hdmi_throughput)
|
|
+{
|
|
+/* Pixel rates in KPixels/sec */
|
|
+#define HDMI_DSC_PEAK_PIXEL_RATE 2720000
|
|
+/*
|
|
+ * Rates at which the source and sink are required to process pixels in each
|
|
+ * slice, can be two levels: either at least 340000KHz or at least 40000KHz.
|
|
+ */
|
|
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_0 340000
|
|
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_1 400000
|
|
+
|
|
+/* Spec limits the slice width to 2720 pixels */
|
|
+#define MAX_HDMI_SLICE_WIDTH 2720
|
|
+ int kslice_adjust;
|
|
+ int adjusted_clk_khz;
|
|
+ int min_slices;
|
|
+ int target_slices;
|
|
+ int max_throughput; /* max clock freq. in khz per slice */
|
|
+ int max_slice_width;
|
|
+ int slice_width;
|
|
+ int pixel_clock = crtc_state->mode.clock;
|
|
+
|
|
+ if (!hdmi_throughput)
|
|
+ return 0;
|
|
+
|
|
+ /*
|
|
+ * Slice Width determination : HDMI2.1 Section 7.7.5.1
|
|
+ * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as
|
|
+ * for 4:4:4 is 1.0. Multiplying these factors by 10 and later
|
|
+ * dividing adjusted clock value by 10.
|
|
+ */
|
|
+ if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) ||
|
|
+ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format))
|
|
+ kslice_adjust = 10;
|
|
+ else
|
|
+ kslice_adjust = 5;
|
|
+
|
|
+ /*
|
|
+ * As per spec, the rate at which the source and the sink process
|
|
+ * the pixels per slice are at two levels: at least 340Mhz or 400Mhz.
|
|
+ * This depends upon the pixel clock rate and output formats
|
|
+ * (kslice adjust).
|
|
+ * If pixel clock * kslice adjust >= 2720MHz slices can be processed
|
|
+ * at max 340MHz, otherwise they can be processed at max 400MHz.
|
|
+ */
|
|
+
|
|
+ adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10);
|
|
+
|
|
+ if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE)
|
|
+ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0;
|
|
+ else
|
|
+ max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1;
|
|
+
|
|
+ /*
|
|
+ * Taking into account the sink's capability for maximum
|
|
+ * clock per slice (in MHz) as read from HF-VSDB.
|
|
+ */
|
|
+ max_throughput = min(max_throughput, hdmi_throughput * 1000);
|
|
+
|
|
+ min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput);
|
|
+ max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width);
|
|
+
|
|
+ /*
|
|
+ * Keep on increasing the num of slices/line, starting from min_slices
|
|
+ * per line till we get such a number, for which the slice_width is
|
|
+ * just less than max_slice_width. The slices/line selected should be
|
|
+ * less than or equal to the max horizontal slices that the combination
|
|
+ * of PCON encoder and HDMI decoder can support.
|
|
+ */
|
|
+ do {
|
|
+ if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1)
|
|
+ target_slices = 1;
|
|
+ else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2)
|
|
+ target_slices = 2;
|
|
+ else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4)
|
|
+ target_slices = 4;
|
|
+ else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8)
|
|
+ target_slices = 8;
|
|
+ else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12)
|
|
+ target_slices = 12;
|
|
+ else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16)
|
|
+ target_slices = 16;
|
|
+ else
|
|
+ return 0;
|
|
+
|
|
+ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices);
|
|
+ if (slice_width > max_slice_width)
|
|
+ min_slices = target_slices + 1;
|
|
+ } while (slice_width > max_slice_width);
|
|
+
|
|
+ return target_slices;
|
|
+}
|
|
+
|
|
+static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi,
|
|
+ struct drm_crtc_state *crtc_state)
|
|
+{
|
|
+ int hdmi_throughput = hdmi->dsc_cap.clk_per_slice;
|
|
+ int hdmi_max_slices = hdmi->dsc_cap.max_slices;
|
|
+ int rk_max_slices = 8;
|
|
+ int rk_max_slice_width = 2048;
|
|
+
|
|
+ return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices,
|
|
+ rk_max_slice_width,
|
|
+ hdmi_max_slices, hdmi_throughput);
|
|
+}
|
|
+
|
|
+static int
|
|
+hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp,
|
|
+ int slice_width, int num_slices, bool hdmi_all_bpp,
|
|
+ int hdmi_max_chunk_bytes)
|
|
+{
|
|
+ int max_dsc_bpp, min_dsc_bpp;
|
|
+ int target_bytes;
|
|
+ bool bpp_found = false;
|
|
+ int bpp_decrement_x16;
|
|
+ int bpp_target;
|
|
+ int bpp_target_x16;
|
|
+
|
|
+ /*
|
|
+ * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec
|
|
+ * Start with the max bpp and keep on decrementing with
|
|
+ * fractional bpp, if supported by PCON DSC encoder
|
|
+ *
|
|
+ * for each bpp we check if no of bytes can be supported by HDMI sink
|
|
+ */
|
|
+
|
|
+ /* only 9\10\12 bpp was tested */
|
|
+ min_dsc_bpp = 9;
|
|
+ max_dsc_bpp = 12;
|
|
+
|
|
+ /*
|
|
+ * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink
|
|
+ * Section 7.7.34 : Source shall not enable compressed Video
|
|
+ * Transport with bpp_target settings above 12 bpp unless
|
|
+ * DSC_all_bpp is set to 1.
|
|
+ */
|
|
+ if (!hdmi_all_bpp)
|
|
+ max_dsc_bpp = min(max_dsc_bpp, 12);
|
|
+
|
|
+ /*
|
|
+ * The Sink has a limit of compressed data in bytes for a scanline,
|
|
+ * as described in max_chunk_bytes field in HFVSDB block of edid.
|
|
+ * The no. of bytes depend on the target bits per pixel that the
|
|
+ * source configures. So we start with the max_bpp and calculate
|
|
+ * the target_chunk_bytes. We keep on decrementing the target_bpp,
|
|
+ * till we get the target_chunk_bytes just less than what the sink's
|
|
+ * max_chunk_bytes, or else till we reach the min_dsc_bpp.
|
|
+ *
|
|
+ * The decrement is according to the fractional support from PCON DSC
|
|
+ * encoder. For fractional BPP we use bpp_target as a multiple of 16.
|
|
+ *
|
|
+ * bpp_target_x16 = bpp_target * 16
|
|
+ * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps
|
|
+ * {1/16, 1/8, 1/4, 1/2, 1} respectively.
|
|
+ */
|
|
+
|
|
+ bpp_target = max_dsc_bpp;
|
|
+
|
|
+ /* src does not support fractional bpp implies decrement by 16 for bppx16 */
|
|
+ if (!src_fractional_bpp)
|
|
+ src_fractional_bpp = 1;
|
|
+ bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp);
|
|
+ bpp_target_x16 = bpp_target * 16;
|
|
+
|
|
+ while (bpp_target_x16 > (min_dsc_bpp * 16)) {
|
|
+ int bpp;
|
|
+
|
|
+ bpp = DIV_ROUND_UP(bpp_target_x16, 16);
|
|
+ target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8);
|
|
+ if (target_bytes <= hdmi_max_chunk_bytes) {
|
|
+ bpp_found = true;
|
|
+ break;
|
|
+ }
|
|
+ bpp_target_x16 -= bpp_decrement_x16;
|
|
+ }
|
|
+ if (bpp_found)
|
|
+ return bpp_target_x16;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi,
|
|
+ int num_slices, int slice_width)
|
|
+{
|
|
+ bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp;
|
|
+ int fractional_bpp = 0;
|
|
+ int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024;
|
|
+
|
|
+ return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width,
|
|
+ num_slices, hdmi_all_bpp,
|
|
+ hdmi_max_chunk_bytes);
|
|
+}
|
|
+
|
|
+static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi,
|
|
+ u16 pic_width, u16 pic_height,
|
|
+ u16 slice_width, u16 slice_height,
|
|
+ u16 bits_per_pixel, u8 bits_per_component)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < PPS_TABLE_LEN; i++)
|
|
+ if (pic_width == pps_datas[i].pic_width &&
|
|
+ pic_height == pps_datas[i].pic_height &&
|
|
+ slice_width == pps_datas[i].slice_width &&
|
|
+ slice_height == pps_datas[i].slice_height &&
|
|
+ bits_per_component == pps_datas[i].bpc &&
|
|
+ bits_per_pixel == pps_datas[i].bpp &&
|
|
+ hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb)
|
|
+ break;
|
|
+
|
|
+ if (i == PPS_TABLE_LEN) {
|
|
+ dev_err(hdmi->dev, "can't find pps cfg!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128);
|
|
+ hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) *
|
|
+ (pic_width / slice_width);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi,
|
|
+ struct rockchip_crtc_state *s,
|
|
+ struct drm_crtc_state *crtc_state)
|
|
+{
|
|
+ int ret;
|
|
+ int slice_height;
|
|
+ int slice_width;
|
|
+ int bits_per_pixel;
|
|
+ int slice_count;
|
|
+ bool hdmi_is_dsc_1_2;
|
|
+ unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format);
|
|
+
|
|
+ if (!crtc_state)
|
|
+ return;
|
|
+
|
|
+ hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2;
|
|
+
|
|
+ if (!hdmi_is_dsc_1_2)
|
|
+ return;
|
|
+
|
|
+ slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay);
|
|
+ if (!slice_height)
|
|
+ return;
|
|
+
|
|
+ slice_count = hdmi_dsc_slices(hdmi, crtc_state);
|
|
+ if (!slice_count)
|
|
+ return;
|
|
+
|
|
+ slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count);
|
|
+
|
|
+ bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width);
|
|
+ if (!bits_per_pixel)
|
|
+ return;
|
|
+
|
|
+ ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay,
|
|
+ crtc_state->mode.vdisplay, slice_width,
|
|
+ slice_height, bits_per_pixel, depth);
|
|
+
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "set vdsc cfg failed\n");
|
|
+ return;
|
|
+ }
|
|
+ dev_info(hdmi->dev, "dsc_enable\n");
|
|
+ s->dsc_enable = 1;
|
|
+ s->dsc_sink_cap.version_major = 1;
|
|
+ s->dsc_sink_cap.version_minor = 2;
|
|
+ s->dsc_sink_cap.slice_width = slice_width;
|
|
+ s->dsc_sink_cap.slice_height = slice_height;
|
|
+ s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel;
|
|
+ s->dsc_sink_cap.block_pred = 1;
|
|
+ s->dsc_sink_cap.native_420 = 0;
|
|
+
|
|
+ memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128);
|
|
+}
|
|
+/////////////////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+// static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi,
|
|
+// u32 *config,
|
|
+// int phy_table_size)
|
|
+// {
|
|
+// int i;
|
|
+//
|
|
+// if (phy_table_size > ARRAY_SIZE(rockchip_phy_config)) {
|
|
+// dev_err(hdmi->dev, "phy table array number is out of range\n");
|
|
+// return -E2BIG;
|
|
+// }
|
|
+//
|
|
+// for (i = 0; i < phy_table_size; i++) {
|
|
+// if (config[i * 4] != 0)
|
|
+// rockchip_phy_config[i].mpixelclock = (u64)config[i * 4];
|
|
+// else
|
|
+// rockchip_phy_config[i].mpixelclock = ~0UL;
|
|
+// rockchip_phy_config[i].sym_ctr = (u16)config[i * 4 + 1];
|
|
+// rockchip_phy_config[i].term = (u16)config[i * 4 + 2];
|
|
+// rockchip_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3];
|
|
+// }
|
|
+//
|
|
+// return 0;
|
|
+// }
|
|
+
|
|
+static void repo_hpd_event(struct work_struct *p_work)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work);
|
|
+ bool change;
|
|
+
|
|
+ change = drm_helper_hpd_irq_event(hdmi->drm_dev);
|
|
+ if (change) {
|
|
+ dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat);
|
|
+ // dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change);
|
|
+ }
|
|
+}
|
|
+
|
|
+static irqreturn_t rockchip_hdmi_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) {
|
|
+ dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat);
|
|
+
|
|
+ if (!hdmi->id)
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
|
|
+ RK3588_HDMI0_HPD_INT_MSK);
|
|
+ else
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK,
|
|
+ RK3588_HDMI1_HPD_INT_MSK);
|
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
|
+ return IRQ_WAKE_THREAD;
|
|
+ }
|
|
+
|
|
+ return IRQ_NONE;
|
|
+}
|
|
+
|
|
+static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = dev_id;
|
|
+ u32 intr_stat, val;
|
|
+ int msecs;
|
|
+ bool stat;
|
|
+
|
|
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
|
|
+
|
|
+ if (!intr_stat)
|
|
+ return IRQ_NONE;
|
|
+
|
|
+ if (!hdmi->id) {
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
|
|
+ RK3588_HDMI0_HPD_INT_CLR);
|
|
+ if (intr_stat & RK3588_HDMI0_LEVEL_INT)
|
|
+ stat = true;
|
|
+ else
|
|
+ stat = false;
|
|
+ } else {
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
|
|
+ RK3588_HDMI1_HPD_INT_CLR);
|
|
+ if (intr_stat & RK3588_HDMI1_LEVEL_INT)
|
|
+ stat = true;
|
|
+ else
|
|
+ stat = false;
|
|
+ }
|
|
+
|
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
|
+
|
|
+ if (stat) {
|
|
+ hdmi->hpd_stat = true;
|
|
+ msecs = 150;
|
|
+ } else {
|
|
+ hdmi->hpd_stat = false;
|
|
+ msecs = 20;
|
|
+ }
|
|
+ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
|
|
+
|
|
+ if (!hdmi->id) {
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
|
|
+ RK3588_HDMI0_HPD_INT_CLR) |
|
|
+ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
|
|
+ } else {
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
|
|
+ RK3588_HDMI1_HPD_INT_CLR) |
|
|
+ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
|
|
+ }
|
|
+
|
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static void init_hpd_work(struct rockchip_hdmi *hdmi)
|
|
+{
|
|
+ hdmi->workqueue = create_workqueue("hpd_queue");
|
|
+ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
|
|
+}
|
|
+
|
|
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)) {
|
|
@@ -217,6 +1174,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
|
return PTR_ERR(hdmi->regmap);
|
|
}
|
|
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
|
|
+ if (IS_ERR(hdmi->vo1_regmap)) {
|
|
+ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n");
|
|
+ return PTR_ERR(hdmi->vo1_regmap);
|
|
+ }
|
|
+ }
|
|
+
|
|
hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
|
|
if (!hdmi->ref_clk)
|
|
hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");
|
|
@@ -246,6 +1211,79 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
|
if (IS_ERR(hdmi->avdd_1v8))
|
|
return PTR_ERR(hdmi->avdd_1v8);
|
|
|
|
+ hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio");
|
|
+ if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) {
|
|
+ hdmi->hclk_vio = NULL;
|
|
+ } else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) {
|
|
+ return -EPROBE_DEFER;
|
|
+ } else if (IS_ERR(hdmi->hclk_vio)) {
|
|
+ dev_err(hdmi->dev, "failed to get hclk_vio clock\n");
|
|
+ return PTR_ERR(hdmi->hclk_vio);
|
|
+ }
|
|
+
|
|
+ hdmi->hclk_vop = devm_clk_get(hdmi->dev, "hclk");
|
|
+ if (PTR_ERR(hdmi->hclk_vop) == -ENOENT) {
|
|
+ hdmi->hclk_vop = NULL;
|
|
+ } else if (PTR_ERR(hdmi->hclk_vop) == -EPROBE_DEFER) {
|
|
+ return -EPROBE_DEFER;
|
|
+ } else if (IS_ERR(hdmi->hclk_vop)) {
|
|
+ dev_err(hdmi->dev, "failed to get hclk_vop clock\n");
|
|
+ return PTR_ERR(hdmi->hclk_vop);
|
|
+ }
|
|
+
|
|
+ hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud");
|
|
+ if (IS_ERR(hdmi->aud_clk)) {
|
|
+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk),
|
|
+ "failed to get aud_clk clock\n");
|
|
+ return PTR_ERR(hdmi->aud_clk);
|
|
+ }
|
|
+
|
|
+ hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd");
|
|
+ if (IS_ERR(hdmi->hpd_clk)) {
|
|
+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk),
|
|
+ "failed to get hpd_clk clock\n");
|
|
+ return PTR_ERR(hdmi->hpd_clk);
|
|
+ }
|
|
+
|
|
+ hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1");
|
|
+ if (IS_ERR(hdmi->hclk_vo1)) {
|
|
+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1),
|
|
+ "failed to get hclk_vo1 clock\n");
|
|
+ return PTR_ERR(hdmi->hclk_vo1);
|
|
+ }
|
|
+
|
|
+ hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc");
|
|
+ if (IS_ERR(hdmi->earc_clk)) {
|
|
+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk),
|
|
+ "failed to get earc_clk clock\n");
|
|
+ return PTR_ERR(hdmi->earc_clk);
|
|
+ }
|
|
+
|
|
+ hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref");
|
|
+ if (IS_ERR(hdmi->hdmitx_ref)) {
|
|
+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref),
|
|
+ "failed to get hdmitx_ref clock\n");
|
|
+ return PTR_ERR(hdmi->hdmitx_ref);
|
|
+ }
|
|
+
|
|
+ hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk");
|
|
+ if (IS_ERR(hdmi->pclk)) {
|
|
+ dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk),
|
|
+ "failed to get pclk clock\n");
|
|
+ return PTR_ERR(hdmi->pclk);
|
|
+ }
|
|
+
|
|
+ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
|
|
+ GPIOD_OUT_HIGH);
|
|
+ if (IS_ERR(hdmi->enable_gpio)) {
|
|
+ ret = PTR_ERR(hdmi->enable_gpio);
|
|
+ dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ hdmi->skip_check_420_mode =
|
|
+ of_property_read_bool(np, "skip-check-420-mode");
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -284,9 +1322,114 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
|
|
|
|
return MODE_BAD;
|
|
}
|
|
+/* [CC:] enable downstream mode_valid() */
|
|
+// static enum drm_mode_status
|
|
+// dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, void *data,
|
|
+// const struct drm_display_info *info,
|
|
+// const struct drm_display_mode *mode)
|
|
+// {
|
|
+// struct drm_encoder *encoder = connector->encoder;
|
|
+// enum drm_mode_status status = MODE_OK;
|
|
+// struct drm_device *dev = connector->dev;
|
|
+// struct rockchip_drm_private *priv = dev->dev_private;
|
|
+// struct drm_crtc *crtc;
|
|
+// struct rockchip_hdmi *hdmi;
|
|
+//
|
|
+// /*
|
|
+// * Pixel clocks we support are always < 2GHz and so fit in an
|
|
+// * int. We should make sure source rate does too so we don't get
|
|
+// * overflow when we multiply by 1000.
|
|
+// */
|
|
+// if (mode->clock > INT_MAX / 1000)
|
|
+// return MODE_BAD;
|
|
+//
|
|
+// if (!encoder) {
|
|
+// const struct drm_connector_helper_funcs *funcs;
|
|
+//
|
|
+// funcs = connector->helper_private;
|
|
+// if (funcs->atomic_best_encoder)
|
|
+// encoder = funcs->atomic_best_encoder(connector,
|
|
+// connector->state);
|
|
+// else
|
|
+// encoder = funcs->best_encoder(connector);
|
|
+// }
|
|
+//
|
|
+// if (!encoder || !encoder->possible_crtcs)
|
|
+// return MODE_BAD;
|
|
+//
|
|
+// hdmi = to_rockchip_hdmi(encoder);
|
|
+//
|
|
+// /*
|
|
+// * If sink max TMDS clock < 340MHz, we should check the mode pixel
|
|
+// * clock > 340MHz is YCbCr420 or not and whether the platform supports
|
|
+// * YCbCr420.
|
|
+// */
|
|
+// if (!hdmi->skip_check_420_mode) {
|
|
+// if (mode->clock > 340000 &&
|
|
+// connector->display_info.max_tmds_clock < 340000 &&
|
|
+// (!drm_mode_is_420(&connector->display_info, mode) ||
|
|
+// !connector->ycbcr_420_allowed))
|
|
+// return MODE_BAD;
|
|
+//
|
|
+// if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 &&
|
|
+// !drm_mode_is_420(&connector->display_info, mode))
|
|
+// return MODE_BAD;
|
|
+// };
|
|
+//
|
|
+// if (hdmi->phy) {
|
|
+// if (hdmi->is_hdmi_qp)
|
|
+// phy_set_bus_width(hdmi->phy, mode->clock * 10);
|
|
+// else
|
|
+// phy_set_bus_width(hdmi->phy, 8);
|
|
+// }
|
|
+//
|
|
+// /*
|
|
+// * ensure all drm display mode can work, if someone want support more
|
|
+// * resolutions, please limit the possible_crtc, only connect to
|
|
+// * needed crtc.
|
|
+// */
|
|
+// drm_for_each_crtc(crtc, connector->dev) {
|
|
+// int pipe = drm_crtc_index(crtc);
|
|
+// const struct rockchip_crtc_funcs *funcs =
|
|
+// priv->crtc_funcs[pipe];
|
|
+//
|
|
+// if (!(encoder->possible_crtcs & drm_crtc_mask(crtc)))
|
|
+// continue;
|
|
+// if (!funcs || !funcs->mode_valid)
|
|
+// continue;
|
|
+//
|
|
+// status = funcs->mode_valid(crtc, mode,
|
|
+// DRM_MODE_CONNECTOR_HDMIA);
|
|
+// if (status != MODE_OK)
|
|
+// return status;
|
|
+// }
|
|
+//
|
|
+// return status;
|
|
+// }
|
|
+//
|
|
|
|
static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
|
|
{
|
|
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
|
|
+
|
|
+ if (crtc->state->active_changed) {
|
|
+ if (hdmi->plat_data->split_mode) {
|
|
+ s->output_if &= ~(VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1);
|
|
+ } else {
|
|
+ if (!hdmi->id)
|
|
+ s->output_if &= ~VOP_OUTPUT_IF_HDMI1;
|
|
+ else
|
|
+ s->output_if &= ~VOP_OUTPUT_IF_HDMI0;
|
|
+ }
|
|
+ }
|
|
+ /*
|
|
+ * when plug out hdmi it will be switch cvbs and then phy bus width
|
|
+ * must be set as 8
|
|
+ */
|
|
+ if (hdmi->phy)
|
|
+ phy_set_bus_width(hdmi->phy, 8);
|
|
}
|
|
|
|
static bool
|
|
@@ -302,6 +1445,27 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
|
|
struct drm_display_mode *adj_mode)
|
|
{
|
|
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
|
+ struct drm_crtc *crtc;
|
|
+ struct rockchip_crtc_state *s;
|
|
+
|
|
+ if (!encoder->crtc)
|
|
+ return;
|
|
+ crtc = encoder->crtc;
|
|
+
|
|
+ if (!crtc->state)
|
|
+ return;
|
|
+ s = to_rockchip_crtc_state(crtc->state);
|
|
+
|
|
+ if (!s)
|
|
+ return;
|
|
+
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ s->dsc_enable = 0;
|
|
+ if (hdmi->link_cfg.dsc_mode)
|
|
+ dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state);
|
|
+
|
|
+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
|
|
+ }
|
|
|
|
clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
|
|
}
|
|
@@ -309,14 +1473,25 @@ 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 mux;
|
|
int ret;
|
|
|
|
+ if (WARN_ON(!crtc || !crtc->state))
|
|
+ return;
|
|
+
|
|
+ if (hdmi->phy)
|
|
+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
|
|
+
|
|
+ clk_set_rate(hdmi->ref_clk,
|
|
+ crtc->state->adjusted_mode.crtc_clock * 1000);
|
|
+
|
|
if (hdmi->chip_data->lcdsel_grf_reg < 0)
|
|
return;
|
|
|
|
- ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
|
|
- if (ret)
|
|
+ mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
|
|
+ if (mux)
|
|
val = hdmi->chip_data->lcdsel_lit;
|
|
else
|
|
val = hdmi->chip_data->lcdsel_big;
|
|
@@ -331,24 +1506,1018 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
|
if (ret != 0)
|
|
DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
|
|
|
|
+ if (hdmi->chip_data->lcdsel_grf_reg == RK3288_GRF_SOC_CON6) {
|
|
+ struct rockchip_crtc_state *s =
|
|
+ to_rockchip_crtc_state(crtc->state);
|
|
+ u32 mode_mask = mux ? RK3288_HDMI_LCDC1_YUV420 :
|
|
+ RK3288_HDMI_LCDC0_YUV420;
|
|
+
|
|
+ if (s->output_mode == ROCKCHIP_OUT_MODE_YUV420)
|
|
+ val = HIWORD_UPDATE(mode_mask, mode_mask);
|
|
+ else
|
|
+ val = HIWORD_UPDATE(0, mode_mask);
|
|
+
|
|
+ regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON16, val);
|
|
+ }
|
|
+
|
|
clk_disable_unprepare(hdmi->grf_clk);
|
|
DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
|
|
ret ? "LIT" : "BIG");
|
|
}
|
|
|
|
-static int
|
|
-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
|
|
- struct drm_crtc_state *crtc_state,
|
|
- struct drm_connector_state *conn_state)
|
|
+static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi)
|
|
{
|
|
- struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
|
+ int val;
|
|
+ bool is_hdmi0;
|
|
|
|
- s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
|
|
- s->output_type = DRM_MODE_CONNECTOR_HDMIA;
|
|
+ if (!hdmi->id)
|
|
+ is_hdmi0 = true;
|
|
+ else
|
|
+ is_hdmi0 = false;
|
|
+
|
|
+ if (!hdmi->link_cfg.frl_mode) {
|
|
+ val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK);
|
|
+ if (is_hdmi0)
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
|
|
+ else
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
|
|
+
|
|
+ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
|
|
+ if (is_hdmi0)
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
|
|
+ else
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK);
|
|
+ if (is_hdmi0)
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
|
|
+ else
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
|
|
+
|
|
+ if (hdmi->link_cfg.dsc_mode) {
|
|
+ val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA,
|
|
+ RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
|
|
+ if (is_hdmi0)
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
|
|
+ else
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
|
|
+ } else {
|
|
+ val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
|
|
+ if (is_hdmi0)
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
|
|
+ else
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format,
|
|
+ u32 depth)
|
|
+{
|
|
+ u32 val = 0;
|
|
+
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_RGB888_1X24:
|
|
+ case MEDIA_BUS_FMT_RGB101010_1X30:
|
|
+ val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK);
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK);
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_YUV8_1X24:
|
|
+ case MEDIA_BUS_FMT_YUV10_1X30:
|
|
+ val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK);
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(hdmi->dev, "can't set correct color format\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (hdmi->link_cfg.dsc_mode)
|
|
+ val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK);
|
|
+
|
|
+ if (depth == 8)
|
|
+ val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK);
|
|
+ else
|
|
+ val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK);
|
|
+
|
|
+ if (!hdmi->id)
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
|
|
+ else
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
|
|
+}
|
|
+
|
|
+static void rk3588_set_grf_cfg(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ int color_depth;
|
|
+
|
|
+ rk3588_set_link_mode(hdmi);
|
|
+ color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format);
|
|
+ rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth);
|
|
+}
|
|
+
|
|
+static void
|
|
+dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct rockchip_hdmi *hdmi,
|
|
+ unsigned int *color_format,
|
|
+ unsigned int *output_mode,
|
|
+ unsigned long *bus_format,
|
|
+ unsigned int *bus_width,
|
|
+ unsigned long *enc_out_encoding,
|
|
+ unsigned int *eotf)
|
|
+{
|
|
+ struct drm_display_info *info = &conn_state->connector->display_info;
|
|
+ struct drm_display_mode mode;
|
|
+ struct hdr_output_metadata *hdr_metadata;
|
|
+ u32 vic;
|
|
+ unsigned long tmdsclock, pixclock;
|
|
+ unsigned int color_depth;
|
|
+ bool support_dc = false;
|
|
+ bool sink_is_hdmi = true;
|
|
+ u32 max_tmds_clock = info->max_tmds_clock;
|
|
+ int output_eotf;
|
|
+
|
|
+ drm_mode_copy(&mode, &crtc_state->mode);
|
|
+ pixclock = mode.crtc_clock;
|
|
+ if (hdmi->plat_data->split_mode) {
|
|
+ drm_mode_convert_to_origin_mode(&mode);
|
|
+ pixclock /= 2;
|
|
+ }
|
|
+
|
|
+ vic = drm_match_cea_mode(&mode);
|
|
+
|
|
+ if (!hdmi->is_hdmi_qp)
|
|
+ sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi);
|
|
+
|
|
+ *color_format = RK_IF_FORMAT_RGB;
|
|
+
|
|
+ switch (hdmi->hdmi_output) {
|
|
+ case RK_IF_FORMAT_YCBCR_HQ:
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR444;
|
|
+ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR422;
|
|
+ else if (conn_state->connector->ycbcr_420_allowed &&
|
|
+ drm_mode_is_420(info, &mode) &&
|
|
+ (pixclock >= 594000 && !hdmi->is_hdmi_qp))
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ break;
|
|
+ case RK_IF_FORMAT_YCBCR_LQ:
|
|
+ if (conn_state->connector->ycbcr_420_allowed &&
|
|
+ drm_mode_is_420(info, &mode) && pixclock >= 594000)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR422;
|
|
+ else if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR444;
|
|
+ break;
|
|
+ case RK_IF_FORMAT_YCBCR420:
|
|
+ if (conn_state->connector->ycbcr_420_allowed &&
|
|
+ drm_mode_is_420(info, &mode) && pixclock >= 594000)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ break;
|
|
+ case RK_IF_FORMAT_YCBCR422:
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR422;
|
|
+ break;
|
|
+ case RK_IF_FORMAT_YCBCR444:
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR444;
|
|
+ break;
|
|
+ case RK_IF_FORMAT_RGB:
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (*color_format == RK_IF_FORMAT_RGB &&
|
|
+ info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)
|
|
+ support_dc = true;
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR444 &&
|
|
+ info->edid_hdmi_rgb444_dc_modes &
|
|
+ (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30))
|
|
+ support_dc = true;
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR422)
|
|
+ support_dc = true;
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR420 &&
|
|
+ info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
|
|
+ support_dc = true;
|
|
+
|
|
+ if (hdmi->colordepth > 8 && support_dc)
|
|
+ color_depth = 10;
|
|
+ else
|
|
+ color_depth = 8;
|
|
+
|
|
+ if (!sink_is_hdmi) {
|
|
+ *color_format = RK_IF_FORMAT_RGB;
|
|
+ color_depth = 8;
|
|
+ }
|
|
+
|
|
+ *eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
|
|
+ if (conn_state->hdr_output_metadata) {
|
|
+ hdr_metadata = (struct hdr_output_metadata *)
|
|
+ conn_state->hdr_output_metadata->data;
|
|
+ output_eotf = hdr_metadata->hdmi_metadata_type1.eotf;
|
|
+ if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR &&
|
|
+ output_eotf <= HDMI_EOTF_BT_2100_HLG)
|
|
+ *eotf = output_eotf;
|
|
+ }
|
|
+
|
|
+ hdmi->colorimetry = conn_state->colorspace;
|
|
+
|
|
+ if ((*eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR &&
|
|
+ conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf &
|
|
+ BIT(*eotf)) || ((hdmi->colorimetry >= DRM_MODE_COLORIMETRY_BT2020_CYCC) &&
|
|
+ (hdmi->colorimetry <= DRM_MODE_COLORIMETRY_BT2020_YCC)))
|
|
+ *enc_out_encoding = V4L2_YCBCR_ENC_BT2020;
|
|
+ else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
|
|
+ (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18))
|
|
+ *enc_out_encoding = V4L2_YCBCR_ENC_601;
|
|
+ else
|
|
+ *enc_out_encoding = V4L2_YCBCR_ENC_709;
|
|
+
|
|
+ if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
|
|
+ /* BT2020 require color depth at lest 10bit */
|
|
+ color_depth = 10;
|
|
+ /* We prefer use YCbCr422 to send 10bit */
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR422;
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR420) {
|
|
+ if (mode.clock >= 340000)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ else
|
|
+ *color_format = RK_IF_FORMAT_RGB;
|
|
+ } else {
|
|
+ *color_format = RK_IF_FORMAT_RGB;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (mode.flags & DRM_MODE_FLAG_DBLCLK)
|
|
+ pixclock *= 2;
|
|
+ if ((mode.flags & DRM_MODE_FLAG_3D_MASK) ==
|
|
+ DRM_MODE_FLAG_3D_FRAME_PACKING)
|
|
+ pixclock *= 2;
|
|
+
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8)
|
|
+ tmdsclock = pixclock;
|
|
+ else
|
|
+ tmdsclock = pixclock * (color_depth) / 8;
|
|
+
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR420)
|
|
+ tmdsclock /= 2;
|
|
+
|
|
+ /* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */
|
|
+ if (!max_tmds_clock)
|
|
+ max_tmds_clock = 340000;
|
|
+
|
|
+ max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk);
|
|
+
|
|
+ if ((tmdsclock > max_tmds_clock) && !hdmi->is_hdmi_qp) {
|
|
+ if (max_tmds_clock >= 594000) {
|
|
+ color_depth = 8;
|
|
+ } else if (max_tmds_clock > 340000) {
|
|
+ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ } else {
|
|
+ color_depth = 8;
|
|
+ if (drm_mode_is_420(info, &mode) || tmdsclock >= 594000)
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ if (mode.clock >= 340000) {
|
|
+ if (drm_mode_is_420(info, &mode))
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ else
|
|
+ *color_format = RK_IF_FORMAT_RGB;
|
|
+ } else if (tmdsclock > max_tmds_clock) {
|
|
+ color_depth = 8;
|
|
+ if (drm_mode_is_420(info, &mode))
|
|
+ *color_format = RK_IF_FORMAT_YCBCR420;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR420) {
|
|
+ *output_mode = ROCKCHIP_OUT_MODE_YUV420;
|
|
+ if (color_depth > 8)
|
|
+ *bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
|
|
+ else
|
|
+ *bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
|
|
+ *bus_width = color_depth / 2;
|
|
+ } else {
|
|
+ *output_mode = ROCKCHIP_OUT_MODE_AAAA;
|
|
+ if (color_depth > 8) {
|
|
+ if (*color_format != RK_IF_FORMAT_RGB &&
|
|
+ !hdmi->unsupported_yuv_input)
|
|
+ *bus_format = MEDIA_BUS_FMT_YUV10_1X30;
|
|
+ else
|
|
+ *bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
|
|
+ } else {
|
|
+ if (*color_format != RK_IF_FORMAT_RGB &&
|
|
+ !hdmi->unsupported_yuv_input)
|
|
+ *bus_format = MEDIA_BUS_FMT_YUV8_1X24;
|
|
+ else
|
|
+ *bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
|
+ }
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR422)
|
|
+ *bus_width = 8;
|
|
+ else
|
|
+ *bus_width = color_depth;
|
|
+ }
|
|
+
|
|
+ hdmi->bus_format = *bus_format;
|
|
+
|
|
+ if (*color_format == RK_IF_FORMAT_YCBCR422) {
|
|
+ if (color_depth == 12)
|
|
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
|
|
+ else if (color_depth == 10)
|
|
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
|
|
+ else
|
|
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
|
|
+ } else {
|
|
+ hdmi->output_bus_format = *bus_format;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool
|
|
+dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state,
|
|
+ struct rockchip_hdmi *hdmi)
|
|
+{
|
|
+ struct drm_crtc_state *crtc_state = conn_state->crtc->state;
|
|
+ unsigned int colorformat;
|
|
+ unsigned long bus_format;
|
|
+ unsigned long output_bus_format = hdmi->output_bus_format;
|
|
+ unsigned long enc_out_encoding = hdmi->enc_out_encoding;
|
|
+ unsigned int eotf, bus_width;
|
|
+ unsigned int output_mode;
|
|
+
|
|
+ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
|
|
+ &colorformat,
|
|
+ &output_mode, &bus_format, &bus_width,
|
|
+ &hdmi->enc_out_encoding, &eotf);
|
|
+
|
|
+ if (output_bus_format != hdmi->output_bus_format ||
|
|
+ enc_out_encoding != hdmi->enc_out_encoding)
|
|
+ return true;
|
|
+ else
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct drm_connector_state *conn_state)
|
|
+{
|
|
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
|
|
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
|
+ unsigned int colorformat, bus_width, tmdsclk;
|
|
+ struct drm_display_mode mode;
|
|
+ unsigned int output_mode;
|
|
+ unsigned long bus_format;
|
|
+ int color_depth;
|
|
+ bool secondary = false;
|
|
+
|
|
+ /*
|
|
+ * There are two hdmi but only one encoder in split mode,
|
|
+ * so we need to check twice.
|
|
+ */
|
|
+secondary:
|
|
+ drm_mode_copy(&mode, &crtc_state->mode);
|
|
+
|
|
+ hdmi->vp_id = s->vp_id;
|
|
+ if (hdmi->plat_data->split_mode)
|
|
+ drm_mode_convert_to_origin_mode(&mode);
|
|
+
|
|
+ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
|
|
+ &colorformat,
|
|
+ &output_mode, &bus_format, &bus_width,
|
|
+ &hdmi->enc_out_encoding, &s->eotf);
|
|
+
|
|
+ s->bus_format = bus_format;
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ color_depth = hdmi_bus_fmt_color_depth(bus_format);
|
|
+ tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock);
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
|
|
+ tmdsclk /= 2;
|
|
+ hdmi_select_link_config(hdmi, crtc_state, tmdsclk);
|
|
+
|
|
+ if (hdmi->link_cfg.frl_mode) {
|
|
+ gpiod_set_value(hdmi->enable_gpio, 0);
|
|
+ /* in the current version, support max 40G frl */
|
|
+ if (hdmi->link_cfg.rate_per_lane >= 10) {
|
|
+ hdmi->link_cfg.frl_lanes = 4;
|
|
+ hdmi->link_cfg.rate_per_lane = 10;
|
|
+ }
|
|
+ bus_width = hdmi->link_cfg.frl_lanes *
|
|
+ hdmi->link_cfg.rate_per_lane * 1000000;
|
|
+ /* 10 bit color depth and frl mode */
|
|
+ if (color_depth == 10)
|
|
+ bus_width |=
|
|
+ COLOR_DEPTH_10BIT | HDMI_FRL_MODE;
|
|
+ else
|
|
+ bus_width |= HDMI_FRL_MODE;
|
|
+ } else {
|
|
+ gpiod_set_value(hdmi->enable_gpio, 1);
|
|
+ bus_width = hdmi_get_tmdsclock(hdmi, mode.clock * 10);
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
|
|
+ bus_width /= 2;
|
|
+
|
|
+ if (color_depth == 10)
|
|
+ bus_width |= COLOR_DEPTH_10BIT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ hdmi->phy_bus_width = bus_width;
|
|
+
|
|
+ if (hdmi->phy)
|
|
+ phy_set_bus_width(hdmi->phy, bus_width);
|
|
+
|
|
+ s->output_type = DRM_MODE_CONNECTOR_HDMIA;
|
|
+ s->tv_state = &conn_state->tv;
|
|
+
|
|
+ if (hdmi->plat_data->split_mode) {
|
|
+ s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
|
|
+ if (hdmi->plat_data->right && hdmi->id)
|
|
+ s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
|
|
+ s->output_if |= VOP_OUTPUT_IF_HDMI0 | VOP_OUTPUT_IF_HDMI1;
|
|
+ } else {
|
|
+ if (!hdmi->id)
|
|
+ s->output_if |= VOP_OUTPUT_IF_HDMI0;
|
|
+ else
|
|
+ s->output_if |= VOP_OUTPUT_IF_HDMI1;
|
|
+ }
|
|
+
|
|
+ s->output_mode = output_mode;
|
|
+ hdmi->bus_format = s->bus_format;
|
|
+
|
|
+ if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
|
|
+ s->color_space = V4L2_COLORSPACE_BT2020;
|
|
+ else if (colorformat == RK_IF_FORMAT_RGB)
|
|
+ s->color_space = V4L2_COLORSPACE_DEFAULT;
|
|
+ else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709)
|
|
+ s->color_space = V4L2_COLORSPACE_REC709;
|
|
+ else
|
|
+ s->color_space = V4L2_COLORSPACE_SMPTE170M;
|
|
+
|
|
+ if (hdmi->plat_data->split_mode && !secondary) {
|
|
+ hdmi = rockchip_hdmi_find_by_id(hdmi->dev->driver, !hdmi->id);
|
|
+ secondary = true;
|
|
+ goto secondary;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+dw_hdmi_rockchip_get_input_bus_format(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->bus_format;
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+dw_hdmi_rockchip_get_output_bus_format(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->output_bus_format;
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+dw_hdmi_rockchip_get_enc_in_encoding(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->enc_out_encoding;
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+dw_hdmi_rockchip_get_enc_out_encoding(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->enc_out_encoding;
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+dw_hdmi_rockchip_get_quant_range(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->hdmi_quant_range;
|
|
+}
|
|
+
|
|
+static struct drm_property *
|
|
+dw_hdmi_rockchip_get_hdr_property(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->hdr_panel_metadata_property;
|
|
+}
|
|
+
|
|
+static struct drm_property_blob *
|
|
+dw_hdmi_rockchip_get_hdr_blob(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return hdmi->hdr_panel_blob_ptr;
|
|
+}
|
|
+
|
|
+static bool
|
|
+dw_hdmi_rockchip_get_color_changed(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ bool ret = false;
|
|
+
|
|
+ if (hdmi->color_changed)
|
|
+ ret = true;
|
|
+ hdmi->color_changed = 0;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_rockchip_get_yuv422_format(struct drm_connector *connector,
|
|
+ struct edid *edid)
|
|
+{
|
|
+ if (!connector || !edid)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return rockchip_drm_get_yuv422_format(connector, edid);
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ if (!edid)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap,
|
|
+ &hdmi->max_frl_rate_per_lane,
|
|
+ &hdmi->max_lanes, edid);
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ int ret;
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data;
|
|
+ size_t size = sizeof(*sink_data);
|
|
+ struct drm_property *property = hdmi->next_hdr_sink_data_property;
|
|
+ struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr;
|
|
+
|
|
+ if (!edid)
|
|
+ return -EINVAL;
|
|
+
|
|
+ rockchip_drm_parse_next_hdr(sink_data, edid);
|
|
+
|
|
+ ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data,
|
|
+ &connector->base, property);
|
|
+
|
|
+ return ret;
|
|
+};
|
|
+
|
|
+static
|
|
+struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ return &hdmi->link_cfg;
|
|
+}
|
|
+
|
|
+static int dw_hdmi_dclk_set(void *data, bool enable)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ char clk_name[16];
|
|
+ struct clk *dclk;
|
|
+ int ret;
|
|
+
|
|
+ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", hdmi->vp_id);
|
|
+
|
|
+ dclk = devm_clk_get(hdmi->dev, clk_name);
|
|
+ if (IS_ERR(dclk)) {
|
|
+ DRM_DEV_ERROR(hdmi->dev, "failed to get %s\n", clk_name);
|
|
+ return PTR_ERR(dclk);
|
|
+ }
|
|
+
|
|
+ if (enable) {
|
|
+ ret = clk_prepare_enable(dclk);
|
|
+ if (ret < 0)
|
|
+ DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n",
|
|
+ hdmi->vp_id, ret);
|
|
+ } else {
|
|
+ clk_disable_unprepare(dclk);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
|
|
+static const struct drm_prop_enum_list color_depth_enum_list[] = {
|
|
+ { 0, "Automatic" }, /* Prefer highest color depth */
|
|
+ { 8, "24bit" },
|
|
+ { 10, "30bit" },
|
|
+};
|
|
+
|
|
+static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = {
|
|
+ { RK_IF_FORMAT_RGB, "rgb" },
|
|
+ { RK_IF_FORMAT_YCBCR444, "ycbcr444" },
|
|
+ { RK_IF_FORMAT_YCBCR422, "ycbcr422" },
|
|
+ { RK_IF_FORMAT_YCBCR420, "ycbcr420" },
|
|
+ { RK_IF_FORMAT_YCBCR_HQ, "ycbcr_high_subsampling" },
|
|
+ { RK_IF_FORMAT_YCBCR_LQ, "ycbcr_low_subsampling" },
|
|
+ { RK_IF_FORMAT_MAX, "invalid_output" },
|
|
+};
|
|
+
|
|
+static const struct drm_prop_enum_list quant_range_enum_list[] = {
|
|
+ { HDMI_QUANTIZATION_RANGE_DEFAULT, "default" },
|
|
+ { HDMI_QUANTIZATION_RANGE_LIMITED, "limit" },
|
|
+ { HDMI_QUANTIZATION_RANGE_FULL, "full" },
|
|
+};
|
|
+
|
|
+static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = {
|
|
+ { 0, "auto" },
|
|
+ { 1, "force_hdmi" },
|
|
+ { 2, "force_dvi" },
|
|
+};
|
|
+
|
|
+static const struct drm_prop_enum_list output_type_cap_list[] = {
|
|
+ { 0, "DVI" },
|
|
+ { 1, "HDMI" },
|
|
+};
|
|
+
|
|
+static void
|
|
+dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
|
|
+ unsigned int color, int version,
|
|
+ void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ struct drm_property *prop;
|
|
+ struct rockchip_drm_private *private = connector->dev->dev_private;
|
|
+
|
|
+ switch (color) {
|
|
+ case MEDIA_BUS_FMT_RGB101010_1X30:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_RGB;
|
|
+ hdmi->colordepth = 10;
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_YUV8_1X24:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444;
|
|
+ hdmi->colordepth = 8;
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_YUV10_1X30:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR444;
|
|
+ hdmi->colordepth = 10;
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_UYVY10_1X20:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422;
|
|
+ hdmi->colordepth = 10;
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_UYVY8_1X16:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR422;
|
|
+ hdmi->colordepth = 8;
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420;
|
|
+ hdmi->colordepth = 8;
|
|
+ break;
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_YCBCR420;
|
|
+ hdmi->colordepth = 10;
|
|
+ break;
|
|
+ default:
|
|
+ hdmi->hdmi_output = RK_IF_FORMAT_RGB;
|
|
+ hdmi->colordepth = 8;
|
|
+ }
|
|
+
|
|
+ hdmi->bus_format = color;
|
|
+
|
|
+ if (hdmi->hdmi_output == RK_IF_FORMAT_YCBCR422) {
|
|
+ if (hdmi->colordepth == 12)
|
|
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
|
|
+ else if (hdmi->colordepth == 10)
|
|
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
|
|
+ else
|
|
+ hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
|
|
+ } else {
|
|
+ hdmi->output_bus_format = hdmi->bus_format;
|
|
+ }
|
|
+
|
|
+ /* RK3368 does not support deep color mode */
|
|
+ if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) {
|
|
+ prop = drm_property_create_enum(connector->dev, 0,
|
|
+ RK_IF_PROP_COLOR_DEPTH,
|
|
+ color_depth_enum_list,
|
|
+ ARRAY_SIZE(color_depth_enum_list));
|
|
+ if (prop) {
|
|
+ hdmi->color_depth_property = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create_enum(connector->dev, 0, RK_IF_PROP_COLOR_FORMAT,
|
|
+ drm_hdmi_output_enum_list,
|
|
+ ARRAY_SIZE(drm_hdmi_output_enum_list));
|
|
+ if (prop) {
|
|
+ hdmi->hdmi_output_property = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create_range(connector->dev, 0,
|
|
+ RK_IF_PROP_COLOR_DEPTH_CAPS,
|
|
+ 0, 0xff);
|
|
+ if (prop) {
|
|
+ hdmi->colordepth_capacity = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create_range(connector->dev, 0,
|
|
+ RK_IF_PROP_COLOR_FORMAT_CAPS,
|
|
+ 0, 0xf);
|
|
+ if (prop) {
|
|
+ hdmi->outputmode_capacity = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create(connector->dev,
|
|
+ DRM_MODE_PROP_BLOB |
|
|
+ DRM_MODE_PROP_IMMUTABLE,
|
|
+ "HDR_PANEL_METADATA", 0);
|
|
+ if (prop) {
|
|
+ hdmi->hdr_panel_metadata_property = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create(connector->dev,
|
|
+ DRM_MODE_PROP_BLOB |
|
|
+ DRM_MODE_PROP_IMMUTABLE,
|
|
+ "NEXT_HDR_SINK_DATA", 0);
|
|
+ if (prop) {
|
|
+ hdmi->next_hdr_sink_data_property = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create_bool(connector->dev, DRM_MODE_PROP_IMMUTABLE,
|
|
+ "USER_SPLIT_MODE");
|
|
+ if (prop) {
|
|
+ hdmi->user_split_mode_prop = prop;
|
|
+ drm_object_attach_property(&connector->base, prop,
|
|
+ hdmi->user_split_mode ? 1 : 0);
|
|
+ }
|
|
+
|
|
+ if (!hdmi->is_hdmi_qp) {
|
|
+ prop = drm_property_create_enum(connector->dev, 0,
|
|
+ "output_hdmi_dvi",
|
|
+ output_hdmi_dvi_enum_list,
|
|
+ ARRAY_SIZE(output_hdmi_dvi_enum_list));
|
|
+ if (prop) {
|
|
+ hdmi->output_hdmi_dvi = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create_enum(connector->dev, 0,
|
|
+ "output_type_capacity",
|
|
+ output_type_cap_list,
|
|
+ ARRAY_SIZE(output_type_cap_list));
|
|
+ if (prop) {
|
|
+ hdmi->output_type_capacity = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+
|
|
+ prop = drm_property_create_enum(connector->dev, 0,
|
|
+ "hdmi_quant_range",
|
|
+ quant_range_enum_list,
|
|
+ ARRAY_SIZE(quant_range_enum_list));
|
|
+ if (prop) {
|
|
+ hdmi->quant_range = prop;
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ prop = connector->dev->mode_config.hdr_output_metadata_property;
|
|
+ if (version >= 0x211a || hdmi->is_hdmi_qp)
|
|
+ drm_object_attach_property(&connector->base, prop, 0);
|
|
+
|
|
+ if (!drm_mode_create_hdmi_colorspace_property(connector, 0))
|
|
+ drm_object_attach_property(&connector->base,
|
|
+ connector->colorspace_property, 0);
|
|
+
|
|
+ // [CC:] if this is not needed, also drop connector_id_prop
|
|
+ if (!private->connector_id_prop)
|
|
+ private->connector_id_prop = drm_property_create_range(connector->dev,
|
|
+ DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE,
|
|
+ "CONNECTOR_ID", 0, 0xf);
|
|
+ if (private->connector_id_prop)
|
|
+ drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id);
|
|
+}
|
|
+
|
|
+static void
|
|
+dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector,
|
|
+ void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ if (hdmi->color_depth_property) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->color_depth_property);
|
|
+ hdmi->color_depth_property = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->hdmi_output_property) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->hdmi_output_property);
|
|
+ hdmi->hdmi_output_property = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->colordepth_capacity) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->colordepth_capacity);
|
|
+ hdmi->colordepth_capacity = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->outputmode_capacity) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->outputmode_capacity);
|
|
+ hdmi->outputmode_capacity = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->quant_range) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->quant_range);
|
|
+ hdmi->quant_range = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->hdr_panel_metadata_property) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->hdr_panel_metadata_property);
|
|
+ hdmi->hdr_panel_metadata_property = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->next_hdr_sink_data_property) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->next_hdr_sink_data_property);
|
|
+ hdmi->next_hdr_sink_data_property = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->output_hdmi_dvi) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->output_hdmi_dvi);
|
|
+ hdmi->output_hdmi_dvi = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->output_type_capacity) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->output_type_capacity);
|
|
+ hdmi->output_type_capacity = NULL;
|
|
+ }
|
|
+
|
|
+ if (hdmi->user_split_mode_prop) {
|
|
+ drm_property_destroy(connector->dev,
|
|
+ hdmi->user_split_mode_prop);
|
|
+ hdmi->user_split_mode_prop = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_rockchip_set_property(struct drm_connector *connector,
|
|
+ struct drm_connector_state *state,
|
|
+ struct drm_property *property,
|
|
+ u64 val,
|
|
+ void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ struct drm_mode_config *config = &connector->dev->mode_config;
|
|
+
|
|
+ if (property == hdmi->color_depth_property) {
|
|
+ hdmi->colordepth = val;
|
|
+ /* If hdmi is disconnected, state->crtc is null */
|
|
+ if (!state->crtc)
|
|
+ return 0;
|
|
+ if (dw_hdmi_rockchip_check_color(state, hdmi))
|
|
+ hdmi->color_changed++;
|
|
+ return 0;
|
|
+ } else if (property == hdmi->hdmi_output_property) {
|
|
+ hdmi->hdmi_output = val;
|
|
+ if (!state->crtc)
|
|
+ return 0;
|
|
+ if (dw_hdmi_rockchip_check_color(state, hdmi))
|
|
+ hdmi->color_changed++;
|
|
+ return 0;
|
|
+ } else if (property == hdmi->quant_range) {
|
|
+ u64 quant_range = hdmi->hdmi_quant_range;
|
|
+
|
|
+ hdmi->hdmi_quant_range = val;
|
|
+ if (quant_range != hdmi->hdmi_quant_range)
|
|
+ dw_hdmi_set_quant_range(hdmi->hdmi);
|
|
+ return 0;
|
|
+ } else if (property == config->hdr_output_metadata_property) {
|
|
+ return 0;
|
|
+ } else if (property == hdmi->output_hdmi_dvi) {
|
|
+ if (hdmi->force_output != val)
|
|
+ hdmi->color_changed++;
|
|
+ hdmi->force_output = val;
|
|
+ dw_hdmi_set_output_type(hdmi->hdmi, val);
|
|
+ return 0;
|
|
+ } else if (property == hdmi->colordepth_capacity) {
|
|
+ return 0;
|
|
+ } else if (property == hdmi->outputmode_capacity) {
|
|
+ return 0;
|
|
+ } else if (property == hdmi->output_type_capacity) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ DRM_ERROR("Unknown property [PROP:%d:%s]\n",
|
|
+ property->base.id, property->name);
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int
|
|
+dw_hdmi_rockchip_get_property(struct drm_connector *connector,
|
|
+ const struct drm_connector_state *state,
|
|
+ struct drm_property *property,
|
|
+ u64 *val,
|
|
+ void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ struct drm_display_info *info = &connector->display_info;
|
|
+ struct drm_mode_config *config = &connector->dev->mode_config;
|
|
+
|
|
+ if (property == hdmi->color_depth_property) {
|
|
+ *val = hdmi->colordepth;
|
|
+ return 0;
|
|
+ } else if (property == hdmi->hdmi_output_property) {
|
|
+ *val = hdmi->hdmi_output;
|
|
+ return 0;
|
|
+ } else if (property == hdmi->colordepth_capacity) {
|
|
+ *val = BIT(RK_IF_DEPTH_8);
|
|
+ /* RK3368 only support 8bit */
|
|
+ if (hdmi->unsupported_deep_color)
|
|
+ return 0;
|
|
+ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)
|
|
+ *val |= BIT(RK_IF_DEPTH_10);
|
|
+ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)
|
|
+ *val |= BIT(RK_IF_DEPTH_12);
|
|
+ if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_48)
|
|
+ *val |= BIT(RK_IF_DEPTH_16);
|
|
+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
|
|
+ *val |= BIT(RK_IF_DEPTH_420_10);
|
|
+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
|
|
+ *val |= BIT(RK_IF_DEPTH_420_12);
|
|
+ if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
|
|
+ *val |= BIT(RK_IF_DEPTH_420_16);
|
|
+ return 0;
|
|
+ } else if (property == hdmi->outputmode_capacity) {
|
|
+ *val = BIT(RK_IF_FORMAT_RGB);
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444)
|
|
+ *val |= BIT(RK_IF_FORMAT_YCBCR444);
|
|
+ if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
|
|
+ *val |= BIT(RK_IF_FORMAT_YCBCR422);
|
|
+ if (connector->ycbcr_420_allowed &&
|
|
+ info->color_formats & DRM_COLOR_FORMAT_YCBCR420)
|
|
+ *val |= BIT(RK_IF_FORMAT_YCBCR420);
|
|
+ return 0;
|
|
+ } else if (property == hdmi->quant_range) {
|
|
+ *val = hdmi->hdmi_quant_range;
|
|
+ return 0;
|
|
+ } else if (property == config->hdr_output_metadata_property) {
|
|
+ *val = state->hdr_output_metadata ?
|
|
+ state->hdr_output_metadata->base.id : 0;
|
|
+ return 0;
|
|
+ } else if (property == hdmi->output_hdmi_dvi) {
|
|
+ *val = hdmi->force_output;
|
|
+ return 0;
|
|
+ } else if (property == hdmi->output_type_capacity) {
|
|
+ *val = dw_hdmi_get_output_type_cap(hdmi->hdmi);
|
|
+ return 0;
|
|
+ } else if (property == hdmi->user_split_mode_prop) {
|
|
+ *val = hdmi->user_split_mode;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ DRM_ERROR("Unknown property [PROP:%d:%s]\n",
|
|
+ property->base.id, property->name);
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = {
|
|
+ .attach_properties = dw_hdmi_rockchip_attach_properties,
|
|
+ .destroy_properties = dw_hdmi_rockchip_destroy_properties,
|
|
+ .set_property = dw_hdmi_rockchip_set_property,
|
|
+ .get_property = dw_hdmi_rockchip_get_property,
|
|
+};
|
|
+
|
|
static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
|
|
.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
|
|
.mode_set = dw_hdmi_rockchip_encoder_mode_set,
|
|
@@ -357,20 +2526,24 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
|
|
.atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
|
|
};
|
|
|
|
-static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
|
|
- const struct drm_display_info *display,
|
|
- const struct drm_display_mode *mode)
|
|
+static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
|
|
- return phy_power_on(hdmi->phy);
|
|
+ while (hdmi->phy->power_count > 0)
|
|
+ phy_power_off(hdmi->phy);
|
|
}
|
|
|
|
-static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
|
|
+static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
|
|
+ const struct drm_display_info *display,
|
|
+ const struct drm_display_mode *mode)
|
|
{
|
|
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
|
|
- phy_power_off(hdmi->phy);
|
|
+ dw_hdmi_rockchip_genphy_disable(dw_hdmi, data);
|
|
+ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display);
|
|
+
|
|
+ return phy_power_on(hdmi->phy);
|
|
}
|
|
|
|
static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
|
|
@@ -437,6 +2610,90 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
|
|
RK3328_HDMI_HPD_IOE));
|
|
}
|
|
|
|
+static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi,
|
|
+ void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ while (hdmi->phy->power_count > 0)
|
|
+ phy_power_off(hdmi->phy);
|
|
+}
|
|
+
|
|
+static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data,
|
|
+ struct drm_display_mode *mode)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data);
|
|
+
|
|
+ return phy_power_on(hdmi->phy);
|
|
+}
|
|
+
|
|
+static enum drm_connector_status
|
|
+dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
|
|
+{
|
|
+ u32 val;
|
|
+ int ret;
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
|
|
+
|
|
+ if (!hdmi->id) {
|
|
+ if (val & RK3588_HDMI0_LEVEL_INT) {
|
|
+ hdmi->hpd_stat = true;
|
|
+ ret = connector_status_connected;
|
|
+ } else {
|
|
+ hdmi->hpd_stat = false;
|
|
+ ret = connector_status_disconnected;
|
|
+ }
|
|
+ } else {
|
|
+ if (val & RK3588_HDMI1_LEVEL_INT) {
|
|
+ hdmi->hpd_stat = true;
|
|
+ ret = connector_status_connected;
|
|
+ } else {
|
|
+ hdmi->hpd_stat = false;
|
|
+ ret = connector_status_disconnected;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+ u32 val;
|
|
+
|
|
+ if (!hdmi->id) {
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
|
|
+ RK3588_HDMI0_HPD_INT_CLR) |
|
|
+ HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
|
|
+ } else {
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
|
|
+ RK3588_HDMI1_HPD_INT_CLR) |
|
|
+ HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
|
|
+ }
|
|
+
|
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
|
+}
|
|
+
|
|
+static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data,
|
|
+ u32 mode_mask, bool enable)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
|
+
|
|
+ if (!hdmi->phy)
|
|
+ return;
|
|
+
|
|
+ /* set phy earc/frl mode */
|
|
+ if (enable)
|
|
+ hdmi->phy_bus_width |= mode_mask;
|
|
+ else
|
|
+ hdmi->phy_bus_width &= ~mode_mask;
|
|
+
|
|
+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
|
|
+}
|
|
+
|
|
static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
|
|
.init = dw_hdmi_rockchip_genphy_init,
|
|
.disable = dw_hdmi_rockchip_genphy_disable,
|
|
@@ -526,6 +2783,30 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
|
|
.use_drm_infoframe = true,
|
|
};
|
|
|
|
+static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
|
|
+ .init = dw_hdmi_qp_rockchip_genphy_init,
|
|
+ .disable = dw_hdmi_qp_rockchip_phy_disable,
|
|
+ .read_hpd = dw_hdmi_rk3588_read_hpd,
|
|
+ .setup_hpd = dw_hdmi_rk3588_setup_hpd,
|
|
+ .set_mode = dw_hdmi_rk3588_phy_set_mode,
|
|
+};
|
|
+
|
|
+struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = {
|
|
+ .lcdsel_grf_reg = -1,
|
|
+ .ddc_en_reg = RK3588_GRF_VO1_CON3,
|
|
+ .split_mode = true,
|
|
+};
|
|
+
|
|
+static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
|
|
+ .phy_data = &rk3588_hdmi_chip_data,
|
|
+ .qp_phy_ops = &rk3588_hdmi_phy_ops,
|
|
+ .phy_name = "samsung_hdptx_phy",
|
|
+ .phy_force_vendor = true,
|
|
+ .ycbcr_420_allowed = true,
|
|
+ .is_hdmi_qp = true,
|
|
+ .use_drm_infoframe = true,
|
|
+};
|
|
+
|
|
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
|
{ .compatible = "rockchip,rk3228-dw-hdmi",
|
|
.data = &rk3228_hdmi_drv_data
|
|
@@ -542,6 +2823,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);
|
|
@@ -551,44 +2835,107 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct dw_hdmi_plat_data *plat_data;
|
|
- const struct of_device_id *match;
|
|
struct drm_device *drm = data;
|
|
struct drm_encoder *encoder;
|
|
struct rockchip_hdmi *hdmi;
|
|
+ struct rockchip_hdmi *secondary;
|
|
int ret;
|
|
+ u32 val;
|
|
|
|
if (!pdev->dev.of_node)
|
|
return -ENODEV;
|
|
|
|
- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
|
+ hdmi = platform_get_drvdata(pdev);
|
|
if (!hdmi)
|
|
return -ENOMEM;
|
|
|
|
- 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);
|
|
- if (!plat_data)
|
|
- return -ENOMEM;
|
|
+ plat_data = hdmi->plat_data;
|
|
+ hdmi->drm_dev = drm;
|
|
|
|
- hdmi->dev = &pdev->dev;
|
|
- hdmi->plat_data = plat_data;
|
|
- hdmi->chip_data = plat_data->phy_data;
|
|
plat_data->phy_data = hdmi;
|
|
- plat_data->priv_data = hdmi;
|
|
- encoder = &hdmi->encoder.encoder;
|
|
+ plat_data->get_input_bus_format =
|
|
+ dw_hdmi_rockchip_get_input_bus_format;
|
|
+ plat_data->get_output_bus_format =
|
|
+ dw_hdmi_rockchip_get_output_bus_format;
|
|
+ plat_data->get_enc_in_encoding =
|
|
+ dw_hdmi_rockchip_get_enc_in_encoding;
|
|
+ plat_data->get_enc_out_encoding =
|
|
+ dw_hdmi_rockchip_get_enc_out_encoding;
|
|
+ plat_data->get_quant_range =
|
|
+ dw_hdmi_rockchip_get_quant_range;
|
|
+ plat_data->get_hdr_property =
|
|
+ dw_hdmi_rockchip_get_hdr_property;
|
|
+ plat_data->get_hdr_blob =
|
|
+ dw_hdmi_rockchip_get_hdr_blob;
|
|
+ plat_data->get_color_changed =
|
|
+ dw_hdmi_rockchip_get_color_changed;
|
|
+ plat_data->get_yuv422_format =
|
|
+ dw_hdmi_rockchip_get_yuv422_format;
|
|
+ plat_data->get_edid_dsc_info =
|
|
+ dw_hdmi_rockchip_get_edid_dsc_info;
|
|
+ plat_data->get_next_hdr_data =
|
|
+ dw_hdmi_rockchip_get_next_hdr_data;
|
|
+ plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg;
|
|
+ plat_data->set_grf_cfg = rk3588_set_grf_cfg;
|
|
+ plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode;
|
|
+ plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode;
|
|
+ plat_data->dclk_set = dw_hdmi_dclk_set;
|
|
+
|
|
+ plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
|
|
+
|
|
+ secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id);
|
|
+ /* If don't enable hdmi0 and hdmi1, we don't enable split mode */
|
|
+ if (hdmi->chip_data->split_mode && secondary) {
|
|
|
|
- encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
|
- rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
|
|
- dev->of_node, 0, 0);
|
|
+ /*
|
|
+ * hdmi can only attach bridge and init encoder/connector in the
|
|
+ * last bind hdmi in split mode, or hdmi->hdmi_qp will not be initialized
|
|
+ * and plat_data->left/right will be null pointer. we must check if split
|
|
+ * mode is on and determine the sequence of hdmi bind.
|
|
+ */
|
|
+ if (device_property_read_bool(dev, "split-mode") ||
|
|
+ device_property_read_bool(secondary->dev, "split-mode")) {
|
|
+ plat_data->split_mode = true;
|
|
+ secondary->plat_data->split_mode = true;
|
|
+ if (!secondary->plat_data->first_screen)
|
|
+ plat_data->first_screen = true;
|
|
+ }
|
|
+
|
|
+ if (device_property_read_bool(dev, "user-split-mode") ||
|
|
+ device_property_read_bool(secondary->dev, "user-split-mode")) {
|
|
+ hdmi->user_split_mode = true;
|
|
+ secondary->user_split_mode = true;
|
|
+ }
|
|
+ }
|
|
|
|
- /*
|
|
- * If we failed to find the CRTC(s) which this encoder is
|
|
- * supposed to be connected to, it's because the CRTC has
|
|
- * not been registered yet. Defer probing, and hope that
|
|
- * the required CRTC is added later.
|
|
- */
|
|
- if (encoder->possible_crtcs == 0)
|
|
- return -EPROBE_DEFER;
|
|
+ if (!plat_data->first_screen) {
|
|
+ encoder = &hdmi->encoder.encoder;
|
|
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
|
+ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
|
|
+ dev->of_node, 0, 0);
|
|
+ /*
|
|
+ * If we failed to find the CRTC(s) which this encoder is
|
|
+ * supposed to be connected to, it's because the CRTC has
|
|
+ * not been registered yet. Defer probing, and hope that
|
|
+ * the required CRTC is added later.
|
|
+ */
|
|
+ if (encoder->possible_crtcs == 0)
|
|
+ return -EPROBE_DEFER;
|
|
+
|
|
+ drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
|
|
+ // [CC:] consider using drmm_simple_encoder_alloc()
|
|
+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
|
|
+ }
|
|
+
|
|
+ if (!plat_data->max_tmdsclk)
|
|
+ hdmi->max_tmdsclk = 594000;
|
|
+ else
|
|
+ hdmi->max_tmdsclk = plat_data->max_tmdsclk;
|
|
+
|
|
+ hdmi->is_hdmi_qp = plat_data->is_hdmi_qp;
|
|
+
|
|
+ hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input;
|
|
+ hdmi->unsupported_deep_color = plat_data->unsupported_deep_color;
|
|
|
|
ret = rockchip_hdmi_parse_dt(hdmi);
|
|
if (ret) {
|
|
@@ -597,34 +2944,44 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
return ret;
|
|
}
|
|
|
|
- hdmi->phy = devm_phy_optional_get(dev, "hdmi");
|
|
- if (IS_ERR(hdmi->phy)) {
|
|
- ret = PTR_ERR(hdmi->phy);
|
|
- if (ret != -EPROBE_DEFER)
|
|
- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
|
|
+ ret = clk_prepare_enable(hdmi->aud_clk);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
- ret = regulator_enable(hdmi->avdd_0v9);
|
|
+ ret = clk_prepare_enable(hdmi->hpd_clk);
|
|
if (ret) {
|
|
- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
|
|
- goto err_avdd_0v9;
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret);
|
|
+ return ret;
|
|
}
|
|
|
|
- ret = regulator_enable(hdmi->avdd_1v8);
|
|
+ ret = clk_prepare_enable(hdmi->hclk_vo1);
|
|
if (ret) {
|
|
- DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
|
|
- goto err_avdd_1v8;
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret);
|
|
+ return ret;
|
|
}
|
|
|
|
- ret = clk_prepare_enable(hdmi->ref_clk);
|
|
+ ret = clk_prepare_enable(hdmi->earc_clk);
|
|
if (ret) {
|
|
- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
|
|
- ret);
|
|
- goto err_clk;
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(hdmi->hdmitx_ref);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(hdmi->pclk);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret);
|
|
+ return ret;
|
|
}
|
|
|
|
- if (hdmi->chip_data == &rk3568_chip_data) {
|
|
+ if (hdmi->chip_data->ddc_en_reg == RK3568_GRF_VO_CON1) {
|
|
regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
|
|
HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
|
|
RK3568_HDMI_SCLIN_MSK,
|
|
@@ -632,12 +2989,132 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
RK3568_HDMI_SCLIN_MSK));
|
|
}
|
|
|
|
- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
|
|
- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ if (!hdmi->id) {
|
|
+ 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);
|
|
+ } else {
|
|
+ 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_CON6, 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_HDMI1_GRANT_SEL,
|
|
+ RK3588_HDMI1_GRANT_SEL);
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
|
|
+ }
|
|
+ init_hpd_work(hdmi);
|
|
+ }
|
|
|
|
- platform_set_drvdata(pdev, hdmi);
|
|
+ ret = clk_prepare_enable(hdmi->hclk_vio);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
|
|
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
|
|
+ ret = clk_prepare_enable(hdmi->hclk_vop);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(hdmi->ref_clk);
|
|
+ if (ret) {
|
|
+ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
|
|
+ ret);
|
|
+ goto err_clk;
|
|
+ }
|
|
+
|
|
+ ret = regulator_enable(hdmi->avdd_0v9);
|
|
+ if (ret) {
|
|
+ DRM_DEV_ERROR(hdmi->dev, "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);
|
|
+ goto err_avdd_1v8;
|
|
+ }
|
|
+
|
|
+ if (!hdmi->id)
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
|
|
+ else
|
|
+ val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK);
|
|
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
|
|
+
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ hdmi->hpd_irq = platform_get_irq(pdev, 4);
|
|
+ if (hdmi->hpd_irq < 0)
|
|
+ return hdmi->hpd_irq;
|
|
+
|
|
+ ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq,
|
|
+ rockchip_hdmi_hardirq,
|
|
+ rockchip_hdmi_irq,
|
|
+ IRQF_SHARED, "dw-hdmi-qp-hpd",
|
|
+ hdmi);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ hdmi->phy = devm_phy_optional_get(dev, "hdmi");
|
|
+ if (IS_ERR(hdmi->phy)) {
|
|
+ hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy");
|
|
+ if (IS_ERR(hdmi->phy)) {
|
|
+ ret = PTR_ERR(hdmi->phy);
|
|
+ if (ret != -EPROBE_DEFER)
|
|
+ DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ // [CC:] do proper error handling, e.g. clk_disable_unprepare
|
|
+ hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, &hdmi->encoder.encoder, plat_data);
|
|
+
|
|
+ if (IS_ERR(hdmi->hdmi_qp)) {
|
|
+ ret = PTR_ERR(hdmi->hdmi_qp);
|
|
+ drm_encoder_cleanup(&hdmi->encoder.encoder);
|
|
+ }
|
|
+
|
|
+ if (plat_data->connector) {
|
|
+ hdmi->sub_dev.connector = plat_data->connector;
|
|
+ hdmi->sub_dev.of_node = dev->of_node;
|
|
+ rockchip_drm_register_sub_dev(&hdmi->sub_dev);
|
|
+ }
|
|
+
|
|
+ if (plat_data->split_mode && secondary) {
|
|
+ if (device_property_read_bool(dev, "split-mode")) {
|
|
+ plat_data->right = secondary->hdmi_qp;
|
|
+ secondary->plat_data->left = hdmi->hdmi_qp;
|
|
+ } else {
|
|
+ plat_data->left = secondary->hdmi_qp;
|
|
+ secondary->plat_data->right = hdmi->hdmi_qp;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ hdmi->hdmi = dw_hdmi_bind(pdev, &hdmi->encoder.encoder, plat_data);
|
|
|
|
/*
|
|
* If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
|
|
@@ -648,11 +3125,24 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
|
goto err_bind;
|
|
}
|
|
|
|
+ if (plat_data->connector) {
|
|
+ hdmi->sub_dev.connector = plat_data->connector;
|
|
+ hdmi->sub_dev.of_node = dev->of_node;
|
|
+ rockchip_drm_register_sub_dev(&hdmi->sub_dev);
|
|
+ }
|
|
+
|
|
return 0;
|
|
|
|
err_bind:
|
|
- drm_encoder_cleanup(encoder);
|
|
+ drm_encoder_cleanup(&hdmi->encoder.encoder);
|
|
+ clk_disable_unprepare(hdmi->aud_clk);
|
|
clk_disable_unprepare(hdmi->ref_clk);
|
|
+ clk_disable_unprepare(hdmi->hclk_vop);
|
|
+ clk_disable_unprepare(hdmi->hpd_clk);
|
|
+ clk_disable_unprepare(hdmi->hclk_vo1);
|
|
+ clk_disable_unprepare(hdmi->earc_clk);
|
|
+ clk_disable_unprepare(hdmi->hdmitx_ref);
|
|
+ clk_disable_unprepare(hdmi->pclk);
|
|
err_clk:
|
|
regulator_disable(hdmi->avdd_1v8);
|
|
err_avdd_1v8:
|
|
@@ -666,9 +3156,30 @@ 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(&hdmi->work);
|
|
+ flush_workqueue(hdmi->workqueue);
|
|
+ destroy_workqueue(hdmi->workqueue);
|
|
+ }
|
|
+
|
|
+ if (hdmi->sub_dev.connector)
|
|
+ rockchip_drm_unregister_sub_dev(&hdmi->sub_dev);
|
|
+
|
|
+ if (hdmi->is_hdmi_qp)
|
|
+ dw_hdmi_qp_unbind(hdmi->hdmi_qp);
|
|
+ else
|
|
+ dw_hdmi_unbind(hdmi->hdmi);
|
|
+
|
|
drm_encoder_cleanup(&hdmi->encoder.encoder);
|
|
+
|
|
+ clk_disable_unprepare(hdmi->aud_clk);
|
|
clk_disable_unprepare(hdmi->ref_clk);
|
|
+ clk_disable_unprepare(hdmi->hclk_vop);
|
|
+ clk_disable_unprepare(hdmi->hpd_clk);
|
|
+ clk_disable_unprepare(hdmi->hclk_vo1);
|
|
+ clk_disable_unprepare(hdmi->earc_clk);
|
|
+ clk_disable_unprepare(hdmi->hdmitx_ref);
|
|
+ clk_disable_unprepare(hdmi->pclk);
|
|
|
|
regulator_disable(hdmi->avdd_1v8);
|
|
regulator_disable(hdmi->avdd_0v9);
|
|
@@ -681,30 +3192,131 @@ static const struct component_ops dw_hdmi_rockchip_ops = {
|
|
|
|
static int dw_hdmi_rockchip_probe(struct platform_device *pdev)
|
|
{
|
|
+ struct rockchip_hdmi *hdmi;
|
|
+ const struct of_device_id *match;
|
|
+ struct dw_hdmi_plat_data *plat_data;
|
|
+ int id;
|
|
+
|
|
+ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
|
+ if (!hdmi)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ id = of_alias_get_id(pdev->dev.of_node, "hdmi");
|
|
+ if (id < 0)
|
|
+ id = 0;
|
|
+
|
|
+ hdmi->id = id;
|
|
+ hdmi->dev = &pdev->dev;
|
|
+
|
|
+ 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);
|
|
+ if (!plat_data)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ plat_data->id = hdmi->id;
|
|
+ hdmi->plat_data = plat_data;
|
|
+ hdmi->chip_data = plat_data->phy_data;
|
|
+
|
|
+ platform_set_drvdata(pdev, hdmi);
|
|
+ pm_runtime_enable(&pdev->dev);
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
+
|
|
return component_add(&pdev->dev, &dw_hdmi_rockchip_ops);
|
|
}
|
|
|
|
+static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev);
|
|
+
|
|
+ if (!hdmi)
|
|
+ return;
|
|
+
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ cancel_delayed_work(&hdmi->work);
|
|
+ flush_workqueue(hdmi->workqueue);
|
|
+ dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp);
|
|
+ } else {
|
|
+ dw_hdmi_suspend(hdmi->hdmi);
|
|
+ }
|
|
+ pm_runtime_put_sync(&pdev->dev);
|
|
+}
|
|
+
|
|
static void dw_hdmi_rockchip_remove(struct platform_device *pdev)
|
|
{
|
|
component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
+}
|
|
+
|
|
+static int dw_hdmi_rockchip_suspend(struct device *dev)
|
|
+{
|
|
+ struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
|
|
+
|
|
+ if (hdmi->is_hdmi_qp)
|
|
+ dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp);
|
|
+ else
|
|
+ dw_hdmi_suspend(hdmi->hdmi);
|
|
+
|
|
+ pm_runtime_put_sync(dev);
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev)
|
|
{
|
|
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
|
|
+ u32 val;
|
|
|
|
- dw_hdmi_resume(hdmi->hdmi);
|
|
+ if (hdmi->is_hdmi_qp) {
|
|
+ if (!hdmi->id) {
|
|
+ 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);
|
|
+ } else {
|
|
+ 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_CON6, 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_HDMI1_GRANT_SEL,
|
|
+ RK3588_HDMI1_GRANT_SEL);
|
|
+ regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
|
|
+ }
|
|
+
|
|
+ dw_hdmi_qp_resume(dev, hdmi->hdmi_qp);
|
|
+ drm_helper_hpd_irq_event(hdmi->drm_dev);
|
|
+ } else {
|
|
+ dw_hdmi_resume(hdmi->hdmi);
|
|
+ }
|
|
+ pm_runtime_get_sync(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops dw_hdmi_rockchip_pm = {
|
|
- SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume)
|
|
+ SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend,
|
|
+ dw_hdmi_rockchip_resume)
|
|
};
|
|
|
|
struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
|
|
.probe = dw_hdmi_rockchip_probe,
|
|
.remove_new = dw_hdmi_rockchip_remove,
|
|
+ .shutdown = dw_hdmi_rockchip_shutdown,
|
|
.driver = {
|
|
.name = "dwhdmi-rockchip",
|
|
.pm = &dw_hdmi_rockchip_pm,
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
|
|
index ab55d7132..44d49c86b 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
|
|
@@ -17,8 +17,12 @@
|
|
#include <linux/iommu.h>
|
|
|
|
#include <drm/drm_aperture.h>
|
|
+#include <drm/drm_edid.h>
|
|
+#include <drm/drm_debugfs.h>
|
|
+#include <drm/drm_displayid.h>
|
|
#include <drm/drm_drv.h>
|
|
#include <drm/drm_fbdev_generic.h>
|
|
+//#include <drm/drm_framebuffer.h>
|
|
#include <drm/drm_gem_dma_helper.h>
|
|
#include <drm/drm_of.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
@@ -44,6 +48,629 @@
|
|
|
|
static const struct drm_driver rockchip_drm_driver;
|
|
|
|
+void drm_mode_convert_to_split_mode(struct drm_display_mode *mode)
|
|
+{
|
|
+ u16 hactive, hfp, hsync, hbp;
|
|
+
|
|
+ hactive = mode->hdisplay;
|
|
+ hfp = mode->hsync_start - mode->hdisplay;
|
|
+ hsync = mode->hsync_end - mode->hsync_start;
|
|
+ hbp = mode->htotal - mode->hsync_end;
|
|
+
|
|
+ mode->clock *= 2;
|
|
+ mode->hdisplay = hactive * 2;
|
|
+ mode->hsync_start = mode->hdisplay + hfp * 2;
|
|
+ mode->hsync_end = mode->hsync_start + hsync * 2;
|
|
+ mode->htotal = mode->hsync_end + hbp * 2;
|
|
+ drm_mode_set_name(mode);
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mode_convert_to_split_mode);
|
|
+
|
|
+void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode)
|
|
+{
|
|
+ u16 hactive, hfp, hsync, hbp;
|
|
+
|
|
+ hactive = mode->hdisplay;
|
|
+ hfp = mode->hsync_start - mode->hdisplay;
|
|
+ hsync = mode->hsync_end - mode->hsync_start;
|
|
+ hbp = mode->htotal - mode->hsync_end;
|
|
+
|
|
+ mode->clock /= 2;
|
|
+ mode->hdisplay = hactive / 2;
|
|
+ mode->hsync_start = mode->hdisplay + hfp / 2;
|
|
+ mode->hsync_end = mode->hsync_start + hsync / 2;
|
|
+ mode->htotal = mode->hsync_end + hbp / 2;
|
|
+}
|
|
+EXPORT_SYMBOL(drm_mode_convert_to_origin_mode);
|
|
+
|
|
+static DEFINE_MUTEX(rockchip_drm_sub_dev_lock);
|
|
+static LIST_HEAD(rockchip_drm_sub_dev_list);
|
|
+
|
|
+void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev)
|
|
+{
|
|
+ mutex_lock(&rockchip_drm_sub_dev_lock);
|
|
+ list_add_tail(&sub_dev->list, &rockchip_drm_sub_dev_list);
|
|
+ mutex_unlock(&rockchip_drm_sub_dev_lock);
|
|
+}
|
|
+EXPORT_SYMBOL(rockchip_drm_register_sub_dev);
|
|
+
|
|
+void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev)
|
|
+{
|
|
+ mutex_lock(&rockchip_drm_sub_dev_lock);
|
|
+ list_del(&sub_dev->list);
|
|
+ mutex_unlock(&rockchip_drm_sub_dev_lock);
|
|
+}
|
|
+EXPORT_SYMBOL(rockchip_drm_unregister_sub_dev);
|
|
+
|
|
+static int
|
|
+cea_db_tag(const u8 *db)
|
|
+{
|
|
+ return db[0] >> 5;
|
|
+}
|
|
+
|
|
+static int
|
|
+cea_db_payload_len(const u8 *db)
|
|
+{
|
|
+ return db[0] & 0x1f;
|
|
+}
|
|
+
|
|
+#define for_each_cea_db(cea, i, start, end) \
|
|
+ for ((i) = (start); \
|
|
+ (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); \
|
|
+ (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
|
|
+
|
|
+#define HDMI_NEXT_HDR_VSDB_OUI 0xd04601
|
|
+
|
|
+static bool cea_db_is_hdmi_next_hdr_block(const u8 *db)
|
|
+{
|
|
+ unsigned int oui;
|
|
+
|
|
+ if (cea_db_tag(db) != 0x07)
|
|
+ return false;
|
|
+
|
|
+ if (cea_db_payload_len(db) < 11)
|
|
+ return false;
|
|
+
|
|
+ oui = db[3] << 16 | db[2] << 8 | db[1];
|
|
+
|
|
+ return oui == HDMI_NEXT_HDR_VSDB_OUI;
|
|
+}
|
|
+
|
|
+static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
|
|
+{
|
|
+ unsigned int oui;
|
|
+
|
|
+ if (cea_db_tag(db) != 0x03)
|
|
+ return false;
|
|
+
|
|
+ if (cea_db_payload_len(db) < 7)
|
|
+ return false;
|
|
+
|
|
+ oui = db[3] << 16 | db[2] << 8 | db[1];
|
|
+
|
|
+ return oui == HDMI_FORUM_IEEE_OUI;
|
|
+}
|
|
+
|
|
+static int
|
|
+cea_db_offsets(const u8 *cea, int *start, int *end)
|
|
+{
|
|
+ /* DisplayID CTA extension blocks and top-level CEA EDID
|
|
+ * block header definitions differ in the following bytes:
|
|
+ * 1) Byte 2 of the header specifies length differently,
|
|
+ * 2) Byte 3 is only present in the CEA top level block.
|
|
+ *
|
|
+ * The different definitions for byte 2 follow.
|
|
+ *
|
|
+ * DisplayID CTA extension block defines byte 2 as:
|
|
+ * Number of payload bytes
|
|
+ *
|
|
+ * CEA EDID block defines byte 2 as:
|
|
+ * Byte number (decimal) within this block where the 18-byte
|
|
+ * DTDs begin. If no non-DTD data is present in this extension
|
|
+ * block, the value should be set to 04h (the byte after next).
|
|
+ * If set to 00h, there are no DTDs present in this block and
|
|
+ * no non-DTD data.
|
|
+ */
|
|
+ if (cea[0] == 0x81) {
|
|
+ /*
|
|
+ * for_each_displayid_db() has already verified
|
|
+ * that these stay within expected bounds.
|
|
+ */
|
|
+ *start = 3;
|
|
+ *end = *start + cea[2];
|
|
+ } else if (cea[0] == 0x02) {
|
|
+ /* Data block offset in CEA extension block */
|
|
+ *start = 4;
|
|
+ *end = cea[2];
|
|
+ if (*end == 0)
|
|
+ *end = 127;
|
|
+ if (*end < 4 || *end > 127)
|
|
+ return -ERANGE;
|
|
+ } else {
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u8 *find_edid_extension(const struct edid *edid,
|
|
+ int ext_id, int *ext_index)
|
|
+{
|
|
+ u8 *edid_ext = NULL;
|
|
+ int i;
|
|
+
|
|
+ /* No EDID or EDID extensions */
|
|
+ if (edid == NULL || edid->extensions == 0)
|
|
+ return NULL;
|
|
+
|
|
+ /* Find CEA extension */
|
|
+ for (i = *ext_index; i < edid->extensions; i++) {
|
|
+ edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
|
|
+ if (edid_ext[0] == ext_id)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (i >= edid->extensions)
|
|
+ return NULL;
|
|
+
|
|
+ *ext_index = i + 1;
|
|
+
|
|
+ return edid_ext;
|
|
+}
|
|
+
|
|
+static int validate_displayid(u8 *displayid, int length, int idx)
|
|
+{
|
|
+ int i, dispid_length;
|
|
+ u8 csum = 0;
|
|
+ struct displayid_header *base;
|
|
+
|
|
+ base = (struct displayid_header *)&displayid[idx];
|
|
+
|
|
+ DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
|
|
+ base->rev, base->bytes, base->prod_id, base->ext_count);
|
|
+
|
|
+ /* +1 for DispID checksum */
|
|
+ dispid_length = sizeof(*base) + base->bytes + 1;
|
|
+ if (dispid_length > length - idx)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (i = 0; i < dispid_length; i++)
|
|
+ csum += displayid[idx + i];
|
|
+ if (csum) {
|
|
+ DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u8 *find_displayid_extension(const struct edid *edid,
|
|
+ int *length, int *idx,
|
|
+ int *ext_index)
|
|
+{
|
|
+ u8 *displayid = find_edid_extension(edid, 0x70, ext_index);
|
|
+ struct displayid_header *base;
|
|
+ int ret;
|
|
+
|
|
+ if (!displayid)
|
|
+ return NULL;
|
|
+
|
|
+ /* EDID extensions block checksum isn't for us */
|
|
+ *length = EDID_LENGTH - 1;
|
|
+ *idx = 1;
|
|
+
|
|
+ ret = validate_displayid(displayid, *length, *idx);
|
|
+ if (ret)
|
|
+ return NULL;
|
|
+
|
|
+ base = (struct displayid_header *)&displayid[*idx];
|
|
+ *length = *idx + sizeof(*base) + base->bytes;
|
|
+
|
|
+ return displayid;
|
|
+}
|
|
+
|
|
+static u8 *find_cea_extension(const struct edid *edid)
|
|
+{
|
|
+ int length, idx;
|
|
+ const struct displayid_block *block;
|
|
+ u8 *cea;
|
|
+ u8 *displayid;
|
|
+ int ext_index;
|
|
+
|
|
+ /* Look for a top level CEA extension block */
|
|
+ /* FIXME: make callers iterate through multiple CEA ext blocks? */
|
|
+ ext_index = 0;
|
|
+ cea = find_edid_extension(edid, 0x02, &ext_index);
|
|
+ if (cea)
|
|
+ return cea;
|
|
+
|
|
+ /* CEA blocks can also be found embedded in a DisplayID block */
|
|
+ ext_index = 0;
|
|
+ for (;;) {
|
|
+ struct displayid_iter iter;
|
|
+ const struct drm_edid *drm_edid;
|
|
+ int edid_size = (edid->extensions + 1) * EDID_LENGTH;
|
|
+
|
|
+ drm_edid = drm_edid_alloc(edid, edid_size);
|
|
+ if (!drm_edid)
|
|
+ return NULL;
|
|
+ displayid = find_displayid_extension(edid, &length, &idx,
|
|
+ &ext_index);
|
|
+ if (!displayid) {
|
|
+ drm_edid_free(drm_edid);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ displayid_iter_edid_begin(drm_edid, &iter);
|
|
+ idx += sizeof(struct displayid_header);
|
|
+ displayid_iter_for_each(block, &iter) {
|
|
+ if (block->tag == 0x81) {
|
|
+ displayid_iter_end(&iter);
|
|
+ drm_edid_free(drm_edid);
|
|
+ return (u8 *)block;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ displayid_iter_end(&iter);
|
|
+ drm_edid_free(drm_edid);
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#define EDID_CEA_YCRCB422 (1 << 4)
|
|
+
|
|
+int rockchip_drm_get_yuv422_format(struct drm_connector *connector,
|
|
+ struct edid *edid)
|
|
+{
|
|
+ struct drm_display_info *info;
|
|
+ const u8 *edid_ext;
|
|
+
|
|
+ if (!connector || !edid)
|
|
+ return -EINVAL;
|
|
+
|
|
+ info = &connector->display_info;
|
|
+
|
|
+ edid_ext = find_cea_extension(edid);
|
|
+ if (!edid_ext)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (edid_ext[3] & EDID_CEA_YCRCB422)
|
|
+ info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(rockchip_drm_get_yuv422_format);
|
|
+
|
|
+static
|
|
+void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane)
|
|
+{
|
|
+ switch (max_frl_rate) {
|
|
+ case 1:
|
|
+ *max_lanes = 3;
|
|
+ *max_rate_per_lane = 3;
|
|
+ break;
|
|
+ case 2:
|
|
+ *max_lanes = 3;
|
|
+ *max_rate_per_lane = 6;
|
|
+ break;
|
|
+ case 3:
|
|
+ *max_lanes = 4;
|
|
+ *max_rate_per_lane = 6;
|
|
+ break;
|
|
+ case 4:
|
|
+ *max_lanes = 4;
|
|
+ *max_rate_per_lane = 8;
|
|
+ break;
|
|
+ case 5:
|
|
+ *max_lanes = 4;
|
|
+ *max_rate_per_lane = 10;
|
|
+ break;
|
|
+ case 6:
|
|
+ *max_lanes = 4;
|
|
+ *max_rate_per_lane = 12;
|
|
+ break;
|
|
+ case 0:
|
|
+ default:
|
|
+ *max_lanes = 0;
|
|
+ *max_rate_per_lane = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+#define EDID_DSC_10BPC (1 << 0)
|
|
+#define EDID_DSC_12BPC (1 << 1)
|
|
+#define EDID_DSC_16BPC (1 << 2)
|
|
+#define EDID_DSC_ALL_BPP (1 << 3)
|
|
+#define EDID_DSC_NATIVE_420 (1 << 6)
|
|
+#define EDID_DSC_1P2 (1 << 7)
|
|
+#define EDID_DSC_MAX_FRL_RATE_MASK 0xf0
|
|
+#define EDID_DSC_MAX_SLICES 0xf
|
|
+#define EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f
|
|
+#define EDID_MAX_FRL_RATE_MASK 0xf0
|
|
+
|
|
+static
|
|
+void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap,
|
|
+ u8 *max_frl_rate_per_lane, u8 *max_lanes,
|
|
+ const u8 *hf_vsdb)
|
|
+{
|
|
+ u8 max_frl_rate;
|
|
+ u8 dsc_max_frl_rate;
|
|
+ u8 dsc_max_slices;
|
|
+
|
|
+ if (!hf_vsdb[7])
|
|
+ return;
|
|
+
|
|
+ DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n");
|
|
+ max_frl_rate = (hf_vsdb[7] & EDID_MAX_FRL_RATE_MASK) >> 4;
|
|
+ get_max_frl_rate(max_frl_rate, max_lanes,
|
|
+ max_frl_rate_per_lane);
|
|
+
|
|
+ if (cea_db_payload_len(hf_vsdb) < 13)
|
|
+ return;
|
|
+
|
|
+ dsc_cap->v_1p2 = hf_vsdb[11] & EDID_DSC_1P2;
|
|
+
|
|
+ if (!dsc_cap->v_1p2)
|
|
+ return;
|
|
+
|
|
+ dsc_cap->native_420 = hf_vsdb[11] & EDID_DSC_NATIVE_420;
|
|
+ dsc_cap->all_bpp = hf_vsdb[11] & EDID_DSC_ALL_BPP;
|
|
+
|
|
+ if (hf_vsdb[11] & EDID_DSC_16BPC)
|
|
+ dsc_cap->bpc_supported = 16;
|
|
+ else if (hf_vsdb[11] & EDID_DSC_12BPC)
|
|
+ dsc_cap->bpc_supported = 12;
|
|
+ else if (hf_vsdb[11] & EDID_DSC_10BPC)
|
|
+ dsc_cap->bpc_supported = 10;
|
|
+ else
|
|
+ dsc_cap->bpc_supported = 0;
|
|
+
|
|
+ dsc_max_frl_rate = (hf_vsdb[12] & EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
|
|
+ get_max_frl_rate(dsc_max_frl_rate, &dsc_cap->max_lanes,
|
|
+ &dsc_cap->max_frl_rate_per_lane);
|
|
+ dsc_cap->total_chunk_kbytes = hf_vsdb[13] & EDID_DSC_TOTAL_CHUNK_KBYTES;
|
|
+
|
|
+ dsc_max_slices = hf_vsdb[12] & EDID_DSC_MAX_SLICES;
|
|
+ switch (dsc_max_slices) {
|
|
+ case 1:
|
|
+ dsc_cap->max_slices = 1;
|
|
+ dsc_cap->clk_per_slice = 340;
|
|
+ break;
|
|
+ case 2:
|
|
+ dsc_cap->max_slices = 2;
|
|
+ dsc_cap->clk_per_slice = 340;
|
|
+ break;
|
|
+ case 3:
|
|
+ dsc_cap->max_slices = 4;
|
|
+ dsc_cap->clk_per_slice = 340;
|
|
+ break;
|
|
+ case 4:
|
|
+ dsc_cap->max_slices = 8;
|
|
+ dsc_cap->clk_per_slice = 340;
|
|
+ break;
|
|
+ case 5:
|
|
+ dsc_cap->max_slices = 8;
|
|
+ dsc_cap->clk_per_slice = 400;
|
|
+ break;
|
|
+ case 6:
|
|
+ dsc_cap->max_slices = 12;
|
|
+ dsc_cap->clk_per_slice = 400;
|
|
+ break;
|
|
+ case 7:
|
|
+ dsc_cap->max_slices = 16;
|
|
+ dsc_cap->clk_per_slice = 400;
|
|
+ break;
|
|
+ case 0:
|
|
+ default:
|
|
+ dsc_cap->max_slices = 0;
|
|
+ dsc_cap->clk_per_slice = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+enum {
|
|
+ VER_26_BYTE_V0,
|
|
+ VER_15_BYTE_V1,
|
|
+ VER_12_BYTE_V1,
|
|
+ VER_12_BYTE_V2,
|
|
+};
|
|
+
|
|
+static int check_next_hdr_version(const u8 *next_hdr_db)
|
|
+{
|
|
+ u16 ver;
|
|
+
|
|
+ ver = (next_hdr_db[5] & 0xf0) << 8 | next_hdr_db[0];
|
|
+
|
|
+ switch (ver) {
|
|
+ case 0x00f9:
|
|
+ return VER_26_BYTE_V0;
|
|
+ case 0x20ee:
|
|
+ return VER_15_BYTE_V1;
|
|
+ case 0x20eb:
|
|
+ return VER_12_BYTE_V1;
|
|
+ case 0x40eb:
|
|
+ return VER_12_BYTE_V2;
|
|
+ default:
|
|
+ return -ENOENT;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void parse_ver_26_v0_data(struct ver_26_v0 *hdr, const u8 *data)
|
|
+{
|
|
+ hdr->yuv422_12bit = data[5] & BIT(0);
|
|
+ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1;
|
|
+ hdr->global_dimming = (data[5] & BIT(2)) >> 2;
|
|
+
|
|
+ hdr->dm_major_ver = (data[21] & 0xf0) >> 4;
|
|
+ hdr->dm_minor_ver = data[21] & 0xf;
|
|
+
|
|
+ hdr->t_min_pq = (data[19] << 4) | ((data[18] & 0xf0) >> 4);
|
|
+ hdr->t_max_pq = (data[20] << 4) | (data[18] & 0xf);
|
|
+
|
|
+ hdr->rx = (data[7] << 4) | ((data[6] & 0xf0) >> 4);
|
|
+ hdr->ry = (data[8] << 4) | (data[6] & 0xf);
|
|
+ hdr->gx = (data[10] << 4) | ((data[9] & 0xf0) >> 4);
|
|
+ hdr->gy = (data[11] << 4) | (data[9] & 0xf);
|
|
+ hdr->bx = (data[13] << 4) | ((data[12] & 0xf0) >> 4);
|
|
+ hdr->by = (data[14] << 4) | (data[12] & 0xf);
|
|
+ hdr->wx = (data[16] << 4) | ((data[15] & 0xf0) >> 4);
|
|
+ hdr->wy = (data[17] << 4) | (data[15] & 0xf);
|
|
+}
|
|
+
|
|
+static void parse_ver_15_v1_data(struct ver_15_v1 *hdr, const u8 *data)
|
|
+{
|
|
+ hdr->yuv422_12bit = data[5] & BIT(0);
|
|
+ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1;
|
|
+ hdr->global_dimming = data[6] & BIT(0);
|
|
+
|
|
+ hdr->dm_version = (data[5] & 0x1c) >> 2;
|
|
+
|
|
+ hdr->colorimetry = data[7] & BIT(0);
|
|
+
|
|
+ hdr->t_max_lum = (data[6] & 0xfe) >> 1;
|
|
+ hdr->t_min_lum = (data[7] & 0xfe) >> 1;
|
|
+
|
|
+ hdr->rx = data[9];
|
|
+ hdr->ry = data[10];
|
|
+ hdr->gx = data[11];
|
|
+ hdr->gy = data[12];
|
|
+ hdr->bx = data[13];
|
|
+ hdr->by = data[14];
|
|
+}
|
|
+
|
|
+static void parse_ver_12_v1_data(struct ver_12_v1 *hdr, const u8 *data)
|
|
+{
|
|
+ hdr->yuv422_12bit = data[5] & BIT(0);
|
|
+ hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1;
|
|
+ hdr->global_dimming = data[6] & BIT(0);
|
|
+
|
|
+ hdr->dm_version = (data[5] & 0x1c) >> 2;
|
|
+
|
|
+ hdr->colorimetry = data[7] & BIT(0);
|
|
+
|
|
+ hdr->t_max_lum = (data[6] & 0xfe) >> 1;
|
|
+ hdr->t_min_lum = (data[7] & 0xfe) >> 1;
|
|
+
|
|
+ hdr->low_latency = data[8] & 0x3;
|
|
+
|
|
+ hdr->unique_rx = (data[11] & 0xf8) >> 3;
|
|
+ hdr->unique_ry = (data[11] & 0x7) << 2 | (data[10] & BIT(0)) << 1 |
|
|
+ (data[9] & BIT(0));
|
|
+ hdr->unique_gx = (data[9] & 0xfe) >> 1;
|
|
+ hdr->unique_gy = (data[10] & 0xfe) >> 1;
|
|
+ hdr->unique_bx = (data[8] & 0xe0) >> 5;
|
|
+ hdr->unique_by = (data[8] & 0x1c) >> 2;
|
|
+}
|
|
+
|
|
+static void parse_ver_12_v2_data(struct ver_12_v2 *hdr, const u8 *data)
|
|
+{
|
|
+ hdr->yuv422_12bit = data[5] & BIT(0);
|
|
+ hdr->backlt_ctrl = (data[5] & BIT(1)) >> 1;
|
|
+ hdr->global_dimming = (data[6] & BIT(2)) >> 2;
|
|
+
|
|
+ hdr->dm_version = (data[5] & 0x1c) >> 2;
|
|
+ hdr->backlt_min_luma = data[6] & 0x3;
|
|
+ hdr->interface = data[7] & 0x3;
|
|
+ hdr->yuv444_10b_12b = (data[8] & BIT(0)) << 1 | (data[9] & BIT(0));
|
|
+
|
|
+ hdr->t_min_pq_v2 = (data[6] & 0xf8) >> 3;
|
|
+ hdr->t_max_pq_v2 = (data[7] & 0xf8) >> 3;
|
|
+
|
|
+ hdr->unique_rx = (data[10] & 0xf8) >> 3;
|
|
+ hdr->unique_ry = (data[11] & 0xf8) >> 3;
|
|
+ hdr->unique_gx = (data[8] & 0xfe) >> 1;
|
|
+ hdr->unique_gy = (data[9] & 0xfe) >> 1;
|
|
+ hdr->unique_bx = data[10] & 0x7;
|
|
+ hdr->unique_by = data[11] & 0x7;
|
|
+}
|
|
+
|
|
+static
|
|
+void parse_next_hdr_block(struct next_hdr_sink_data *sink_data,
|
|
+ const u8 *next_hdr_db)
|
|
+{
|
|
+ int version;
|
|
+
|
|
+ version = check_next_hdr_version(next_hdr_db);
|
|
+ if (version < 0)
|
|
+ return;
|
|
+
|
|
+ sink_data->version = version;
|
|
+
|
|
+ switch (version) {
|
|
+ case VER_26_BYTE_V0:
|
|
+ parse_ver_26_v0_data(&sink_data->ver_26_v0, next_hdr_db);
|
|
+ break;
|
|
+ case VER_15_BYTE_V1:
|
|
+ parse_ver_15_v1_data(&sink_data->ver_15_v1, next_hdr_db);
|
|
+ break;
|
|
+ case VER_12_BYTE_V1:
|
|
+ parse_ver_12_v1_data(&sink_data->ver_12_v1, next_hdr_db);
|
|
+ break;
|
|
+ case VER_12_BYTE_V2:
|
|
+ parse_ver_12_v2_data(&sink_data->ver_12_v2, next_hdr_db);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap,
|
|
+ u8 *max_frl_rate_per_lane, u8 *max_lanes,
|
|
+ const struct edid *edid)
|
|
+{
|
|
+ const u8 *edid_ext;
|
|
+ int i, start, end;
|
|
+
|
|
+ if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid)
|
|
+ return -EINVAL;
|
|
+
|
|
+ edid_ext = find_cea_extension(edid);
|
|
+ if (!edid_ext)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (cea_db_offsets(edid_ext, &start, &end))
|
|
+ return -EINVAL;
|
|
+
|
|
+ for_each_cea_db(edid_ext, i, start, end) {
|
|
+ const u8 *db = &edid_ext[i];
|
|
+
|
|
+ if (cea_db_is_hdmi_forum_vsdb(db))
|
|
+ parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane,
|
|
+ max_lanes, db);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(rockchip_drm_parse_cea_ext);
|
|
+
|
|
+int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data,
|
|
+ const struct edid *edid)
|
|
+{
|
|
+ const u8 *edid_ext;
|
|
+ int i, start, end;
|
|
+
|
|
+ if (!sink_data || !edid)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memset(sink_data, 0, sizeof(struct next_hdr_sink_data));
|
|
+
|
|
+ edid_ext = find_cea_extension(edid);
|
|
+ if (!edid_ext)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (cea_db_offsets(edid_ext, &start, &end))
|
|
+ return -EINVAL;
|
|
+
|
|
+ for_each_cea_db(edid_ext, i, start, end) {
|
|
+ const u8 *db = &edid_ext[i];
|
|
+
|
|
+ if (cea_db_is_hdmi_next_hdr_block(db))
|
|
+ parse_next_hdr_block(sink_data, db);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(rockchip_drm_parse_next_hdr);
|
|
+
|
|
/*
|
|
* Attach a (component) device to the shared drm dma mapping from master drm
|
|
* device. This is used by the VOPs to map GEM buffers to a common DMA
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
|
|
index aeb03a572..581496f04 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
|
|
@@ -10,8 +10,11 @@
|
|
#define _ROCKCHIP_DRM_DRV_H
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
+#include <drm/display/drm_dsc.h>
|
|
+#include <drm/drm_fb_helper.h>
|
|
+#include <drm/drm_fourcc.h>
|
|
#include <drm/drm_gem.h>
|
|
-
|
|
+#include <drm/rockchip_drm.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/component.h>
|
|
@@ -19,25 +22,367 @@
|
|
#define ROCKCHIP_MAX_FB_BUFFER 3
|
|
#define ROCKCHIP_MAX_CONNECTOR 2
|
|
#define ROCKCHIP_MAX_CRTC 4
|
|
+#define ROCKCHIP_MAX_LAYER 16
|
|
|
|
struct drm_device;
|
|
struct drm_connector;
|
|
struct iommu_domain;
|
|
|
|
+#define VOP_COLOR_KEY_NONE (0 << 31)
|
|
+#define VOP_COLOR_KEY_MASK (1 << 31)
|
|
+
|
|
+#define VOP_OUTPUT_IF_RGB BIT(0)
|
|
+#define VOP_OUTPUT_IF_BT1120 BIT(1)
|
|
+#define VOP_OUTPUT_IF_BT656 BIT(2)
|
|
+#define VOP_OUTPUT_IF_LVDS0 BIT(3)
|
|
+#define VOP_OUTPUT_IF_LVDS1 BIT(4)
|
|
+#define VOP_OUTPUT_IF_MIPI0 BIT(5)
|
|
+#define VOP_OUTPUT_IF_MIPI1 BIT(6)
|
|
+#define VOP_OUTPUT_IF_eDP0 BIT(7)
|
|
+#define VOP_OUTPUT_IF_eDP1 BIT(8)
|
|
+#define VOP_OUTPUT_IF_DP0 BIT(9)
|
|
+#define VOP_OUTPUT_IF_DP1 BIT(10)
|
|
+#define VOP_OUTPUT_IF_HDMI0 BIT(11)
|
|
+#define VOP_OUTPUT_IF_HDMI1 BIT(12)
|
|
+
|
|
+#ifndef DRM_FORMAT_NV20
|
|
+#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */
|
|
+#endif
|
|
+
|
|
+#ifndef DRM_FORMAT_NV30
|
|
+#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */
|
|
+#endif
|
|
+
|
|
+#define RK_IF_PROP_COLOR_DEPTH "color_depth"
|
|
+#define RK_IF_PROP_COLOR_FORMAT "color_format"
|
|
+#define RK_IF_PROP_COLOR_DEPTH_CAPS "color_depth_caps"
|
|
+#define RK_IF_PROP_COLOR_FORMAT_CAPS "color_format_caps"
|
|
+
|
|
+enum rk_if_color_depth {
|
|
+ RK_IF_DEPTH_8,
|
|
+ RK_IF_DEPTH_10,
|
|
+ RK_IF_DEPTH_12,
|
|
+ RK_IF_DEPTH_16,
|
|
+ RK_IF_DEPTH_420_10,
|
|
+ RK_IF_DEPTH_420_12,
|
|
+ RK_IF_DEPTH_420_16,
|
|
+ RK_IF_DEPTH_6,
|
|
+ RK_IF_DEPTH_MAX,
|
|
+};
|
|
+
|
|
+enum rk_if_color_format {
|
|
+ RK_IF_FORMAT_RGB, /* default RGB */
|
|
+ RK_IF_FORMAT_YCBCR444, /* YCBCR 444 */
|
|
+ RK_IF_FORMAT_YCBCR422, /* YCBCR 422 */
|
|
+ RK_IF_FORMAT_YCBCR420, /* YCBCR 420 */
|
|
+ RK_IF_FORMAT_YCBCR_HQ, /* Highest subsampled YUV */
|
|
+ RK_IF_FORMAT_YCBCR_LQ, /* Lowest subsampled YUV */
|
|
+ RK_IF_FORMAT_MAX,
|
|
+};
|
|
+
|
|
+struct rockchip_drm_sub_dev {
|
|
+ struct list_head list;
|
|
+ struct drm_connector *connector;
|
|
+ struct device_node *of_node;
|
|
+ void (*loader_protect)(struct drm_encoder *encoder, bool on);
|
|
+ void (*oob_hotplug_event)(struct drm_connector *connector);
|
|
+};
|
|
+
|
|
+struct rockchip_sdr2hdr_state {
|
|
+ int sdr2hdr_func;
|
|
+
|
|
+ bool bt1886eotf_pre_conv_en;
|
|
+ bool rgb2rgb_pre_conv_en;
|
|
+ bool rgb2rgb_pre_conv_mode;
|
|
+ bool st2084oetf_pre_conv_en;
|
|
+
|
|
+ bool bt1886eotf_post_conv_en;
|
|
+ bool rgb2rgb_post_conv_en;
|
|
+ bool rgb2rgb_post_conv_mode;
|
|
+ bool st2084oetf_post_conv_en;
|
|
+};
|
|
+
|
|
+struct rockchip_hdr_state {
|
|
+ bool pre_overlay;
|
|
+ bool hdr2sdr_en;
|
|
+ struct rockchip_sdr2hdr_state sdr2hdr_state;
|
|
+};
|
|
+
|
|
+struct rockchip_bcsh_state {
|
|
+ int brightness;
|
|
+ int contrast;
|
|
+ int saturation;
|
|
+ int sin_hue;
|
|
+ int cos_hue;
|
|
+};
|
|
+
|
|
+struct rockchip_crtc {
|
|
+ struct drm_crtc crtc;
|
|
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
|
|
+ /**
|
|
+ * @vop_dump_status the status of vop dump control
|
|
+ * @vop_dump_list_head the list head of vop dump list
|
|
+ * @vop_dump_list_init_flag init once
|
|
+ * @vop_dump_times control the dump times
|
|
+ * @frme_count the frame of dump buf
|
|
+ */
|
|
+ enum vop_dump_status vop_dump_status;
|
|
+ struct list_head vop_dump_list_head;
|
|
+ bool vop_dump_list_init_flag;
|
|
+ int vop_dump_times;
|
|
+ int frame_count;
|
|
+#endif
|
|
+};
|
|
+
|
|
+struct rockchip_dsc_sink_cap {
|
|
+ /**
|
|
+ * @slice_width: the number of pixel columns that comprise the slice width
|
|
+ * @slice_height: the number of pixel rows that comprise the slice height
|
|
+ * @block_pred: Does block prediction
|
|
+ * @native_420: Does sink support DSC with 4:2:0 compression
|
|
+ * @bpc_supported: compressed bpc supported by sink : 10, 12 or 16 bpc
|
|
+ * @version_major: DSC major version
|
|
+ * @version_minor: DSC minor version
|
|
+ * @target_bits_per_pixel_x16: bits num after compress and multiply 16
|
|
+ */
|
|
+ u16 slice_width;
|
|
+ u16 slice_height;
|
|
+ bool block_pred;
|
|
+ bool native_420;
|
|
+ u8 bpc_supported;
|
|
+ u8 version_major;
|
|
+ u8 version_minor;
|
|
+ u16 target_bits_per_pixel_x16;
|
|
+};
|
|
+
|
|
struct rockchip_crtc_state {
|
|
struct drm_crtc_state base;
|
|
+ int vp_id;
|
|
int output_type;
|
|
int output_mode;
|
|
int output_bpc;
|
|
int output_flags;
|
|
bool enable_afbc;
|
|
+
|
|
+ //[CC:] vop2 related change
|
|
+ /**
|
|
+ * @splice_mode: enabled when display a hdisplay > 4096 on rk3588
|
|
+ */
|
|
+ bool splice_mode;
|
|
+
|
|
+ /**
|
|
+ * @hold_mode: enabled when it's:
|
|
+ * (1) mcu hold mode
|
|
+ * (2) mipi dsi cmd mode
|
|
+ * (3) edp psr mode
|
|
+ */
|
|
+ bool hold_mode;
|
|
+
|
|
+ struct drm_tv_connector_state *tv_state;
|
|
+ int left_margin;
|
|
+ int right_margin;
|
|
+ int top_margin;
|
|
+ int bottom_margin;
|
|
+ int vdisplay;
|
|
+ int afbdc_win_format;
|
|
+ int afbdc_win_width;
|
|
+ int afbdc_win_height;
|
|
+ int afbdc_win_ptr;
|
|
+ int afbdc_win_id;
|
|
+ int afbdc_en;
|
|
+ int afbdc_win_vir_width;
|
|
+ int afbdc_win_xoffset;
|
|
+ int afbdc_win_yoffset;
|
|
+ int dsp_layer_sel;
|
|
+ u32 output_if;
|
|
u32 bus_format;
|
|
u32 bus_flags;
|
|
+ int yuv_overlay;
|
|
+ int post_r2y_en;
|
|
+ int post_y2r_en;
|
|
+ int post_csc_mode;
|
|
+ int bcsh_en;
|
|
int color_space;
|
|
+ int eotf;
|
|
+ u32 background;
|
|
+ u32 line_flag;
|
|
+ u8 mode_update;
|
|
+ u8 dsc_id;
|
|
+
|
|
+ //[CC:] vop2 related changes
|
|
+ u8 dsc_enable;
|
|
+ unsigned long dsc_clk;
|
|
+
|
|
+ u8 dsc_slice_num;
|
|
+ u8 dsc_pixel_num;
|
|
+
|
|
+ u64 dsc_txp_clk_rate;
|
|
+ u64 dsc_pxl_clk_rate;
|
|
+ u64 dsc_cds_clk_rate;
|
|
+
|
|
+ struct drm_dsc_picture_parameter_set pps;
|
|
+ struct rockchip_dsc_sink_cap dsc_sink_cap;
|
|
+ struct rockchip_hdr_state hdr;
|
|
};
|
|
+
|
|
#define to_rockchip_crtc_state(s) \
|
|
container_of(s, struct rockchip_crtc_state, base)
|
|
|
|
+struct rockchip_drm_vcnt {
|
|
+ struct drm_pending_vblank_event *event;
|
|
+ __u32 sequence;
|
|
+ int pipe;
|
|
+};
|
|
+
|
|
+struct rockchip_logo {
|
|
+ dma_addr_t dma_addr;
|
|
+ struct drm_mm_node logo_reserved_node;
|
|
+ void *kvaddr;
|
|
+ phys_addr_t start;
|
|
+ phys_addr_t size;
|
|
+ int count;
|
|
+};
|
|
+
|
|
+struct loader_cubic_lut {
|
|
+ bool enable;
|
|
+ u32 offset;
|
|
+};
|
|
+
|
|
+struct rockchip_drm_dsc_cap {
|
|
+ bool v_1p2;
|
|
+ bool native_420;
|
|
+ bool all_bpp;
|
|
+ u8 bpc_supported;
|
|
+ u8 max_slices;
|
|
+ u8 max_lanes;
|
|
+ u8 max_frl_rate_per_lane;
|
|
+ u8 total_chunk_kbytes;
|
|
+ int clk_per_slice;
|
|
+};
|
|
+
|
|
+struct ver_26_v0 {
|
|
+ u8 yuv422_12bit;
|
|
+ u8 support_2160p_60;
|
|
+ u8 global_dimming;
|
|
+ u8 dm_major_ver;
|
|
+ u8 dm_minor_ver;
|
|
+ u16 t_min_pq;
|
|
+ u16 t_max_pq;
|
|
+ u16 rx;
|
|
+ u16 ry;
|
|
+ u16 gx;
|
|
+ u16 gy;
|
|
+ u16 bx;
|
|
+ u16 by;
|
|
+ u16 wx;
|
|
+ u16 wy;
|
|
+} __packed;
|
|
+
|
|
+struct ver_15_v1 {
|
|
+ u8 yuv422_12bit;
|
|
+ u8 support_2160p_60;
|
|
+ u8 global_dimming;
|
|
+ u8 dm_version;
|
|
+ u8 colorimetry;
|
|
+ u8 t_max_lum;
|
|
+ u8 t_min_lum;
|
|
+ u8 rx;
|
|
+ u8 ry;
|
|
+ u8 gx;
|
|
+ u8 gy;
|
|
+ u8 bx;
|
|
+ u8 by;
|
|
+} __packed;
|
|
+
|
|
+struct ver_12_v1 {
|
|
+ u8 yuv422_12bit;
|
|
+ u8 support_2160p_60;
|
|
+ u8 global_dimming;
|
|
+ u8 dm_version;
|
|
+ u8 colorimetry;
|
|
+ u8 low_latency;
|
|
+ u8 t_max_lum;
|
|
+ u8 t_min_lum;
|
|
+ u8 unique_rx;
|
|
+ u8 unique_ry;
|
|
+ u8 unique_gx;
|
|
+ u8 unique_gy;
|
|
+ u8 unique_bx;
|
|
+ u8 unique_by;
|
|
+} __packed;
|
|
+
|
|
+struct ver_12_v2 {
|
|
+ u8 yuv422_12bit;
|
|
+ u8 backlt_ctrl;
|
|
+ u8 global_dimming;
|
|
+ u8 dm_version;
|
|
+ u8 backlt_min_luma;
|
|
+ u8 interface;
|
|
+ u8 yuv444_10b_12b;
|
|
+ u8 t_min_pq_v2;
|
|
+ u8 t_max_pq_v2;
|
|
+ u8 unique_rx;
|
|
+ u8 unique_ry;
|
|
+ u8 unique_gx;
|
|
+ u8 unique_gy;
|
|
+ u8 unique_bx;
|
|
+ u8 unique_by;
|
|
+} __packed;
|
|
+
|
|
+struct next_hdr_sink_data {
|
|
+ u8 version;
|
|
+ struct ver_26_v0 ver_26_v0;
|
|
+ struct ver_15_v1 ver_15_v1;
|
|
+ struct ver_12_v1 ver_12_v1;
|
|
+ struct ver_12_v2 ver_12_v2;
|
|
+} __packed;
|
|
+
|
|
+//[CC:] drop struct dmcfreq_vop_info
|
|
+struct dmcfreq_vop_info;
|
|
+
|
|
+/*
|
|
+ * Rockchip drm private crtc funcs.
|
|
+ * @loader_protect: protect loader logo crtc's power
|
|
+ * @enable_vblank: enable crtc vblank irq.
|
|
+ * @disable_vblank: disable crtc vblank irq.
|
|
+ * @bandwidth: report present crtc bandwidth consume.
|
|
+ * @cancel_pending_vblank: cancel pending vblank.
|
|
+ * @debugfs_init: init crtc debugfs.
|
|
+ * @debugfs_dump: debugfs to dump crtc and plane state.
|
|
+ * @regs_dump: dump vop current register config.
|
|
+ * @mode_valid: verify that the current mode is supported.
|
|
+ * @crtc_close: close vop.
|
|
+ * @crtc_send_mcu_cmd: send mcu panel init cmd.
|
|
+ * @te_handler: soft te hand for cmd mode panel.
|
|
+ * @wait_vact_end: wait the last active line.
|
|
+ */
|
|
+struct rockchip_crtc_funcs {
|
|
+ int (*loader_protect)(struct drm_crtc *crtc, bool on);
|
|
+ int (*enable_vblank)(struct drm_crtc *crtc);
|
|
+ void (*disable_vblank)(struct drm_crtc *crtc);
|
|
+ size_t (*bandwidth)(struct drm_crtc *crtc,
|
|
+ struct drm_crtc_state *crtc_state,
|
|
+ struct dmcfreq_vop_info *vop_bw_info);
|
|
+ void (*cancel_pending_vblank)(struct drm_crtc *crtc,
|
|
+ struct drm_file *file_priv);
|
|
+ int (*debugfs_init)(struct drm_minor *minor, struct drm_crtc *crtc);
|
|
+ int (*debugfs_dump)(struct drm_crtc *crtc, struct seq_file *s);
|
|
+ void (*regs_dump)(struct drm_crtc *crtc, struct seq_file *s);
|
|
+ enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc,
|
|
+ const struct drm_display_mode *mode,
|
|
+ int output_type);
|
|
+ void (*crtc_close)(struct drm_crtc *crtc);
|
|
+ void (*crtc_send_mcu_cmd)(struct drm_crtc *crtc, u32 type, u32 value);
|
|
+ void (*te_handler)(struct drm_crtc *crtc);
|
|
+ int (*wait_vact_end)(struct drm_crtc *crtc, unsigned int mstimeout);
|
|
+ void (*crtc_standby)(struct drm_crtc *crtc, bool standby);
|
|
+};
|
|
+
|
|
+struct rockchip_dclk_pll {
|
|
+ struct clk *pll;
|
|
+ unsigned int use_count;
|
|
+};
|
|
+
|
|
/*
|
|
* Rockchip drm private structure.
|
|
*
|
|
@@ -46,10 +391,56 @@ struct rockchip_crtc_state {
|
|
* @mm_lock: protect drm_mm on multi-threads.
|
|
*/
|
|
struct rockchip_drm_private {
|
|
+ struct rockchip_logo *logo;
|
|
+ struct drm_fb_helper *fbdev_helper;
|
|
+ struct drm_gem_object *fbdev_bo;
|
|
struct iommu_domain *domain;
|
|
+ struct gen_pool *secure_buffer_pool;
|
|
struct device *iommu_dev;
|
|
struct mutex mm_lock;
|
|
struct drm_mm mm;
|
|
+ struct list_head psr_list;
|
|
+ struct mutex psr_list_lock;
|
|
+ struct mutex commit_lock;
|
|
+
|
|
+ /* private crtc prop */
|
|
+ struct drm_property *soc_id_prop;
|
|
+ struct drm_property *port_id_prop;
|
|
+ struct drm_property *aclk_prop;
|
|
+ struct drm_property *bg_prop;
|
|
+ struct drm_property *line_flag_prop;
|
|
+
|
|
+ /* private plane prop */
|
|
+ struct drm_property *eotf_prop;
|
|
+ struct drm_property *color_space_prop;
|
|
+ struct drm_property *async_commit_prop;
|
|
+ struct drm_property *share_id_prop;
|
|
+
|
|
+ /* private connector prop */
|
|
+ struct drm_property *connector_id_prop;
|
|
+
|
|
+ const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
|
|
+
|
|
+ struct rockchip_dclk_pll default_pll;
|
|
+ struct rockchip_dclk_pll hdmi_pll;
|
|
+
|
|
+ /*
|
|
+ * protect some shared overlay resource
|
|
+ * OVL_LAYER_SEL/OVL_PORT_SEL
|
|
+ */
|
|
+ struct mutex ovl_lock;
|
|
+
|
|
+ struct rockchip_drm_vcnt vcnt[ROCKCHIP_MAX_CRTC];
|
|
+ /**
|
|
+ * @loader_protect
|
|
+ * ignore restore_fbdev_mode_atomic when in logo on state
|
|
+ */
|
|
+ bool loader_protect;
|
|
+
|
|
+ dma_addr_t cubic_lut_dma_addr;
|
|
+ void *cubic_lut_kvaddr;
|
|
+ struct drm_mm_node *clut_reserved_node;
|
|
+ struct loader_cubic_lut cubic_lut[ROCKCHIP_MAX_CRTC];
|
|
};
|
|
|
|
struct rockchip_encoder {
|
|
@@ -66,16 +457,52 @@ void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
|
|
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
|
|
int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
|
|
struct device_node *np, int port, int reg);
|
|
+
|
|
+int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
|
|
+ const struct rockchip_crtc_funcs *crtc_funcs);
|
|
+void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc);
|
|
+void rockchip_drm_crtc_standby(struct drm_crtc *crtc, bool standby);
|
|
+
|
|
+void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev);
|
|
+void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev);
|
|
+struct rockchip_drm_sub_dev *rockchip_drm_get_sub_dev(struct device_node *node);
|
|
+int rockchip_drm_add_modes_noedid(struct drm_connector *connector);
|
|
+void rockchip_drm_te_handle(struct drm_crtc *crtc);
|
|
+void drm_mode_convert_to_split_mode(struct drm_display_mode *mode);
|
|
+void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode);
|
|
+#if IS_REACHABLE(CONFIG_DRM_ROCKCHIP)
|
|
+int rockchip_drm_get_sub_dev_type(void);
|
|
+#else
|
|
+static inline int rockchip_drm_get_sub_dev_type(void)
|
|
+{
|
|
+ return DRM_MODE_CONNECTOR_Unknown;
|
|
+}
|
|
+#endif
|
|
+
|
|
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
|
|
+uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info);
|
|
+int rockchip_drm_get_yuv422_format(struct drm_connector *connector,
|
|
+ struct edid *edid);
|
|
+int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap,
|
|
+ u8 *max_frl_rate_per_lane, u8 *max_lanes,
|
|
+ const struct edid *edid);
|
|
+int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data,
|
|
+ const struct edid *edid);
|
|
+
|
|
extern struct platform_driver cdn_dp_driver;
|
|
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
|
|
extern struct platform_driver dw_mipi_dsi_rockchip_driver;
|
|
+extern struct platform_driver dw_mipi_dsi2_rockchip_driver;
|
|
extern struct platform_driver inno_hdmi_driver;
|
|
extern struct platform_driver rockchip_dp_driver;
|
|
extern struct platform_driver rockchip_lvds_driver;
|
|
extern struct platform_driver vop_platform_driver;
|
|
-extern struct platform_driver rk3066_hdmi_driver;
|
|
extern struct platform_driver vop2_platform_driver;
|
|
+extern struct platform_driver rk3066_hdmi_driver;
|
|
+extern struct platform_driver rockchip_rgb_driver;
|
|
+extern struct platform_driver dw_dp_driver;
|
|
+extern struct platform_driver vconn_platform_driver;
|
|
+extern struct platform_driver vvop_platform_driver;
|
|
|
|
static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder)
|
|
{
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
|
|
index 4b2daefeb..6ebea5c36 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
|
|
@@ -15,6 +15,16 @@
|
|
#define VOP_MAJOR(version) ((version) >> 8)
|
|
#define VOP_MINOR(version) ((version) & 0xff)
|
|
|
|
+//[CC:] vop2 related changes
|
|
+#define VOP_VERSION_RK3568 VOP_VERSION(0x40, 0x15)
|
|
+#define VOP_VERSION_RK3588 VOP_VERSION(0x40, 0x17)
|
|
+
|
|
+#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE BIT(0)
|
|
+#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE BIT(1)
|
|
+#define ROCKCHIP_OUTPUT_DATA_SWAP BIT(2)
|
|
+/* MIPI DSI DataStream(cmd) mode on rk3588 */
|
|
+#define ROCKCHIP_OUTPUT_MIPI_DS_MODE BIT(3)
|
|
+
|
|
#define NUM_YUV2YUV_COEFFICIENTS 12
|
|
|
|
/* AFBC supports a number of configurable modes. Relevant to us is block size
|
|
@@ -280,11 +290,16 @@ struct vop_data {
|
|
/*
|
|
* display output interface supported by rockchip lcdc
|
|
*/
|
|
-#define ROCKCHIP_OUT_MODE_P888 0
|
|
-#define ROCKCHIP_OUT_MODE_P666 1
|
|
-#define ROCKCHIP_OUT_MODE_P565 2
|
|
+#define ROCKCHIP_OUT_MODE_P888 0
|
|
+#define ROCKCHIP_OUT_MODE_BT1120 0
|
|
+#define ROCKCHIP_OUT_MODE_P666 1
|
|
+#define ROCKCHIP_OUT_MODE_P565 2
|
|
+#define ROCKCHIP_OUT_MODE_BT656 5
|
|
+#define ROCKCHIP_OUT_MODE_S888 8
|
|
+#define ROCKCHIP_OUT_MODE_S888_DUMMY 12
|
|
+#define ROCKCHIP_OUT_MODE_YUV420 14
|
|
/* for use special outface */
|
|
-#define ROCKCHIP_OUT_MODE_AAAA 15
|
|
+#define ROCKCHIP_OUT_MODE_AAAA 15
|
|
|
|
/* output flags */
|
|
#define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0)
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
|
|
index 6862fb146..a073d1d5c 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>
|
|
@@ -17,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 +162,8 @@ struct vop2_video_port {
|
|
struct drm_crtc crtc;
|
|
struct vop2 *vop2;
|
|
struct clk *dclk;
|
|
+ struct reset_control *dclk_rst;
|
|
+ struct clk *dclk_parent;
|
|
unsigned int id;
|
|
const struct vop2_video_port_data *data;
|
|
|
|
@@ -191,6 +196,8 @@ struct vop2 {
|
|
struct regmap *map;
|
|
|
|
struct regmap *grf;
|
|
+ struct regmap *vop_grf;
|
|
+ struct regmap *vo1_grf;
|
|
|
|
/* physical map length of vop2 register */
|
|
u32 len;
|
|
@@ -209,14 +216,43 @@ struct vop2 {
|
|
unsigned int enable_count;
|
|
struct clk *hclk;
|
|
struct clk *aclk;
|
|
+ struct clk *pclk;
|
|
+ // [CC:] handle all display modes
|
|
+ struct clk *hdmi0_phy_pll;
|
|
+ struct reset_control *ahb_rst;
|
|
+ struct reset_control *axi_rst;
|
|
|
|
/* optional internal rgb encoder */
|
|
struct rockchip_rgb *rgb;
|
|
|
|
+ /* list_head of internal clk */
|
|
+ struct list_head clk_list_head;
|
|
+
|
|
/* must be put at the end of the struct */
|
|
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)
|
|
+#define vop2_output_if_is_dp(x) (x == ROCKCHIP_VOP2_EP_DP0 || x == ROCKCHIP_VOP2_EP_DP1)
|
|
+#define vop2_output_if_is_edp(x) (x == ROCKCHIP_VOP2_EP_EDP0 || x == ROCKCHIP_VOP2_EP_EDP1)
|
|
+#define vop2_output_if_is_mipi(x) (x == ROCKCHIP_VOP2_EP_MIPI0 || x == ROCKCHIP_VOP2_EP_MIPI1)
|
|
+#define vop2_output_if_is_lvds(x) (x == ROCKCHIP_VOP2_EP_LVDS0 || x == ROCKCHIP_VOP2_EP_LVDS1)
|
|
+#define vop2_output_if_is_dpi(x) (x == ROCKCHIP_VOP2_EP_RGB0)
|
|
+
|
|
static struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
|
|
{
|
|
return container_of(crtc, struct vop2_video_port, crtc);
|
|
@@ -269,9 +305,16 @@ static bool vop2_cluster_window(const struct vop2_win *win)
|
|
static void vop2_cfg_done(struct vop2_video_port *vp)
|
|
{
|
|
struct vop2 *vop2 = vp->vop2;
|
|
+ unsigned int bits = BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN;
|
|
|
|
- regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE,
|
|
- BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
|
|
+ if (vop2->data->soc_id == 3588) {
|
|
+ bits |= BIT(vp->id) << 16;
|
|
+ // [CC:] handle splice_mode
|
|
+ // if (vcstate->splice_mode)
|
|
+ // bits |= BIT(vp_data->splice_vp_id) | (BIT(vp_data->splice_vp_id) << 16);
|
|
+ }
|
|
+
|
|
+ regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, bits);
|
|
}
|
|
|
|
static void vop2_win_disable(struct vop2_win *win)
|
|
@@ -846,16 +889,36 @@ static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
|
|
ret = clk_prepare_enable(vop2->aclk);
|
|
if (ret < 0) {
|
|
drm_err(vop2->drm, "failed to enable aclk - %d\n", ret);
|
|
- goto err;
|
|
+ goto err_aclk;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(vop2->pclk);
|
|
+ if (ret < 0) {
|
|
+ drm_err(vop2->drm, "failed to enable pclk - %d\n", ret);
|
|
+ goto err_pclk;
|
|
}
|
|
|
|
return 0;
|
|
-err:
|
|
+
|
|
+err_pclk:
|
|
+ clk_disable_unprepare(vop2->aclk);
|
|
+err_aclk:
|
|
clk_disable_unprepare(vop2->hclk);
|
|
|
|
return ret;
|
|
}
|
|
|
|
+static void vop2_power_domain_all_on(struct vop2 *vop2)
|
|
+{
|
|
+ u32 pd;
|
|
+
|
|
+ pd = vop2_readl(vop2, RK3588_SYS_PD_CTRL);
|
|
+ pd |= VOP2_PD_CLUSTER0 | VOP2_PD_CLUSTER1 | VOP2_PD_CLUSTER2 |
|
|
+ VOP2_PD_CLUSTER3 | VOP2_PD_ESMART;
|
|
+
|
|
+ vop2_writel(vop2, RK3588_SYS_PD_CTRL, pd);
|
|
+}
|
|
+
|
|
static void vop2_enable(struct vop2 *vop2)
|
|
{
|
|
int ret;
|
|
@@ -883,6 +946,9 @@ static void vop2_enable(struct vop2 *vop2)
|
|
if (vop2->data->soc_id == 3566)
|
|
vop2_writel(vop2, RK3568_OTP_WIN_EN, 1);
|
|
|
|
+ if (vop2->data->soc_id == 3588)
|
|
+ vop2_power_domain_all_on(vop2);
|
|
+
|
|
vop2_writel(vop2, RK3568_REG_CFG_DONE, RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
|
|
|
|
/*
|
|
@@ -910,63 +976,11 @@ static void vop2_disable(struct vop2 *vop2)
|
|
|
|
regcache_mark_dirty(vop2->map);
|
|
|
|
+ clk_disable_unprepare(vop2->pclk);
|
|
clk_disable_unprepare(vop2->aclk);
|
|
clk_disable_unprepare(vop2->hclk);
|
|
}
|
|
|
|
-static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
|
|
- struct drm_atomic_state *state)
|
|
-{
|
|
- struct vop2_video_port *vp = to_vop2_video_port(crtc);
|
|
- struct vop2 *vop2 = vp->vop2;
|
|
- struct drm_crtc_state *old_crtc_state;
|
|
- int ret;
|
|
-
|
|
- vop2_lock(vop2);
|
|
-
|
|
- old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
|
|
- drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
|
|
-
|
|
- drm_crtc_vblank_off(crtc);
|
|
-
|
|
- /*
|
|
- * Vop standby will take effect at end of current frame,
|
|
- * if dsp hold valid irq happen, it means standby complete.
|
|
- *
|
|
- * we must wait standby complete when we want to disable aclk,
|
|
- * if not, memory bus maybe dead.
|
|
- */
|
|
- reinit_completion(&vp->dsp_hold_completion);
|
|
-
|
|
- vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
|
|
-
|
|
- vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
|
|
-
|
|
- ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
|
|
- msecs_to_jiffies(50));
|
|
- if (!ret)
|
|
- drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
|
|
-
|
|
- vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
|
|
-
|
|
- clk_disable_unprepare(vp->dclk);
|
|
-
|
|
- vop2->enable_count--;
|
|
-
|
|
- if (!vop2->enable_count)
|
|
- vop2_disable(vop2);
|
|
-
|
|
- vop2_unlock(vop2);
|
|
-
|
|
- if (crtc->state->event && !crtc->state->active) {
|
|
- spin_lock_irq(&crtc->dev->event_lock);
|
|
- drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
|
- spin_unlock_irq(&crtc->dev->event_lock);
|
|
-
|
|
- crtc->state->event = NULL;
|
|
- }
|
|
-}
|
|
-
|
|
static int vop2_plane_atomic_check(struct drm_plane *plane,
|
|
struct drm_atomic_state *astate)
|
|
{
|
|
@@ -1004,6 +1018,7 @@ static int vop2_plane_atomic_check(struct drm_plane *plane,
|
|
if (!pstate->visible)
|
|
return 0;
|
|
|
|
+ // [CC:] Drop format var since it's not used anywhere, use ret var instead
|
|
format = vop2_convert_format(fb->format->format);
|
|
if (format < 0)
|
|
return format;
|
|
@@ -1027,6 +1042,35 @@ static int vop2_plane_atomic_check(struct drm_plane *plane,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ // [CC:] do we need the checks below?
|
|
+ bool afbc_en = rockchip_afbc(plane, fb->modifier);
|
|
+ struct vop2_win *win = to_vop2_win(plane);
|
|
+
|
|
+ /*
|
|
+ * This is special feature at rk356x, the cluster layer only can support
|
|
+ * afbc format and can't support linear format;
|
|
+ */
|
|
+ if (vp->vop2->data->soc_id == 3568) {
|
|
+ if (vop2_cluster_window(win) && !afbc_en) {
|
|
+ DRM_ERROR("Unsupported linear format at %s\n", win->data->name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (vp->vop2->data->soc_id > 3568) {
|
|
+ if (vop2_cluster_window(win) && !afbc_en && fb->format->is_yuv) {
|
|
+ DRM_ERROR("Unsupported linear yuv format at %s\n", win->data->name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (vop2_cluster_window(win) && !afbc_en &&
|
|
+ (win->data->supported_rotations & pstate->rotation)) {
|
|
+ DRM_ERROR("Unsupported linear rotation(%d) format at %s\n",
|
|
+ pstate->rotation, win->data->name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
/*
|
|
* Src.x1 can be odd when do clip, but yuv plane start point
|
|
* need align with 2 pixel.
|
|
@@ -1107,6 +1151,7 @@ static void vop2_plane_setup_color_key(struct drm_plane *plane, u32 color_key)
|
|
vop2_win_write(win, VOP2_WIN_COLOR_KEY, (r << 20) | (g << 10) | b);
|
|
}
|
|
|
|
+// [CC:] fix BUG: sleeping function called from invalid context
|
|
static void vop2_plane_atomic_update(struct drm_plane *plane,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
@@ -1272,7 +1317,12 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
|
|
vop2_win_write(win, VOP2_WIN_AFBC_ENABLE, 1);
|
|
vop2_win_write(win, VOP2_WIN_AFBC_FORMAT, afbc_format);
|
|
vop2_win_write(win, VOP2_WIN_AFBC_UV_SWAP, uv_swap);
|
|
- vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
|
|
+
|
|
+ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568)
|
|
+ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 0);
|
|
+ else
|
|
+ vop2_win_write(win, VOP2_WIN_AFBC_AUTO_GATING_EN, 1);
|
|
+
|
|
vop2_win_write(win, VOP2_WIN_AFBC_BLOCK_SPLIT_EN, 0);
|
|
if (pstate->rotation & (DRM_MODE_ROTATE_270 | DRM_MODE_ROTATE_90)) {
|
|
vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, 0);
|
|
@@ -1376,9 +1426,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;
|
|
}
|
|
|
|
@@ -1462,7 +1533,7 @@ static void vop2_post_config(struct drm_crtc *crtc)
|
|
}
|
|
|
|
static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
|
|
- u32 polflags)
|
|
+ u32 polflags, u32 invpolflags)
|
|
{
|
|
struct vop2 *vop2 = vp->vop2;
|
|
u32 die, dip;
|
|
@@ -1477,6 +1548,7 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
|
|
FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
|
|
dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
|
|
dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
|
|
+ // [CC:] use .grf_dclk_inv = VOP_REG(RK3588_GRF_SOC_CON1, 0x1, 14) for 3588 variant
|
|
if (polflags & POLFLAG_DCLK_INV)
|
|
regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
|
|
else
|
|
@@ -1487,8 +1559,18 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
|
|
die |= RK3568_SYS_DSP_INFACE_EN_HDMI |
|
|
FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_HDMI_MUX, vp->id);
|
|
dip &= ~RK3568_DSP_IF_POL__HDMI_PIN_POL;
|
|
- dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, polflags);
|
|
+ dip |= FIELD_PREP(RK3568_DSP_IF_POL__HDMI_PIN_POL, invpolflags);
|
|
+
|
|
+ /* grf_hdmi0_en */
|
|
+ if (vop2->vop_grf)
|
|
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, BIT(1 + 16) | BIT(1));
|
|
+ /* hdmi0_pin_pol */
|
|
+ if (vop2->vo1_grf)
|
|
+ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0,
|
|
+ RK3588_GRF_VO1_CON0__HDMI0_PIN_POL << 16 |
|
|
+ FIELD_PREP(RK3588_GRF_VO1_CON0__HDMI0_PIN_POL, invpolflags));
|
|
break;
|
|
+ // [CC:] TODO: ROCKCHIP_VOP2_EP_HDMI1
|
|
case ROCKCHIP_VOP2_EP_EDP0:
|
|
die &= ~RK3568_SYS_DSP_INFACE_EN_EDP_MUX;
|
|
die |= RK3568_SYS_DSP_INFACE_EN_EDP |
|
|
@@ -1535,11 +1617,354 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
|
|
vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
|
|
}
|
|
|
|
+/*
|
|
+ * calc the dclk on rk3588
|
|
+ * the available div of dclk is 1, 2, 4
|
|
+ */
|
|
+static unsigned long vop2_calc_dclk(unsigned long child_clk, unsigned long max_dclk)
|
|
+{
|
|
+ if (child_clk * 4 <= max_dclk)
|
|
+ return child_clk * 4;
|
|
+ else if (child_clk * 2 <= max_dclk)
|
|
+ return child_clk * 2;
|
|
+ else if (child_clk <= max_dclk)
|
|
+ return child_clk;
|
|
+ else
|
|
+ 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
|
|
+ * DP: dp_pixclk = dclk_out <= dclk_core
|
|
+ * DSI: mipi_pixclk <= dclk_out <= dclk_core
|
|
+ */
|
|
+static unsigned long vop2_calc_cru_cfg(struct vop2_video_port *vp, int id,
|
|
+ int *dclk_core_div, int *dclk_out_div,
|
|
+ int *if_pixclk_div, int *if_dclk_div)
|
|
+{
|
|
+ struct vop2 *vop2 = vp->vop2;
|
|
+ struct drm_crtc *crtc = &vp->crtc;
|
|
+ struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
|
|
+ struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
|
|
+ int output_mode = vcstate->output_mode;
|
|
+ unsigned long v_pixclk = adjusted_mode->crtc_clock * 1000LL; /* video timing pixclk */
|
|
+ unsigned long dclk_core_rate = v_pixclk >> 2;
|
|
+ unsigned long dclk_rate = v_pixclk;
|
|
+ unsigned long dclk_out_rate;
|
|
+ unsigned long if_dclk_rate;
|
|
+ unsigned long if_pixclk_rate;
|
|
+ int K = 1;
|
|
+
|
|
+ if (vop2_output_if_is_hdmi(id)) {
|
|
+ if (vop2->data->soc_id == 3588 && id == ROCKCHIP_VOP2_EP_HDMI0) {
|
|
+ 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
|
|
+ */
|
|
+ if (output_mode == ROCKCHIP_OUT_MODE_YUV420) {
|
|
+ dclk_rate = dclk_rate >> 1;
|
|
+ K = 2;
|
|
+ }
|
|
+
|
|
+ if_pixclk_rate = (dclk_core_rate << 1) / K;
|
|
+ if_dclk_rate = dclk_core_rate / K;
|
|
+
|
|
+ *if_pixclk_div = dclk_rate / if_pixclk_rate;
|
|
+ *if_dclk_div = dclk_rate / if_dclk_rate;
|
|
+ *dclk_core_div = dclk_rate / dclk_core_rate;
|
|
+ } else if (vop2_output_if_is_edp(id)) {
|
|
+ /* edp_pixclk = edp_dclk > dclk_core */
|
|
+ if_pixclk_rate = v_pixclk / K;
|
|
+ if_dclk_rate = v_pixclk / K;
|
|
+ dclk_rate = if_pixclk_rate * K;
|
|
+ *dclk_core_div = dclk_rate / dclk_core_rate;
|
|
+ *if_pixclk_div = dclk_rate / if_pixclk_rate;
|
|
+ *if_dclk_div = *if_pixclk_div;
|
|
+ } else if (vop2_output_if_is_dp(id)) {
|
|
+ if (output_mode == ROCKCHIP_OUT_MODE_YUV420)
|
|
+ dclk_out_rate = v_pixclk >> 3;
|
|
+ else
|
|
+ dclk_out_rate = v_pixclk >> 2;
|
|
+
|
|
+ dclk_rate = vop2_calc_dclk(dclk_out_rate, 600000);
|
|
+ if (!dclk_rate) {
|
|
+ drm_err(vop2->drm, "DP dclk_out_rate out of range(max_dclk: 600 KHZ, dclk_out_rate: %ld KHZ)\n",
|
|
+ dclk_out_rate);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ *dclk_out_div = dclk_rate / dclk_out_rate;
|
|
+ *dclk_core_div = dclk_rate / dclk_core_rate;
|
|
+ } else if (vop2_output_if_is_mipi(id)) {
|
|
+ if_pixclk_rate = dclk_core_rate / K;
|
|
+ /* dclk_core = dclk_out * K = if_pixclk * K = v_pixclk / 4 */
|
|
+ dclk_out_rate = if_pixclk_rate;
|
|
+ /* dclk_rate = N * dclk_core_rate N = (1,2,4 ), we get a little factor here */
|
|
+ dclk_rate = vop2_calc_dclk(dclk_out_rate, 600000);
|
|
+ if (!dclk_rate) {
|
|
+ drm_err(vop2->drm, "MIPI dclk out of range(max_dclk: 600 KHZ, dclk_out_rate: %ld KHZ)\n",
|
|
+ dclk_out_rate);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ *dclk_out_div = dclk_rate / dclk_out_rate;
|
|
+ *dclk_core_div = dclk_rate / dclk_core_rate;
|
|
+ *if_pixclk_div = 1; /*mipi pixclk == dclk_out*/
|
|
+ } else if (vop2_output_if_is_dpi(id)) {
|
|
+ dclk_rate = v_pixclk;
|
|
+ *dclk_core_div = dclk_rate / dclk_core_rate;
|
|
+ }
|
|
+
|
|
+ *if_pixclk_div = ilog2(*if_pixclk_div);
|
|
+ *if_dclk_div = ilog2(*if_dclk_div);
|
|
+ *dclk_core_div = ilog2(*dclk_core_div);
|
|
+ *dclk_out_div = ilog2(*dclk_out_div);
|
|
+
|
|
+ drm_dbg(vop2->drm, "dclk:%ld,if_pixclk_div;%d,if_dclk_div:%d\n", dclk_rate, *if_pixclk_div, *if_dclk_div);
|
|
+
|
|
+ return dclk_rate;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * MIPI port mux on rk3588:
|
|
+ * 0: Video Port2
|
|
+ * 1: Video Port3
|
|
+ * 3: Video Port 1(MIPI1 only)
|
|
+ */
|
|
+static u32 rk3588_get_mipi_port_mux(int vp_id)
|
|
+{
|
|
+ if (vp_id == 1)
|
|
+ return 3;
|
|
+ else if (vp_id == 3)
|
|
+ return 1;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u32 rk3588_get_hdmi_pol(u32 flags)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = (flags & DRM_MODE_FLAG_NHSYNC) ? BIT(HSYNC_POSITIVE) : 0;
|
|
+ val |= (flags & DRM_MODE_FLAG_NVSYNC) ? BIT(VSYNC_POSITIVE) : 0;
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void rk3588_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
|
|
+{
|
|
+ struct vop2 *vop2 = vp->vop2;
|
|
+ int dclk_core_div, dclk_out_div, if_pixclk_div, if_dclk_div;
|
|
+ u32 die, dip, div, vp_clk_div, val;
|
|
+
|
|
+ vop2_calc_cru_cfg(vp, id, &dclk_core_div, &dclk_out_div, &if_pixclk_div, &if_dclk_div);
|
|
+
|
|
+ vp_clk_div = FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_CORE_DIV, dclk_core_div);
|
|
+ vp_clk_div |= FIELD_PREP(RK3588_VP_CLK_CTRL__DCLK_OUT_DIV, dclk_out_div);
|
|
+
|
|
+ die = vop2_readl(vop2, RK3568_DSP_IF_EN);
|
|
+ dip = vop2_readl(vop2, RK3568_DSP_IF_POL);
|
|
+ div = vop2_readl(vop2, RK3568_DSP_IF_CTRL);
|
|
+
|
|
+ switch (id) {
|
|
+ case ROCKCHIP_VOP2_EP_HDMI0:
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX;
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_HDMI0 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id);
|
|
+ val = rk3588_get_hdmi_pol(polflags);
|
|
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 1, 1));
|
|
+ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 6, 5));
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_HDMI1:
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV, if_dclk_div);
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV, if_pixclk_div);
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX;
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_HDMI1 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id);
|
|
+ val = rk3588_get_hdmi_pol(polflags);
|
|
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 4, 4));
|
|
+ regmap_write(vop2->vo1_grf, RK3588_GRF_VO1_CON0, HIWORD_UPDATE(val, 8, 7));
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_EDP0:
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX;
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_EDP0 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX, vp->id);
|
|
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 0, 0));
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_EDP1:
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV, if_dclk_div);
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV, if_pixclk_div);
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX;
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_EDP1 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX, vp->id);
|
|
+ regmap_write(vop2->vop_grf, RK3588_GRF_VOP_CON2, HIWORD_UPDATE(1, 3, 3));
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_MIPI0:
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_MIPI0_PCLK_DIV, if_pixclk_div);
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX;
|
|
+ val = rk3588_get_mipi_port_mux(vp->id);
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_MIPI0 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX, !!val);
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_MIPI1:
|
|
+ div |= FIELD_PREP(RK3588_DSP_IF_MIPI1_PCLK_DIV, if_pixclk_div);
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX;
|
|
+ val = rk3588_get_mipi_port_mux(vp->id);
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, val);
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_DP0:
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_DP0_MUX;
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_DP0 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_DP0_MUX, vp->id);
|
|
+ dip &= ~RK3588_DSP_IF_POL__DP0_PIN_POL;
|
|
+ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP0_PIN_POL, polflags);
|
|
+ break;
|
|
+ case ROCKCHIP_VOP2_EP_DP1:
|
|
+ die &= ~RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX;
|
|
+ die |= RK3588_SYS_DSP_INFACE_EN_MIPI1 |
|
|
+ FIELD_PREP(RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX, vp->id);
|
|
+ dip &= ~RK3588_DSP_IF_POL__DP1_PIN_POL;
|
|
+ dip |= FIELD_PREP(RK3588_DSP_IF_POL__DP1_PIN_POL, polflags);
|
|
+ break;
|
|
+ default:
|
|
+ drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD;
|
|
+
|
|
+ vop2_vp_write(vp, RK3588_VP_CLK_CTRL, vp_clk_div);
|
|
+ vop2_writel(vop2, RK3568_DSP_IF_EN, die);
|
|
+ vop2_writel(vop2, RK3568_DSP_IF_CTRL, div);
|
|
+ vop2_writel(vop2, RK3568_DSP_IF_POL, dip);
|
|
+}
|
|
+
|
|
+static void vop2_set_intf_mux(struct vop2_video_port *vp, int ep_id, u32 polflags)
|
|
+{
|
|
+ struct vop2 *vop2 = vp->vop2;
|
|
+
|
|
+ if (vop2->data->soc_id == 3566 || vop2->data->soc_id == 3568) {
|
|
+ // [CC:] drop 2nd polflags arg
|
|
+ rk3568_set_intf_mux(vp, ep_id, polflags, polflags);
|
|
+ } else if(vop2->data->soc_id == 3588) {
|
|
+ rk3588_set_intf_mux(vp, ep_id, polflags);
|
|
+ }
|
|
+}
|
|
+
|
|
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)
|
|
{
|
|
@@ -1564,9 +1989,11 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
|
u8 out_mode;
|
|
u32 dsp_ctrl = 0;
|
|
int act_end;
|
|
- u32 val, polflags;
|
|
+ u32 val, polflags, invpolflags;
|
|
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",
|
|
@@ -1597,10 +2024,20 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
|
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
|
|
polflags |= BIT(VSYNC_POSITIVE);
|
|
|
|
+ /* RK3588 uses inverted HDMI V/HSYNC polarity */
|
|
+ if (vop2->data->soc_id == 3588) {
|
|
+ invpolflags = 0;
|
|
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
|
+ invpolflags |= BIT(HSYNC_POSITIVE);
|
|
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
|
+ invpolflags |= BIT(VSYNC_POSITIVE);
|
|
+ } else {
|
|
+ invpolflags = polflags;
|
|
+ }
|
|
+
|
|
drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
|
|
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
|
-
|
|
- rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
|
|
+ vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
|
|
}
|
|
|
|
if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
|
|
@@ -1651,13 +2088,44 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
|
|
|
vop2_vp_write(vp, RK3568_VP_DSP_VTOTAL_VS_END, vtotal << 16 | vsync_len);
|
|
|
|
- if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
|
|
- dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
|
|
- clock *= 2;
|
|
+ if (vop2->data->soc_id == 3568) {
|
|
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
|
|
+ dsp_ctrl |= RK3568_VP_DSP_CTRL__CORE_DCLK_DIV;
|
|
+ // [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, vp->dclk_parent);
|
|
+ else
|
|
+ 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);
|
|
@@ -1668,9 +2136,71 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
|
|
|
|
drm_crtc_vblank_on(crtc);
|
|
|
|
+ // [CC:] needed?
|
|
+ ret = reset_control_assert(vp->dclk_rst);
|
|
+ if (ret < 0)
|
|
+ drm_warn(vop2->drm, "failed to assert reset: %d\n", ret);
|
|
+ udelay(10);
|
|
+ ret = reset_control_deassert(vp->dclk_rst);
|
|
+ if (ret < 0)
|
|
+ drm_warn(vop2->drm, "failed to deassert reset: %d\n", ret);
|
|
+
|
|
vop2_unlock(vop2);
|
|
}
|
|
|
|
+static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
|
|
+ struct drm_atomic_state *state)
|
|
+{
|
|
+ struct vop2_video_port *vp = to_vop2_video_port(crtc);
|
|
+ struct vop2 *vop2 = vp->vop2;
|
|
+ struct drm_crtc_state *old_crtc_state;
|
|
+ int ret;
|
|
+
|
|
+ vop2_lock(vop2);
|
|
+
|
|
+ old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
|
|
+ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
|
|
+
|
|
+ drm_crtc_vblank_off(crtc);
|
|
+
|
|
+ /*
|
|
+ * Vop standby will take effect at end of current frame,
|
|
+ * if dsp hold valid irq happen, it means standby complete.
|
|
+ *
|
|
+ * we must wait standby complete when we want to disable aclk,
|
|
+ * if not, memory bus maybe dead.
|
|
+ */
|
|
+ reinit_completion(&vp->dsp_hold_completion);
|
|
+
|
|
+ vop2_crtc_enable_irq(vp, VP_INT_DSP_HOLD_VALID);
|
|
+
|
|
+ vop2_vp_write(vp, RK3568_VP_DSP_CTRL, RK3568_VP_DSP_CTRL__STANDBY);
|
|
+
|
|
+ ret = wait_for_completion_timeout(&vp->dsp_hold_completion,
|
|
+ msecs_to_jiffies(50));
|
|
+ if (!ret)
|
|
+ drm_info(vop2->drm, "wait for vp%d dsp_hold timeout\n", vp->id);
|
|
+
|
|
+ vop2_crtc_disable_irq(vp, VP_INT_DSP_HOLD_VALID);
|
|
+
|
|
+ clk_disable_unprepare(vp->dclk);
|
|
+
|
|
+ vop2->enable_count--;
|
|
+
|
|
+ if (!vop2->enable_count)
|
|
+ vop2_disable(vop2);
|
|
+
|
|
+ vop2_unlock(vop2);
|
|
+
|
|
+ if (crtc->state->event && !crtc->state->active) {
|
|
+ spin_lock_irq(&crtc->dev->event_lock);
|
|
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
|
+ spin_unlock_irq(&crtc->dev->event_lock);
|
|
+
|
|
+ crtc->state->event = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
static int vop2_crtc_atomic_check(struct drm_crtc *crtc,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
@@ -2104,7 +2634,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,
|
|
@@ -2299,7 +2865,7 @@ static int vop2_create_crtcs(struct vop2 *vop2)
|
|
for (i = 0; i < vop2_data->nr_vps; i++) {
|
|
const struct vop2_video_port_data *vp_data;
|
|
struct device_node *np;
|
|
- char dclk_name[9];
|
|
+ char clk_name[16];
|
|
|
|
vp_data = &vop2_data->vp[i];
|
|
vp = &vop2->vps[i];
|
|
@@ -2307,13 +2873,26 @@ static int vop2_create_crtcs(struct vop2 *vop2)
|
|
vp->id = vp_data->id;
|
|
vp->data = vp_data;
|
|
|
|
- snprintf(dclk_name, sizeof(dclk_name), "dclk_vp%d", vp->id);
|
|
- vp->dclk = devm_clk_get(vop2->dev, dclk_name);
|
|
+ snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", vp->id);
|
|
+ vp->dclk = devm_clk_get(vop2->dev, clk_name);
|
|
if (IS_ERR(vp->dclk)) {
|
|
- drm_err(vop2->drm, "failed to get %s\n", dclk_name);
|
|
+ drm_err(vop2->drm, "failed to get %s\n", clk_name);
|
|
return PTR_ERR(vp->dclk);
|
|
}
|
|
|
|
+ vp->dclk_rst = devm_reset_control_get_optional(vop2->dev, clk_name);
|
|
+ if (IS_ERR(vp->dclk_rst)) {
|
|
+ drm_err(vop2->drm, "failed to get dclk reset\n");
|
|
+ return PTR_ERR(vp->dclk_rst);
|
|
+ }
|
|
+
|
|
+ snprintf(clk_name, sizeof(clk_name), "dclk_src_vp%d", vp->id);
|
|
+ vp->dclk_parent = devm_clk_get_optional(vop2->dev, clk_name);
|
|
+ if (IS_ERR(vp->dclk_parent)) {
|
|
+ drm_err(vop2->drm, "failed to get %s\n", clk_name);
|
|
+ return PTR_ERR(vp->dclk_parent);
|
|
+ }
|
|
+
|
|
np = of_graph_get_remote_node(dev->of_node, i, -1);
|
|
if (!np) {
|
|
drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
|
|
@@ -2674,6 +3253,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)
|
|
+ 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);
|
|
@@ -2726,7 +3635,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
|
|
return PTR_ERR(vop2->lut_regs);
|
|
}
|
|
|
|
+ // [CC:] grf -> sys_grf in downstream
|
|
vop2->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
|
|
+ // [CC:] vop_grf -> grf in downstream
|
|
+ // [CC:] make use of new grf's
|
|
+ vop2->vop_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vop-grf");
|
|
+ vop2->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo1-grf");
|
|
|
|
vop2->hclk = devm_clk_get(vop2->dev, "hclk");
|
|
if (IS_ERR(vop2->hclk)) {
|
|
@@ -2740,6 +3654,31 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
|
|
return PTR_ERR(vop2->aclk);
|
|
}
|
|
|
|
+ vop2->pclk = devm_clk_get_optional(vop2->dev, "pclk");
|
|
+ if (IS_ERR(vop2->pclk)) {
|
|
+ DRM_DEV_ERROR(vop2->dev, "failed to get pclk source\n");
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+ // [CC:] drop ahb_rst & axi_rst
|
|
+ vop2->ahb_rst = devm_reset_control_get_optional(vop2->dev, "ahb");
|
|
+ if (IS_ERR(vop2->ahb_rst)) {
|
|
+ DRM_DEV_ERROR(vop2->dev, "failed to get ahb reset\n");
|
|
+ return PTR_ERR(vop2->ahb_rst);
|
|
+ }
|
|
+
|
|
+ vop2->axi_rst = devm_reset_control_get_optional(vop2->dev, "axi");
|
|
+ if (IS_ERR(vop2->axi_rst)) {
|
|
+ DRM_DEV_ERROR(vop2->dev, "failed to get axi reset\n");
|
|
+ return PTR_ERR(vop2->axi_rst);
|
|
+ }
|
|
+
|
|
vop2->irq = platform_get_irq(pdev, 0);
|
|
if (vop2->irq < 0) {
|
|
drm_err(vop2->drm, "cannot find irq for vop2\n");
|
|
@@ -2756,6 +3695,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,
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
|
|
index 56fd31e05..cae71df81 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
|
|
@@ -12,10 +12,18 @@
|
|
#include <linux/regmap.h>
|
|
#include <drm/drm_modes.h>
|
|
|
|
-#define VOP_FEATURE_OUTPUT_10BIT BIT(0)
|
|
+/* a feature to splice two windows and two vps to support resolution > 4096 */
|
|
+#define VOP_FEATURE_SPLICE BIT(5)
|
|
+//[CC:] dupplicate of #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) in vop.h
|
|
+#define VOP_FEATURE_OUTPUT_10BIT BIT(0)
|
|
|
|
+//[CC:] downstream uses #define WIN_FEATURE_AFBDC BIT(3)
|
|
#define WIN_FEATURE_AFBDC BIT(0)
|
|
#define WIN_FEATURE_CLUSTER BIT(1)
|
|
+/* Left win in splice mode */
|
|
+#define WIN_FEATURE_SPLICE_LEFT BIT(6)
|
|
+
|
|
+#define HIWORD_UPDATE(v, h, l) ((GENMASK(h, l) << 16) | ((v) << (l)))
|
|
|
|
/*
|
|
* the delay number of a window in different mode.
|
|
@@ -39,6 +47,18 @@ enum vop2_scale_down_mode {
|
|
VOP2_SCALE_DOWN_AVG,
|
|
};
|
|
|
|
+/*
|
|
+ * vop2 internal power domain id,
|
|
+ * should be all none zero, 0 will be treat as invalid;
|
|
+ */
|
|
+#define VOP2_PD_CLUSTER0 BIT(0)
|
|
+#define VOP2_PD_CLUSTER1 BIT(1)
|
|
+#define VOP2_PD_CLUSTER2 BIT(2)
|
|
+#define VOP2_PD_CLUSTER3 BIT(3)
|
|
+#define VOP2_PD_DSC_8K BIT(5)
|
|
+#define VOP2_PD_DSC_4K BIT(6)
|
|
+#define VOP2_PD_ESMART BIT(7)
|
|
+
|
|
enum vop2_win_regs {
|
|
VOP2_WIN_ENABLE,
|
|
VOP2_WIN_FORMAT,
|
|
@@ -107,6 +127,7 @@ enum vop2_win_regs {
|
|
struct vop2_win_data {
|
|
const char *name;
|
|
unsigned int phys_id;
|
|
+ uint8_t splice_win_id;
|
|
|
|
u32 base;
|
|
enum drm_plane_type type;
|
|
@@ -129,9 +150,11 @@ struct vop2_win_data {
|
|
|
|
struct vop2_video_port_data {
|
|
unsigned int id;
|
|
+ uint8_t splice_vp_id;
|
|
u32 feature;
|
|
u16 gamma_lut_len;
|
|
u16 cubic_lut_len;
|
|
+ unsigned long dclk_max;
|
|
struct vop_rect max_output;
|
|
const u8 pre_scan_max_dly[4];
|
|
unsigned int offset;
|
|
@@ -145,7 +168,13 @@ struct vop2_data {
|
|
struct vop_rect max_output;
|
|
|
|
unsigned int win_size;
|
|
+ // [CC:] convert to an enum as it is used in conditional statements
|
|
unsigned int soc_id;
|
|
+
|
|
+ uint8_t nr_dscs;
|
|
+ uint8_t nr_conns;
|
|
+ const struct vop2_dsc_data *dsc;
|
|
+ const struct vop2_connector_if_data *conn;
|
|
};
|
|
|
|
/* interrupt define */
|
|
@@ -450,6 +479,45 @@ enum dst_factor_mode {
|
|
|
|
#define POLFLAG_DCLK_INV BIT(3)
|
|
|
|
+/* RK3588 registers */
|
|
+#define RK3588_GRF_SOC_CON1 0x0304
|
|
+#define RK3588_GRF_VOP_CON2 0x08
|
|
+#define RK3588_GRF_VO1_CON0 0x00
|
|
+
|
|
+#define RK3588_GRF_VO1_CON0__HDMI0_PIN_POL GENMASK(6, 5)
|
|
+
|
|
+#define RK3588_SYS_PD_CTRL 0x034
|
|
+#define RK3588_VP_CLK_CTRL 0x0c
|
|
+
|
|
+#define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2)
|
|
+#define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0)
|
|
+
|
|
+#define RK3588_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(20, 20)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI1_MUX GENMASK(19, 18)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_EDP_HDMI0_MUX GENMASK(17, 16)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_DP1_MUX GENMASK(15, 14)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_DP0_MUX GENMASK(13, 12)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_DPI GENMASK(9, 8)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_MIPI1 BIT(7)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_MIPI0 BIT(6)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_HDMI1 BIT(5)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_EDP1 BIT(4)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_HDMI0 BIT(3)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_EDP0 BIT(2)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_DP1 BIT(1)
|
|
+#define RK3588_SYS_DSP_INFACE_EN_DP0 BIT(0)
|
|
+
|
|
+#define RK3588_DSP_IF_MIPI1_PCLK_DIV GENMASK(27, 26)
|
|
+#define RK3588_DSP_IF_MIPI0_PCLK_DIV GENMASK(25, 24)
|
|
+#define RK3588_DSP_IF_EDP_HDMI1_PCLK_DIV GENMASK(22, 22)
|
|
+#define RK3588_DSP_IF_EDP_HDMI1_DCLK_DIV GENMASK(21, 20)
|
|
+#define RK3588_DSP_IF_EDP_HDMI0_PCLK_DIV GENMASK(18, 18)
|
|
+#define RK3588_DSP_IF_EDP_HDMI0_DCLK_DIV GENMASK(17, 16)
|
|
+
|
|
+#define RK3588_DSP_IF_POL__DP1_PIN_POL GENMASK(14, 12)
|
|
+#define RK3588_DSP_IF_POL__DP0_PIN_POL GENMASK(10, 8)
|
|
+
|
|
enum vop2_layer_phy_id {
|
|
ROCKCHIP_VOP2_CLUSTER0 = 0,
|
|
ROCKCHIP_VOP2_CLUSTER1,
|
|
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
|
|
index 22288ad7f..2bc1baf5c 100644
|
|
--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
|
|
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
|
|
@@ -131,6 +131,49 @@ static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
|
|
},
|
|
};
|
|
|
|
+static const struct vop2_video_port_data rk3588_vop_video_ports[] = {
|
|
+ {
|
|
+ .id = 0,
|
|
+ // .splice_vp_id = 1,
|
|
+ // .lut_dma_rid = 1,
|
|
+ .feature = VOP_FEATURE_OUTPUT_10BIT,
|
|
+ .gamma_lut_len = 1024,
|
|
+ .cubic_lut_len = 9 * 9 * 9,
|
|
+ // .dclk_max = 600000000,
|
|
+ .max_output = { 7680, 4320 },
|
|
+ .pre_scan_max_dly = { 76, 65, 65, 54 },
|
|
+ .offset = 0xc00,
|
|
+ }, {
|
|
+ .id = 1,
|
|
+ // .lut_dma_rid = 14,
|
|
+ .feature = VOP_FEATURE_OUTPUT_10BIT,
|
|
+ .gamma_lut_len = 1024,
|
|
+ .cubic_lut_len = 9 * 9 * 9,
|
|
+ // .dclk_max = 600000000,
|
|
+ .max_output = { 4096, 2304 },
|
|
+ .pre_scan_max_dly = { 76, 65, 65, 54 },
|
|
+ .offset = 0xd00,
|
|
+ }, {
|
|
+ .id = 2,
|
|
+ // .lut_dma_rid = 14,
|
|
+ .feature = VOP_FEATURE_OUTPUT_10BIT,
|
|
+ .gamma_lut_len = 1024,
|
|
+ .cubic_lut_len = 17 * 17 * 17,
|
|
+ // .dclk_max = 600000000,
|
|
+ .max_output = { 4096, 2304 },
|
|
+ .pre_scan_max_dly = { 52, 52, 52, 52 },
|
|
+ .offset = 0xe00,
|
|
+ }, {
|
|
+ .id = 3,
|
|
+ // .lut_dma_rid = 14,
|
|
+ .gamma_lut_len = 1024,
|
|
+ // .dclk_max = 200000000,
|
|
+ .max_output = { 2048, 1536 },
|
|
+ .pre_scan_max_dly = { 52, 52, 52, 52 },
|
|
+ .offset = 0xf00,
|
|
+ },
|
|
+};
|
|
+
|
|
/*
|
|
* rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
|
|
* Every cluster can work as 4K win or split into two win.
|
|
@@ -234,6 +277,174 @@ static const struct vop2_win_data rk3568_vop_win_data[] = {
|
|
},
|
|
};
|
|
|
|
+/*
|
|
+ * rk3588 vop with 4 cluster, 4 esmart win.
|
|
+ * Every cluster can work as 4K win or split into two win.
|
|
+ * All win in cluster support AFBCD.
|
|
+ *
|
|
+ * Every esmart win and smart win support 4 Multi-region.
|
|
+ *
|
|
+ * Scale filter mode:
|
|
+ *
|
|
+ * * Cluster: bicubic for horizontal scale up, others use bilinear
|
|
+ * * ESmart:
|
|
+ * * nearest-neighbor/bilinear/bicubic for scale up
|
|
+ * * nearest-neighbor/bilinear/average for scale down
|
|
+ *
|
|
+ * AXI Read ID assignment:
|
|
+ * Two AXI bus:
|
|
+ * AXI0 is a read/write bus with a higher performance.
|
|
+ * AXI1 is a read only bus.
|
|
+ *
|
|
+ * Every window on a AXI bus must assigned two unique
|
|
+ * read id(yrgb_id/uv_id, valid id are 0x1~0xe).
|
|
+ *
|
|
+ * AXI0:
|
|
+ * Cluster0/1, Esmart0/1, WriteBack
|
|
+ *
|
|
+ * AXI 1:
|
|
+ * Cluster2/3, Esmart2/3
|
|
+ */
|
|
+static const struct vop2_win_data rk3588_vop_win_data[] = {
|
|
+ {
|
|
+ .name = "Cluster0-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_CLUSTER0,
|
|
+ //[CC:] .splice_win_id = ROCKCHIP_VOP2_CLUSTER1,
|
|
+ .base = 0x1000,
|
|
+ .formats = formats_cluster,
|
|
+ .nformats = ARRAY_SIZE(formats_cluster),
|
|
+ .format_modifiers = format_modifiers_afbc,
|
|
+ .layer_sel_id = 0,
|
|
+ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
|
|
+ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_OVERLAY,
|
|
+ // .pd_id = VOP2_PD_CLUSTER0,
|
|
+ // .axi_id = 0,
|
|
+ // .axi_yrgb_id = 2,
|
|
+ // .axi_uv_id = 3,
|
|
+ .max_upscale_factor = 4,
|
|
+ .max_downscale_factor = 4,
|
|
+ .dly = { 4, 26, 29 },
|
|
+ //[CC:] WIN_FEATURE_SPLICE_LEFT
|
|
+ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
|
|
+ }, {
|
|
+ .name = "Cluster1-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_CLUSTER1,
|
|
+ .base = 0x1200,
|
|
+ .formats = formats_cluster,
|
|
+ .nformats = ARRAY_SIZE(formats_cluster),
|
|
+ .format_modifiers = format_modifiers_afbc,
|
|
+ .layer_sel_id = 1,
|
|
+ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
|
|
+ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_OVERLAY,
|
|
+ // .pd_id = VOP2_PD_CLUSTER1,
|
|
+ // .axi_id = 0,
|
|
+ // .axi_yrgb_id = 6,
|
|
+ // .axi_uv_id = 7,
|
|
+ .max_upscale_factor = 4,
|
|
+ .max_downscale_factor = 4,
|
|
+ .dly = { 4, 26, 29 },
|
|
+ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
|
|
+ }, {
|
|
+ .name = "Cluster2-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_CLUSTER2,
|
|
+ // [CC:] .splice_win_id = ROCKCHIP_VOP2_CLUSTER3,
|
|
+ .base = 0x1400,
|
|
+ .formats = formats_cluster,
|
|
+ .nformats = ARRAY_SIZE(formats_cluster),
|
|
+ .format_modifiers = format_modifiers_afbc,
|
|
+ .layer_sel_id = 4,
|
|
+ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
|
|
+ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_OVERLAY,
|
|
+ // .pd_id = VOP2_PD_CLUSTER2,
|
|
+ // .axi_id = 1,
|
|
+ // .axi_yrgb_id = 2,
|
|
+ // .axi_uv_id = 3,
|
|
+ .max_upscale_factor = 4,
|
|
+ .max_downscale_factor = 4,
|
|
+ .dly = { 4, 26, 29 },
|
|
+ //[CC:] WIN_FEATURE_SPLICE_LEFT
|
|
+ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
|
|
+ }, {
|
|
+ .name = "Cluster3-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_CLUSTER3,
|
|
+ .base = 0x1600,
|
|
+ .formats = formats_cluster,
|
|
+ .nformats = ARRAY_SIZE(formats_cluster),
|
|
+ .format_modifiers = format_modifiers_afbc,
|
|
+ .layer_sel_id = 5,
|
|
+ .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
|
|
+ DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_OVERLAY,
|
|
+ // .pd_id = VOP2_PD_CLUSTER3,
|
|
+ // .axi_id = 1,
|
|
+ // .axi_yrgb_id = 6,
|
|
+ // .axi_uv_id = 7,
|
|
+ .max_upscale_factor = 4,
|
|
+ .max_downscale_factor = 4,
|
|
+ .dly = { 4, 26, 29 },
|
|
+ .feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
|
|
+ }, {
|
|
+ .name = "Esmart0-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_ESMART0,
|
|
+ // [CC:] .splice_win_id = ROCKCHIP_VOP2_ESMART1,
|
|
+ .formats = formats_rk356x_esmart,
|
|
+ .nformats = ARRAY_SIZE(formats_rk356x_esmart),
|
|
+ .format_modifiers = format_modifiers,
|
|
+ .base = 0x1800,
|
|
+ .layer_sel_id = 2,
|
|
+ .supported_rotations = DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_PRIMARY,
|
|
+ // [CC:] .pd_id missing in downstream code
|
|
+ // .axi_id = 0,
|
|
+ // .axi_yrgb_id = 0x0a,
|
|
+ // .axi_uv_id = 0x0b,
|
|
+ .max_upscale_factor = 8,
|
|
+ .max_downscale_factor = 8,
|
|
+ .dly = { 23, 45, 48 },
|
|
+ // .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA,
|
|
+ }, {
|
|
+ .name = "Esmart2-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_ESMART2,
|
|
+ // [CC:] .splice_win_id = ROCKCHIP_VOP2_ESMART3,
|
|
+ .formats = formats_rk356x_esmart,
|
|
+ .nformats = ARRAY_SIZE(formats_rk356x_esmart),
|
|
+ .format_modifiers = format_modifiers,
|
|
+ .base = 0x1c00,
|
|
+ .layer_sel_id = 6,
|
|
+ .supported_rotations = DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_PRIMARY,
|
|
+ // .pd_id = VOP2_PD_ESMART,
|
|
+ // .axi_id = 1,
|
|
+ // .axi_yrgb_id = 0x0a,
|
|
+ // .axi_uv_id = 0x0b,
|
|
+ .max_upscale_factor = 8,
|
|
+ .max_downscale_factor = 8,
|
|
+ .dly = { 23, 45, 48 },
|
|
+ // .feature = WIN_FEATURE_SPLICE_LEFT | WIN_FEATURE_MULTI_AREA,
|
|
+ }, {
|
|
+ .name = "Esmart1-win0",
|
|
+ .phys_id = ROCKCHIP_VOP2_ESMART1,
|
|
+ .formats = formats_rk356x_esmart,
|
|
+ .nformats = ARRAY_SIZE(formats_rk356x_esmart),
|
|
+ .format_modifiers = format_modifiers,
|
|
+ .base = 0x1a00,
|
|
+ .layer_sel_id = 3,
|
|
+ .supported_rotations = DRM_MODE_REFLECT_Y,
|
|
+ .type = DRM_PLANE_TYPE_PRIMARY,
|
|
+ // .pd_id = VOP2_PD_ESMART,
|
|
+ // .axi_id = 0,
|
|
+ // .axi_yrgb_id = 0x0c,
|
|
+ // .axi_uv_id = 0x0d,
|
|
+ .max_upscale_factor = 8,
|
|
+ .max_downscale_factor = 8,
|
|
+ .dly = { 23, 45, 48 },
|
|
+ // .feature = WIN_FEATURE_MULTI_AREA,
|
|
+ },
|
|
+};
|
|
+
|
|
static const struct vop2_data rk3566_vop = {
|
|
.nr_vps = 3,
|
|
.max_input = { 4096, 2304 },
|
|
@@ -246,14 +457,27 @@ static const struct vop2_data rk3566_vop = {
|
|
|
|
static const struct vop2_data rk3568_vop = {
|
|
.nr_vps = 3,
|
|
- .max_input = { 4096, 2304 },
|
|
- .max_output = { 4096, 2304 },
|
|
+ .max_input = { 4096, 4320 },
|
|
+ .max_output = { 4096, 4320 },
|
|
.vp = rk3568_vop_video_ports,
|
|
.win = rk3568_vop_win_data,
|
|
.win_size = ARRAY_SIZE(rk3568_vop_win_data),
|
|
.soc_id = 3568,
|
|
};
|
|
|
|
+static const struct vop2_data rk3588_vop = {
|
|
+ // .feature = VOP_FEATURE_SPLICE,
|
|
+ .nr_vps = 4,
|
|
+ .max_input = { 4096, 2304 },
|
|
+ .max_output = { 4096, 2304 },
|
|
+ .vp = rk3588_vop_video_ports,
|
|
+ .win = rk3588_vop_win_data,
|
|
+ .win_size = ARRAY_SIZE(rk3588_vop_win_data),
|
|
+ // .conn = rk3588_conn_if_data,
|
|
+ // .nr_conns = ARRAY_SIZE(rk3588_conn_if_data),
|
|
+ .soc_id = 3588,
|
|
+};
|
|
+
|
|
static const struct of_device_id vop2_dt_match[] = {
|
|
{
|
|
.compatible = "rockchip,rk3566-vop",
|
|
@@ -261,6 +485,9 @@ static const struct of_device_id vop2_dt_match[] = {
|
|
}, {
|
|
.compatible = "rockchip,rk3568-vop",
|
|
.data = &rk3568_vop,
|
|
+ }, {
|
|
+ .compatible = "rockchip,rk3588-vop",
|
|
+ .data = &rk3588_vop,
|
|
}, {
|
|
},
|
|
};
|
|
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
|
|
index 94360fc96..62c18e25b 100644
|
|
--- a/drivers/phy/rockchip/Kconfig
|
|
+++ b/drivers/phy/rockchip/Kconfig
|
|
@@ -83,6 +83,13 @@ config PHY_ROCKCHIP_PCIE
|
|
help
|
|
Enable this to support the Rockchip PCIe PHY.
|
|
|
|
+config PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI
|
|
+ tristate "Rockchip Samsung HDMI/DP Combo PHY HDMI driver"
|
|
+ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
|
|
+ select GENERIC_PHY
|
|
+ help
|
|
+ Support for Rockchip HDMI/DP Combo PHY with Samsung IP block.
|
|
+
|
|
config PHY_ROCKCHIP_SNPS_PCIE3
|
|
tristate "Rockchip Snps PCIe3 PHY Driver"
|
|
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
|
|
@@ -107,3 +114,15 @@ config PHY_ROCKCHIP_USB
|
|
select GENERIC_PHY
|
|
help
|
|
Enable this to support the Rockchip USB 2.0 PHY.
|
|
+
|
|
+config PHY_ROCKCHIP_USBDP
|
|
+ tristate "Rockchip USBDP COMBO PHY Driver"
|
|
+ depends on ARCH_ROCKCHIP && OF
|
|
+ select GENERIC_PHY
|
|
+ select TYPEC
|
|
+ help
|
|
+ Enable this to support the Rockchip USB3.0/DP combo PHY with
|
|
+ Samsung IP block. This is required for USB3 support on RK3588.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the module
|
|
+ will be called phy-rockchip-usbdp
|
|
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
|
|
index 7eab12923..d266414a1 100644
|
|
--- a/drivers/phy/rockchip/Makefile
|
|
+++ b/drivers/phy/rockchip/Makefile
|
|
@@ -8,6 +8,8 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
|
|
+obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI) += phy-rockchip-samsung-hdptx-hdmi.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
|
|
+obj-$(CONFIG_PHY_ROCKCHIP_USBDP) += phy-rockchip-usbdp.o
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c
|
|
new file mode 100644
|
|
index 000000000..036db0877
|
|
--- /dev/null
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c
|
|
@@ -0,0 +1,2347 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+/*
|
|
+ * Copyright (C) Rockchip Electronics Co.Ltd
|
|
+ * Author:
|
|
+ * Algea Cao <algea.cao@rock-chips.com>
|
|
+ */
|
|
+#include <linux/clk.h>
|
|
+#include <linux/clk-provider.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/iopoll.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/nvmem-consumer.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/reset.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/phy/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/rational.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
|
|
+
|
|
+#define GRF_HDPTX_CON0 0x00
|
|
+#define HDPTX_I_PLL_EN BIT(7)
|
|
+#define HDPTX_I_BIAS_EN BIT(6)
|
|
+#define HDPTX_I_BGR_EN BIT(5)
|
|
+#define GRF_HDPTX_STATUS 0x80
|
|
+#define HDPTX_O_PLL_LOCK_DONE BIT(3)
|
|
+#define HDPTX_O_PHY_CLK_RDY BIT(2)
|
|
+#define HDPTX_O_PHY_RDY BIT(1)
|
|
+#define HDPTX_O_SB_RDY BIT(0)
|
|
+
|
|
+#define CMN_REG0000 0x0000
|
|
+#define CMN_REG0001 0x0004
|
|
+#define CMN_REG0002 0x0008
|
|
+#define CMN_REG0003 0x000C
|
|
+#define CMN_REG0004 0x0010
|
|
+#define CMN_REG0005 0x0014
|
|
+#define CMN_REG0006 0x0018
|
|
+#define CMN_REG0007 0x001C
|
|
+#define CMN_REG0008 0x0020
|
|
+#define LCPLL_EN_MASK BIT(6)
|
|
+#define LCPLL_EN(x) UPDATE(x, 4, 4)
|
|
+#define LCPLL_LCVCO_MODE_EN_MASK BIT(4)
|
|
+#define LCPLL_LCVCO_MODE_EN(x) UPDATE(x, 4, 4)
|
|
+#define CMN_REG0009 0x0024
|
|
+#define CMN_REG000A 0x0028
|
|
+#define CMN_REG000B 0x002C
|
|
+#define CMN_REG000C 0x0030
|
|
+#define CMN_REG000D 0x0034
|
|
+#define CMN_REG000E 0x0038
|
|
+#define CMN_REG000F 0x003C
|
|
+#define CMN_REG0010 0x0040
|
|
+#define CMN_REG0011 0x0044
|
|
+#define CMN_REG0012 0x0048
|
|
+#define CMN_REG0013 0x004C
|
|
+#define CMN_REG0014 0x0050
|
|
+#define CMN_REG0015 0x0054
|
|
+#define CMN_REG0016 0x0058
|
|
+#define CMN_REG0017 0x005C
|
|
+#define CMN_REG0018 0x0060
|
|
+#define CMN_REG0019 0x0064
|
|
+#define CMN_REG001A 0x0068
|
|
+#define CMN_REG001B 0x006C
|
|
+#define CMN_REG001C 0x0070
|
|
+#define CMN_REG001D 0x0074
|
|
+#define CMN_REG001E 0x0078
|
|
+#define LCPLL_PI_EN_MASK BIT(5)
|
|
+#define LCPLL_PI_EN(x) UPDATE(x, 5, 5)
|
|
+#define LCPLL_100M_CLK_EN_MASK BIT(0)
|
|
+#define LCPLL_100M_CLK_EN(x) UPDATE(x, 0, 0)
|
|
+#define CMN_REG001F 0x007C
|
|
+#define CMN_REG0020 0x0080
|
|
+#define CMN_REG0021 0x0084
|
|
+#define CMN_REG0022 0x0088
|
|
+#define CMN_REG0023 0x008C
|
|
+#define CMN_REG0024 0x0090
|
|
+#define CMN_REG0025 0x0094
|
|
+#define LCPLL_PMS_IQDIV_RSTN BIT(4)
|
|
+#define CMN_REG0026 0x0098
|
|
+#define CMN_REG0027 0x009C
|
|
+#define CMN_REG0028 0x00A0
|
|
+#define LCPLL_SDC_FRAC_EN BIT(2)
|
|
+#define LCPLL_SDC_FRAC_RSTN BIT(0)
|
|
+#define CMN_REG0029 0x00A4
|
|
+#define CMN_REG002A 0x00A8
|
|
+#define CMN_REG002B 0x00AC
|
|
+#define CMN_REG002C 0x00B0
|
|
+#define CMN_REG002D 0x00B4
|
|
+#define LCPLL_SDC_N_MASK GENMASK(3, 1)
|
|
+#define LCPLL_SDC_N(x) UPDATE(x, 3, 1)
|
|
+#define CMN_REG002E 0x00B8
|
|
+#define LCPLL_SDC_NUMBERATOR_MASK GENMASK(5, 0)
|
|
+#define LCPLL_SDC_NUMBERATOR(x) UPDATE(x, 5, 0)
|
|
+#define CMN_REG002F 0x00BC
|
|
+#define LCPLL_SDC_DENOMINATOR_MASK GENMASK(7, 2)
|
|
+#define LCPLL_SDC_DENOMINATOR(x) UPDATE(x, 7, 2)
|
|
+#define LCPLL_SDC_NDIV_RSTN BIT(0)
|
|
+#define CMN_REG0030 0x00C0
|
|
+#define CMN_REG0031 0x00C4
|
|
+#define CMN_REG0032 0x00C8
|
|
+#define CMN_REG0033 0x00CC
|
|
+#define CMN_REG0034 0x00D0
|
|
+#define CMN_REG0035 0x00D4
|
|
+#define CMN_REG0036 0x00D8
|
|
+#define CMN_REG0037 0x00DC
|
|
+#define CMN_REG0038 0x00E0
|
|
+#define CMN_REG0039 0x00E4
|
|
+#define CMN_REG003A 0x00E8
|
|
+#define CMN_REG003B 0x00EC
|
|
+#define CMN_REG003C 0x00F0
|
|
+#define CMN_REG003D 0x00F4
|
|
+#define ROPLL_LCVCO_EN BIT(4)
|
|
+#define CMN_REG003E 0x00F8
|
|
+#define CMN_REG003F 0x00FC
|
|
+#define CMN_REG0040 0x0100
|
|
+#define CMN_REG0041 0x0104
|
|
+#define CMN_REG0042 0x0108
|
|
+#define CMN_REG0043 0x010C
|
|
+#define CMN_REG0044 0x0110
|
|
+#define CMN_REG0045 0x0114
|
|
+#define CMN_REG0046 0x0118
|
|
+#define CMN_REG0047 0x011C
|
|
+#define CMN_REG0048 0x0120
|
|
+#define CMN_REG0049 0x0124
|
|
+#define CMN_REG004A 0x0128
|
|
+#define CMN_REG004B 0x012C
|
|
+#define CMN_REG004C 0x0130
|
|
+#define CMN_REG004D 0x0134
|
|
+#define CMN_REG004E 0x0138
|
|
+#define ROPLL_PI_EN BIT(5)
|
|
+#define CMN_REG004F 0x013C
|
|
+#define CMN_REG0050 0x0140
|
|
+#define CMN_REG0051 0x0144
|
|
+#define CMN_REG0052 0x0148
|
|
+#define CMN_REG0053 0x014C
|
|
+#define CMN_REG0054 0x0150
|
|
+#define CMN_REG0055 0x0154
|
|
+#define CMN_REG0056 0x0158
|
|
+#define CMN_REG0057 0x015C
|
|
+#define CMN_REG0058 0x0160
|
|
+#define CMN_REG0059 0x0164
|
|
+#define CMN_REG005A 0x0168
|
|
+#define CMN_REG005B 0x016C
|
|
+#define CMN_REG005C 0x0170
|
|
+#define ROPLL_PMS_IQDIV_RSTN BIT(5)
|
|
+#define CMN_REG005D 0x0174
|
|
+#define CMN_REG005E 0x0178
|
|
+#define ROPLL_SDM_EN_MASK BIT(6)
|
|
+#define ROPLL_SDM_EN(x) UPDATE(x, 6, 6)
|
|
+#define ROPLL_SDM_FRAC_EN_RBR BIT(3)
|
|
+#define ROPLL_SDM_FRAC_EN_HBR BIT(2)
|
|
+#define ROPLL_SDM_FRAC_EN_HBR2 BIT(1)
|
|
+#define ROPLL_SDM_FRAC_EN_HBR3 BIT(0)
|
|
+#define CMN_REG005F 0x017C
|
|
+#define CMN_REG0060 0x0180
|
|
+#define CMN_REG0061 0x0184
|
|
+#define CMN_REG0062 0x0188
|
|
+#define CMN_REG0063 0x018C
|
|
+#define CMN_REG0064 0x0190
|
|
+#define ROPLL_SDM_NUM_SIGN_RBR_MASK BIT(3)
|
|
+#define ROPLL_SDM_NUM_SIGN_RBR(x) UPDATE(x, 3, 3)
|
|
+#define CMN_REG0065 0x0194
|
|
+#define CMN_REG0066 0x0198
|
|
+#define CMN_REG0067 0x019C
|
|
+#define CMN_REG0068 0x01A0
|
|
+#define CMN_REG0069 0x01A4
|
|
+#define ROPLL_SDC_N_RBR_MASK GENMASK(2, 0)
|
|
+#define ROPLL_SDC_N_RBR(x) UPDATE(x, 2, 0)
|
|
+#define CMN_REG006A 0x01A8
|
|
+#define CMN_REG006B 0x01AC
|
|
+#define CMN_REG006C 0x01B0
|
|
+#define CMN_REG006D 0x01B4
|
|
+#define CMN_REG006E 0x01B8
|
|
+#define CMN_REG006F 0x01BC
|
|
+#define CMN_REG0070 0x01C0
|
|
+#define CMN_REG0071 0x01C4
|
|
+#define CMN_REG0072 0x01C8
|
|
+#define CMN_REG0073 0x01CC
|
|
+#define CMN_REG0074 0x01D0
|
|
+#define ROPLL_SDC_NDIV_RSTN BIT(2)
|
|
+#define ROPLL_SSC_EN BIT(0)
|
|
+#define CMN_REG0075 0x01D4
|
|
+#define CMN_REG0076 0x01D8
|
|
+#define CMN_REG0077 0x01DC
|
|
+#define CMN_REG0078 0x01E0
|
|
+#define CMN_REG0079 0x01E4
|
|
+#define CMN_REG007A 0x01E8
|
|
+#define CMN_REG007B 0x01EC
|
|
+#define CMN_REG007C 0x01F0
|
|
+#define CMN_REG007D 0x01F4
|
|
+#define CMN_REG007E 0x01F8
|
|
+#define CMN_REG007F 0x01FC
|
|
+#define CMN_REG0080 0x0200
|
|
+#define CMN_REG0081 0x0204
|
|
+#define OVRD_PLL_CD_CLK_EN BIT(8)
|
|
+#define PLL_CD_HSCLK_EAST_EN BIT(0)
|
|
+#define CMN_REG0082 0x0208
|
|
+#define CMN_REG0083 0x020C
|
|
+#define CMN_REG0084 0x0210
|
|
+#define CMN_REG0085 0x0214
|
|
+#define CMN_REG0086 0x0218
|
|
+#define PLL_PCG_POSTDIV_SEL_MASK GENMASK(7, 4)
|
|
+#define PLL_PCG_POSTDIV_SEL(x) UPDATE(x, 7, 4)
|
|
+#define PLL_PCG_CLK_SEL_MASK GENMASK(3, 1)
|
|
+#define PLL_PCG_CLK_SEL(x) UPDATE(x, 3, 1)
|
|
+#define PLL_PCG_CLK_EN BIT(0)
|
|
+#define CMN_REG0087 0x021C
|
|
+#define PLL_FRL_MODE_EN BIT(3)
|
|
+#define PLL_TX_HS_CLK_EN BIT(2)
|
|
+#define CMN_REG0088 0x0220
|
|
+#define CMN_REG0089 0x0224
|
|
+#define LCPLL_ALONE_MODE BIT(1)
|
|
+#define CMN_REG008A 0x0228
|
|
+#define CMN_REG008B 0x022C
|
|
+#define CMN_REG008C 0x0230
|
|
+#define CMN_REG008D 0x0234
|
|
+#define CMN_REG008E 0x0238
|
|
+#define CMN_REG008F 0x023C
|
|
+#define CMN_REG0090 0x0240
|
|
+#define CMN_REG0091 0x0244
|
|
+#define CMN_REG0092 0x0248
|
|
+#define CMN_REG0093 0x024C
|
|
+#define CMN_REG0094 0x0250
|
|
+#define CMN_REG0095 0x0254
|
|
+#define CMN_REG0096 0x0258
|
|
+#define CMN_REG0097 0x025C
|
|
+#define DIG_CLK_SEL BIT(1)
|
|
+#define ROPLL_REF BIT(1)
|
|
+#define LCPLL_REF 0
|
|
+#define CMN_REG0098 0x0260
|
|
+#define CMN_REG0099 0x0264
|
|
+#define CMN_ROPLL_ALONE_MODE BIT(2)
|
|
+#define ROPLL_ALONE_MODE BIT(2)
|
|
+#define CMN_REG009A 0x0268
|
|
+#define HS_SPEED_SEL BIT(0)
|
|
+#define DIV_10_CLOCK BIT(0)
|
|
+#define CMN_REG009B 0x026C
|
|
+#define IS_SPEED_SEL BIT(4)
|
|
+#define LINK_SYMBOL_CLOCK BIT(4)
|
|
+#define LINK_SYMBOL_CLOCK1_2 0
|
|
+#define CMN_REG009C 0x0270
|
|
+#define CMN_REG009D 0x0274
|
|
+#define CMN_REG009E 0x0278
|
|
+#define CMN_REG009F 0x027C
|
|
+#define CMN_REG00A0 0x0280
|
|
+#define CMN_REG00A1 0x0284
|
|
+#define CMN_REG00A2 0x0288
|
|
+#define CMN_REG00A3 0x028C
|
|
+#define CMN_REG00AD 0x0290
|
|
+#define CMN_REG00A5 0x0294
|
|
+#define CMN_REG00A6 0x0298
|
|
+#define CMN_REG00A7 0x029C
|
|
+#define SB_REG0100 0x0400
|
|
+#define SB_REG0101 0x0404
|
|
+#define SB_REG0102 0x0408
|
|
+#define OVRD_SB_RXTERM_EN_MASK BIT(5)
|
|
+#define OVRD_SB_RXTERM_EN(x) UPDATE(x, 5, 5)
|
|
+#define SB_RXTERM_EN_MASK BIT(4)
|
|
+#define SB_RXTERM_EN(x) UPDATE(x, 4, 4)
|
|
+#define ANA_SB_RXTERM_OFFSP_MASK GENMASK(3, 0)
|
|
+#define ANA_SB_RXTERM_OFFSP(x) UPDATE(x, 3, 0)
|
|
+#define SB_REG0103 0x040C
|
|
+#define ANA_SB_RXTERM_OFFSN_MASK GENMASK(6, 3)
|
|
+#define ANA_SB_RXTERM_OFFSN(x) UPDATE(x, 6, 3)
|
|
+#define OVRD_SB_RX_RESCAL_DONE_MASK BIT(1)
|
|
+#define OVRD_SB_RX_RESCAL_DONE(x) UPDATE(x, 1, 1)
|
|
+#define SB_RX_RESCAL_DONE_MASK BIT(0)
|
|
+#define SB_RX_RESCAL_DONE(x) UPDATE(x, 0, 0)
|
|
+#define SB_REG0104 0x0410
|
|
+#define OVRD_SB_EN_MASK BIT(5)
|
|
+#define OVRD_SB_EN(x) UPDATE(x, 5, 5)
|
|
+#define SB_EN_MASK BIT(4)
|
|
+#define SB_EN(x) UPDATE(x, 4, 4)
|
|
+#define SB_REG0105 0x0414
|
|
+#define OVRD_SB_EARC_CMDC_EN_MASK BIT(6)
|
|
+#define OVRD_SB_EARC_CMDC_EN(x) UPDATE(x, 6, 6)
|
|
+#define SB_EARC_CMDC_EN_MASK BIT(5)
|
|
+#define SB_EARC_CMDC_EN(x) UPDATE(x, 5, 5)
|
|
+#define ANA_SB_TX_HLVL_PROG_MASK GENMASK(2, 0)
|
|
+#define ANA_SB_TX_HLVL_PROG(x) UPDATE(x, 2, 0)
|
|
+#define SB_REG0106 0x0418
|
|
+#define ANA_SB_TX_LLVL_PROG_MASK GENMASK(6, 4)
|
|
+#define ANA_SB_TX_LLVL_PROG(x) UPDATE(x, 6, 4)
|
|
+#define SB_REG0107 0x041C
|
|
+#define SB_REG0108 0x0420
|
|
+#define SB_REG0109 0x0424
|
|
+#define ANA_SB_DMRX_AFC_DIV_RATIO_MASK GENMASK(2, 0)
|
|
+#define ANA_SB_DMRX_AFC_DIV_RATIO(x) UPDATE(x, 2, 0)
|
|
+#define SB_REG010A 0x0428
|
|
+#define SB_REG010B 0x042C
|
|
+#define SB_REG010C 0x0430
|
|
+#define SB_REG010D 0x0434
|
|
+#define SB_REG010E 0x0438
|
|
+#define SB_REG010F 0x043C
|
|
+#define OVRD_SB_VREG_EN_MASK BIT(7)
|
|
+#define OVRD_SB_VREG_EN(x) UPDATE(x, 7, 7)
|
|
+#define SB_VREG_EN_MASK BIT(6)
|
|
+#define SB_VREG_EN(x) UPDATE(x, 6, 6)
|
|
+#define OVRD_SB_VREG_LPF_BYPASS_MASK BIT(5)
|
|
+#define OVRD_SB_VREG_LPF_BYPASS(x) UPDATE(x, 5, 5)
|
|
+#define SB_VREG_LPF_BYPASS_MASK BIT(4)
|
|
+#define SB_VREG_LPF_BYPASS(x) UPDATE(x, 4, 4)
|
|
+#define ANA_SB_VREG_GAIN_CTRL_MASK GENMASK(3, 0)
|
|
+#define ANA_SB_VREG_GAIN_CTRL(x) UPDATE(x, 3, 0)
|
|
+#define SB_REG0110 0x0440
|
|
+#define ANA_SB_VREG_REF_SEL_MASK BIT(0)
|
|
+#define ANA_SB_VREG_REF_SEL(x) UPDATE(x, 0, 0)
|
|
+#define SB_REG0111 0x0444
|
|
+#define SB_REG0112 0x0448
|
|
+#define SB_REG0113 0x044C
|
|
+#define SB_RX_RCAL_OPT_CODE_MASK GENMASK(5, 4)
|
|
+#define SB_RX_RCAL_OPT_CODE(x) UPDATE(x, 5, 4)
|
|
+#define SB_RX_RTERM_CTRL_MASK GENMASK(3, 0)
|
|
+#define SB_RX_RTERM_CTRL(x) UPDATE(x, 3, 0)
|
|
+#define SB_REG0114 0x0450
|
|
+#define SB_TG_SB_EN_DELAY_TIME_MASK GENMASK(5, 3)
|
|
+#define SB_TG_SB_EN_DELAY_TIME(x) UPDATE(x, 5, 3)
|
|
+#define SB_TG_RXTERM_EN_DELAY_TIME_MASK GENMASK(2, 0)
|
|
+#define SB_TG_RXTERM_EN_DELAY_TIME(x) UPDATE(x, 2, 0)
|
|
+#define SB_REG0115 0x0454
|
|
+#define SB_READY_DELAY_TIME_MASK GENMASK(5, 3)
|
|
+#define SB_READY_DELAY_TIME(x) UPDATE(x, 5, 3)
|
|
+#define SB_TG_OSC_EN_DELAY_TIME_MASK GENMASK(2, 0)
|
|
+#define SB_TG_OSC_EN_DELAY_TIME(x) UPDATE(x, 2, 0)
|
|
+#define SB_REG0116 0x0458
|
|
+#define AFC_RSTN_DELAY_TIME_MASK GENMASK(6, 4)
|
|
+#define AFC_RSTN_DELAY_TIME(x) UPDATE(x, 6, 4)
|
|
+#define SB_REG0117 0x045C
|
|
+#define FAST_PULSE_TIME_MASK GENMASK(3, 0)
|
|
+#define FAST_PULSE_TIME(x) UPDATE(x, 3, 0)
|
|
+#define SB_REG0118 0x0460
|
|
+#define SB_REG0119 0x0464
|
|
+#define SB_REG011A 0x0468
|
|
+#define SB_REG011B 0x046C
|
|
+#define SB_EARC_SIG_DET_BYPASS_MASK BIT(4)
|
|
+#define SB_EARC_SIG_DET_BYPASS(x) UPDATE(x, 4, 4)
|
|
+#define SB_AFC_TOL_MASK GENMASK(3, 0)
|
|
+#define SB_AFC_TOL(x) UPDATE(x, 3, 0)
|
|
+#define SB_REG011C 0x0470
|
|
+#define SB_REG011D 0x0474
|
|
+#define SB_REG011E 0x0478
|
|
+#define SB_REG011F 0x047C
|
|
+#define SB_PWM_AFC_CTRL_MASK GENMASK(7, 2)
|
|
+#define SB_PWM_AFC_CTRL(x) UPDATE(x, 7, 2)
|
|
+#define SB_RCAL_RSTN_MASK BIT(1)
|
|
+#define SB_RCAL_RSTN(x) UPDATE(x, 1, 1)
|
|
+#define SB_REG0120 0x0480
|
|
+#define SB_EARC_EN_MASK BIT(1)
|
|
+#define SB_EARC_EN(x) UPDATE(x, 1, 1)
|
|
+#define SB_EARC_AFC_EN_MASK BIT(2)
|
|
+#define SB_EARC_AFC_EN(x) UPDATE(x, 2, 2)
|
|
+#define SB_REG0121 0x0484
|
|
+#define SB_REG0122 0x0488
|
|
+#define SB_REG0123 0x048C
|
|
+#define OVRD_SB_READY_MASK BIT(5)
|
|
+#define OVRD_SB_READY(x) UPDATE(x, 5, 5)
|
|
+#define SB_READY_MASK BIT(4)
|
|
+#define SB_READY(x) UPDATE(x, 4, 4)
|
|
+#define SB_REG0124 0x0490
|
|
+#define SB_REG0125 0x0494
|
|
+#define SB_REG0126 0x0498
|
|
+#define SB_REG0127 0x049C
|
|
+#define SB_REG0128 0x04A0
|
|
+#define SB_REG0129 0x04AD
|
|
+#define LNTOP_REG0200 0x0800
|
|
+#define PROTOCOL_SEL BIT(2)
|
|
+#define HDMI_MODE BIT(2)
|
|
+#define HDMI_TMDS_FRL_SEL BIT(1)
|
|
+#define LNTOP_REG0201 0x0804
|
|
+#define LNTOP_REG0202 0x0808
|
|
+#define LNTOP_REG0203 0x080C
|
|
+#define LNTOP_REG0204 0x0810
|
|
+#define LNTOP_REG0205 0x0814
|
|
+#define LNTOP_REG0206 0x0818
|
|
+#define DATA_BUS_WIDTH (0x3 << 1)
|
|
+#define WIDTH_40BIT (0x3 << 1)
|
|
+#define WIDTH_36BIT (0x2 << 1)
|
|
+#define DATA_BUS_SEL BIT(0)
|
|
+#define DATA_BUS_36_40 BIT(0)
|
|
+#define LNTOP_REG0207 0x081C
|
|
+#define LANE_EN 0xf
|
|
+#define ALL_LANE_EN 0xf
|
|
+#define LNTOP_REG0208 0x0820
|
|
+#define LNTOP_REG0209 0x0824
|
|
+#define LNTOP_REG020A 0x0828
|
|
+#define LNTOP_REG020B 0x082C
|
|
+#define LNTOP_REG020C 0x0830
|
|
+#define LNTOP_REG020D 0x0834
|
|
+#define LNTOP_REG020E 0x0838
|
|
+#define LNTOP_REG020F 0x083C
|
|
+#define LNTOP_REG0210 0x0840
|
|
+#define LNTOP_REG0211 0x0844
|
|
+#define LNTOP_REG0212 0x0848
|
|
+#define LNTOP_REG0213 0x084C
|
|
+#define LNTOP_REG0214 0x0850
|
|
+#define LNTOP_REG0215 0x0854
|
|
+#define LNTOP_REG0216 0x0858
|
|
+#define LNTOP_REG0217 0x085C
|
|
+#define LNTOP_REG0218 0x0860
|
|
+#define LNTOP_REG0219 0x0864
|
|
+#define LNTOP_REG021A 0x0868
|
|
+#define LNTOP_REG021B 0x086C
|
|
+#define LNTOP_REG021C 0x0870
|
|
+#define LNTOP_REG021D 0x0874
|
|
+#define LNTOP_REG021E 0x0878
|
|
+#define LNTOP_REG021F 0x087C
|
|
+#define LNTOP_REG0220 0x0880
|
|
+#define LNTOP_REG0221 0x0884
|
|
+#define LNTOP_REG0222 0x0888
|
|
+#define LNTOP_REG0223 0x088C
|
|
+#define LNTOP_REG0224 0x0890
|
|
+#define LNTOP_REG0225 0x0894
|
|
+#define LNTOP_REG0226 0x0898
|
|
+#define LNTOP_REG0227 0x089C
|
|
+#define LNTOP_REG0228 0x08A0
|
|
+#define LNTOP_REG0229 0x08A4
|
|
+#define LANE_REG0300 0x0C00
|
|
+#define LANE_REG0301 0x0C04
|
|
+#define LANE_REG0302 0x0C08
|
|
+#define LANE_REG0303 0x0C0C
|
|
+#define LANE_REG0304 0x0C10
|
|
+#define LANE_REG0305 0x0C14
|
|
+#define LANE_REG0306 0x0C18
|
|
+#define LANE_REG0307 0x0C1C
|
|
+#define LANE_REG0308 0x0C20
|
|
+#define LANE_REG0309 0x0C24
|
|
+#define LANE_REG030A 0x0C28
|
|
+#define LANE_REG030B 0x0C2C
|
|
+#define LANE_REG030C 0x0C30
|
|
+#define LANE_REG030D 0x0C34
|
|
+#define LANE_REG030E 0x0C38
|
|
+#define LANE_REG030F 0x0C3C
|
|
+#define LANE_REG0310 0x0C40
|
|
+#define LANE_REG0311 0x0C44
|
|
+#define LANE_REG0312 0x0C48
|
|
+#define LN0_TX_SER_RATE_SEL_RBR BIT(5)
|
|
+#define LN0_TX_SER_RATE_SEL_HBR BIT(4)
|
|
+#define LN0_TX_SER_RATE_SEL_HBR2 BIT(3)
|
|
+#define LN0_TX_SER_RATE_SEL_HBR3 BIT(2)
|
|
+#define LANE_REG0313 0x0C4C
|
|
+#define LANE_REG0314 0x0C50
|
|
+#define LANE_REG0315 0x0C54
|
|
+#define LANE_REG0316 0x0C58
|
|
+#define LANE_REG0317 0x0C5C
|
|
+#define LANE_REG0318 0x0C60
|
|
+#define LANE_REG0319 0x0C64
|
|
+#define LANE_REG031A 0x0C68
|
|
+#define LANE_REG031B 0x0C6C
|
|
+#define LANE_REG031C 0x0C70
|
|
+#define LANE_REG031D 0x0C74
|
|
+#define LANE_REG031E 0x0C78
|
|
+#define LANE_REG031F 0x0C7C
|
|
+#define LANE_REG0320 0x0C80
|
|
+#define LANE_REG0321 0x0C84
|
|
+#define LANE_REG0322 0x0C88
|
|
+#define LANE_REG0323 0x0C8C
|
|
+#define LANE_REG0324 0x0C90
|
|
+#define LANE_REG0325 0x0C94
|
|
+#define LANE_REG0326 0x0C98
|
|
+#define LANE_REG0327 0x0C9C
|
|
+#define LANE_REG0328 0x0CA0
|
|
+#define LANE_REG0329 0x0CA4
|
|
+#define LANE_REG032A 0x0CA8
|
|
+#define LANE_REG032B 0x0CAC
|
|
+#define LANE_REG032C 0x0CB0
|
|
+#define LANE_REG032D 0x0CB4
|
|
+#define LANE_REG0400 0x1000
|
|
+#define LANE_REG0401 0x1004
|
|
+#define LANE_REG0402 0x1008
|
|
+#define LANE_REG0403 0x100C
|
|
+#define LANE_REG0404 0x1010
|
|
+#define LANE_REG0405 0x1014
|
|
+#define LANE_REG0406 0x1018
|
|
+#define LANE_REG0407 0x101C
|
|
+#define LANE_REG0408 0x1020
|
|
+#define LANE_REG0409 0x1024
|
|
+#define LANE_REG040A 0x1028
|
|
+#define LANE_REG040B 0x102C
|
|
+#define LANE_REG040C 0x1030
|
|
+#define LANE_REG040D 0x1034
|
|
+#define LANE_REG040E 0x1038
|
|
+#define LANE_REG040F 0x103C
|
|
+#define LANE_REG0410 0x1040
|
|
+#define LANE_REG0411 0x1044
|
|
+#define LANE_REG0412 0x1048
|
|
+#define LN1_TX_SER_RATE_SEL_RBR BIT(5)
|
|
+#define LN1_TX_SER_RATE_SEL_HBR BIT(4)
|
|
+#define LN1_TX_SER_RATE_SEL_HBR2 BIT(3)
|
|
+#define LN1_TX_SER_RATE_SEL_HBR3 BIT(2)
|
|
+#define LANE_REG0413 0x104C
|
|
+#define LANE_REG0414 0x1050
|
|
+#define LANE_REG0415 0x1054
|
|
+#define LANE_REG0416 0x1058
|
|
+#define LANE_REG0417 0x105C
|
|
+#define LANE_REG0418 0x1060
|
|
+#define LANE_REG0419 0x1064
|
|
+#define LANE_REG041A 0x1068
|
|
+#define LANE_REG041B 0x106C
|
|
+#define LANE_REG041C 0x1070
|
|
+#define LANE_REG041D 0x1074
|
|
+#define LANE_REG041E 0x1078
|
|
+#define LANE_REG041F 0x107C
|
|
+#define LANE_REG0420 0x1080
|
|
+#define LANE_REG0421 0x1084
|
|
+#define LANE_REG0422 0x1088
|
|
+#define LANE_REG0423 0x108C
|
|
+#define LANE_REG0424 0x1090
|
|
+#define LANE_REG0425 0x1094
|
|
+#define LANE_REG0426 0x1098
|
|
+#define LANE_REG0427 0x109C
|
|
+#define LANE_REG0428 0x10A0
|
|
+#define LANE_REG0429 0x10A4
|
|
+#define LANE_REG042A 0x10A8
|
|
+#define LANE_REG042B 0x10AC
|
|
+#define LANE_REG042C 0x10B0
|
|
+#define LANE_REG042D 0x10B4
|
|
+#define LANE_REG0500 0x1400
|
|
+#define LANE_REG0501 0x1404
|
|
+#define LANE_REG0502 0x1408
|
|
+#define LANE_REG0503 0x140C
|
|
+#define LANE_REG0504 0x1410
|
|
+#define LANE_REG0505 0x1414
|
|
+#define LANE_REG0506 0x1418
|
|
+#define LANE_REG0507 0x141C
|
|
+#define LANE_REG0508 0x1420
|
|
+#define LANE_REG0509 0x1424
|
|
+#define LANE_REG050A 0x1428
|
|
+#define LANE_REG050B 0x142C
|
|
+#define LANE_REG050C 0x1430
|
|
+#define LANE_REG050D 0x1434
|
|
+#define LANE_REG050E 0x1438
|
|
+#define LANE_REG050F 0x143C
|
|
+#define LANE_REG0510 0x1440
|
|
+#define LANE_REG0511 0x1444
|
|
+#define LANE_REG0512 0x1448
|
|
+#define LN2_TX_SER_RATE_SEL_RBR BIT(5)
|
|
+#define LN2_TX_SER_RATE_SEL_HBR BIT(4)
|
|
+#define LN2_TX_SER_RATE_SEL_HBR2 BIT(3)
|
|
+#define LN2_TX_SER_RATE_SEL_HBR3 BIT(2)
|
|
+#define LANE_REG0513 0x144C
|
|
+#define LANE_REG0514 0x1450
|
|
+#define LANE_REG0515 0x1454
|
|
+#define LANE_REG0516 0x1458
|
|
+#define LANE_REG0517 0x145C
|
|
+#define LANE_REG0518 0x1460
|
|
+#define LANE_REG0519 0x1464
|
|
+#define LANE_REG051A 0x1468
|
|
+#define LANE_REG051B 0x146C
|
|
+#define LANE_REG051C 0x1470
|
|
+#define LANE_REG051D 0x1474
|
|
+#define LANE_REG051E 0x1478
|
|
+#define LANE_REG051F 0x147C
|
|
+#define LANE_REG0520 0x1480
|
|
+#define LANE_REG0521 0x1484
|
|
+#define LANE_REG0522 0x1488
|
|
+#define LANE_REG0523 0x148C
|
|
+#define LANE_REG0524 0x1490
|
|
+#define LANE_REG0525 0x1494
|
|
+#define LANE_REG0526 0x1498
|
|
+#define LANE_REG0527 0x149C
|
|
+#define LANE_REG0528 0x14A0
|
|
+#define LANE_REG0529 0x14AD
|
|
+#define LANE_REG052A 0x14A8
|
|
+#define LANE_REG052B 0x14AC
|
|
+#define LANE_REG052C 0x14B0
|
|
+#define LANE_REG052D 0x14B4
|
|
+#define LANE_REG0600 0x1800
|
|
+#define LANE_REG0601 0x1804
|
|
+#define LANE_REG0602 0x1808
|
|
+#define LANE_REG0603 0x180C
|
|
+#define LANE_REG0604 0x1810
|
|
+#define LANE_REG0605 0x1814
|
|
+#define LANE_REG0606 0x1818
|
|
+#define LANE_REG0607 0x181C
|
|
+#define LANE_REG0608 0x1820
|
|
+#define LANE_REG0609 0x1824
|
|
+#define LANE_REG060A 0x1828
|
|
+#define LANE_REG060B 0x182C
|
|
+#define LANE_REG060C 0x1830
|
|
+#define LANE_REG060D 0x1834
|
|
+#define LANE_REG060E 0x1838
|
|
+#define LANE_REG060F 0x183C
|
|
+#define LANE_REG0610 0x1840
|
|
+#define LANE_REG0611 0x1844
|
|
+#define LANE_REG0612 0x1848
|
|
+#define LN3_TX_SER_RATE_SEL_RBR BIT(5)
|
|
+#define LN3_TX_SER_RATE_SEL_HBR BIT(4)
|
|
+#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3)
|
|
+#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2)
|
|
+#define LANE_REG0613 0x184C
|
|
+#define LANE_REG0614 0x1850
|
|
+#define LANE_REG0615 0x1854
|
|
+#define LANE_REG0616 0x1858
|
|
+#define LANE_REG0617 0x185C
|
|
+#define LANE_REG0618 0x1860
|
|
+#define LANE_REG0619 0x1864
|
|
+#define LANE_REG061A 0x1868
|
|
+#define LANE_REG061B 0x186C
|
|
+#define LANE_REG061C 0x1870
|
|
+#define LANE_REG061D 0x1874
|
|
+#define LANE_REG061E 0x1878
|
|
+#define LANE_REG061F 0x187C
|
|
+#define LANE_REG0620 0x1880
|
|
+#define LANE_REG0621 0x1884
|
|
+#define LANE_REG0622 0x1888
|
|
+#define LANE_REG0623 0x188C
|
|
+#define LANE_REG0624 0x1890
|
|
+#define LANE_REG0625 0x1894
|
|
+#define LANE_REG0626 0x1898
|
|
+#define LANE_REG0627 0x189C
|
|
+#define LANE_REG0628 0x18A0
|
|
+#define LANE_REG0629 0x18A4
|
|
+#define LANE_REG062A 0x18A8
|
|
+#define LANE_REG062B 0x18AC
|
|
+#define LANE_REG062C 0x18B0
|
|
+#define LANE_REG062D 0x18B4
|
|
+
|
|
+#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)
|
|
+
|
|
+enum hdptx_combphy_type {
|
|
+ SS_HDMI,
|
|
+ SS_DP
|
|
+};
|
|
+
|
|
+
|
|
+struct lcpll_config {
|
|
+ u32 bit_rate;
|
|
+ u8 lcvco_mode_en;
|
|
+ u8 pi_en;
|
|
+ u8 clk_en_100m;
|
|
+ u8 pms_mdiv;
|
|
+ u8 pms_mdiv_afc;
|
|
+ u8 pms_pdiv;
|
|
+ u8 pms_refdiv;
|
|
+ u8 pms_sdiv;
|
|
+ u8 pi_cdiv_rstn;
|
|
+ u8 pi_cdiv_sel;
|
|
+ u8 sdm_en;
|
|
+ u8 sdm_rstn;
|
|
+ u8 sdc_frac_en;
|
|
+ u8 sdc_rstn;
|
|
+ u8 sdm_deno;
|
|
+ u8 sdm_num_sign;
|
|
+ u8 sdm_num;
|
|
+ u8 sdc_n;
|
|
+ u8 sdc_n2;
|
|
+ u8 sdc_num;
|
|
+ u8 sdc_deno;
|
|
+ u8 sdc_ndiv_rstn;
|
|
+ u8 ssc_en;
|
|
+ u8 ssc_fm_dev;
|
|
+ u8 ssc_fm_freq;
|
|
+ u8 ssc_clk_div_sel;
|
|
+ u8 cd_tx_ser_rate_sel;
|
|
+};
|
|
+
|
|
+struct ropll_config {
|
|
+ u32 bit_rate;
|
|
+ u8 pms_mdiv;
|
|
+ u8 pms_mdiv_afc;
|
|
+ u8 pms_pdiv;
|
|
+ u8 pms_refdiv;
|
|
+ u8 pms_sdiv;
|
|
+ u8 pms_iqdiv_rstn;
|
|
+ u8 ref_clk_sel;
|
|
+ u8 sdm_en;
|
|
+ u8 sdm_rstn;
|
|
+ u8 sdc_frac_en;
|
|
+ u8 sdc_rstn;
|
|
+ u8 sdm_clk_div;
|
|
+ u8 sdm_deno;
|
|
+ u8 sdm_num_sign;
|
|
+ u8 sdm_num;
|
|
+ u8 sdc_n;
|
|
+ u8 sdc_num;
|
|
+ u8 sdc_deno;
|
|
+ u8 sdc_ndiv_rstn;
|
|
+ u8 ssc_en;
|
|
+ u8 ssc_fm_dev;
|
|
+ u8 ssc_fm_freq;
|
|
+ u8 ssc_clk_div_sel;
|
|
+ u8 ana_cpp_ctrl;
|
|
+ u8 ana_lpf_c_sel;
|
|
+ u8 cd_tx_ser_rate_sel;
|
|
+};
|
|
+
|
|
+struct rockchip_hdptx_phy {
|
|
+ struct device *dev;
|
|
+ struct regmap *regmap;
|
|
+ struct regmap *grf;
|
|
+
|
|
+ int irq;
|
|
+ int id;
|
|
+
|
|
+ struct phy *phy;
|
|
+ struct clk_bulk_data *clks;
|
|
+ int nr_clks;
|
|
+ struct phy_config *phy_cfg;
|
|
+
|
|
+ /* clk provider */
|
|
+ struct clk_hw hw;
|
|
+ struct clk *dclk;
|
|
+ unsigned long rate;
|
|
+
|
|
+ struct reset_control *phy_reset;
|
|
+ struct reset_control *apb_reset;
|
|
+ struct reset_control *cmn_reset;
|
|
+ struct reset_control *init_reset;
|
|
+ struct reset_control *lane_reset;
|
|
+ struct reset_control *ropll_reset;
|
|
+ struct reset_control *lcpll_reset;
|
|
+
|
|
+ bool earc_en;
|
|
+ int count;
|
|
+};
|
|
+
|
|
+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,
|
|
+ },
|
|
+ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0, 0, 0,
|
|
+ },
|
|
+};
|
|
+
|
|
+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,
|
|
+ },
|
|
+ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0,
|
|
+ },
|
|
+};
|
|
+
|
|
+struct ropll_config ropll_tmds_cfg[] = {
|
|
+ { 5940000, 124, 124, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 3712500, 155, 155, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 2970000, 124, 124, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1620000, 135, 135, 1, 1, 3, 1, 1, 0, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10,
|
|
+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1856250, 155, 155, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1540000, 193, 193, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 193, 1, 32, 2, 1,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1485000, 0x7b, 0x7b, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 4, 0, 3, 5, 5, 0x10,
|
|
+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1462500, 122, 122, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 244, 1, 16, 2, 1, 1,
|
|
+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1190000, 149, 149, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 149, 1, 16, 2, 1, 1,
|
|
+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1065000, 89, 89, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 89, 1, 16, 1, 0, 1,
|
|
+ 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 1080000, 135, 135, 1, 1, 5, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14,
|
|
+ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 855000, 214, 214, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 214, 1, 16, 2, 1,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 835000, 105, 105, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 42, 1, 16, 1, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 928125, 155, 155, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 742500, 124, 124, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 62, 1, 16, 5, 0,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 650000, 162, 162, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 54, 0, 16, 4, 1,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 337500, 0x70, 0x70, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 0x2, 0, 0x01, 5, 1,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 400000, 100, 100, 1, 1, 11, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14,
|
|
+ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 270000, 0x5a, 0x5a, 1, 1, 0xf, 1, 1, 0, 1, 0, 1, 1, 0x9, 0, 0x05, 0, 0x14,
|
|
+ 0x18, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { 251750, 84, 84, 1, 1, 0xf, 1, 1, 1, 1, 1, 1, 1, 168, 1, 16, 4, 1,
|
|
+ 1, 1, 0, 0x20, 0x0c, 1, 0x0e, 0, 0,
|
|
+ },
|
|
+ { ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
+ 0, 0, 0, 0,
|
|
+ },
|
|
+};
|
|
+
|
|
+static bool rockchip_hdptx_phy_is_accissible_reg(struct device *dev,
|
|
+ unsigned int reg)
|
|
+{
|
|
+ switch (reg) {
|
|
+ case 0x0000 ... 0x029c:
|
|
+ case 0x0400 ... 0x04a4:
|
|
+ case 0x0800 ... 0x08a4:
|
|
+ case 0x0c00 ... 0x0cb4:
|
|
+ case 0x1000 ... 0x10b4:
|
|
+ case 0x1400 ... 0x14b4:
|
|
+ case 0x1800 ... 0x18b4:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct regmap_config rockchip_hdptx_phy_regmap_config = {
|
|
+ .reg_bits = 32,
|
|
+ .reg_stride = 4,
|
|
+ .val_bits = 32,
|
|
+ .fast_io = true,
|
|
+ .max_register = 0x18b4,
|
|
+ .name = "hdptx-combphy",
|
|
+
|
|
+ .readable_reg = rockchip_hdptx_phy_is_accissible_reg,
|
|
+ .writeable_reg = rockchip_hdptx_phy_is_accissible_reg,
|
|
+};
|
|
+
|
|
+static inline struct rockchip_hdptx_phy *to_rockchip_hdptx_phy(struct clk_hw *hw)
|
|
+{
|
|
+ return container_of(hw, struct rockchip_hdptx_phy, hw);
|
|
+}
|
|
+
|
|
+static inline void hdptx_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u8 val)
|
|
+{
|
|
+ regmap_write(hdptx->regmap, reg, val);
|
|
+}
|
|
+
|
|
+static inline u8 hdptx_read(struct rockchip_hdptx_phy *hdptx, u32 reg)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ regmap_read(hdptx->regmap, reg, &val);
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static inline void hdptx_update_bits(struct rockchip_hdptx_phy *hdptx, u32 reg,
|
|
+ u8 mask, u8 val)
|
|
+{
|
|
+ regmap_update_bits(hdptx->regmap, reg, mask, val);
|
|
+}
|
|
+
|
|
+static inline void hdptx_grf_write(struct rockchip_hdptx_phy *hdptx, u32 reg, u32 val)
|
|
+{
|
|
+ regmap_write(hdptx->grf, reg, val);
|
|
+}
|
|
+
|
|
+static inline u8 hdptx_grf_read(struct rockchip_hdptx_phy *hdptx, u32 reg)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ regmap_read(hdptx->grf, reg, &val);
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void hdptx_pre_power_up(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ u32 val = 0;
|
|
+
|
|
+ reset_control_assert(hdptx->apb_reset);
|
|
+ udelay(20);
|
|
+ reset_control_deassert(hdptx->apb_reset);
|
|
+
|
|
+ reset_control_assert(hdptx->lane_reset);
|
|
+ reset_control_assert(hdptx->cmn_reset);
|
|
+ reset_control_assert(hdptx->init_reset);
|
|
+
|
|
+ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+}
|
|
+
|
|
+static int hdptx_post_enable_lane(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ u32 val = 0;
|
|
+ int i;
|
|
+
|
|
+ reset_control_deassert(hdptx->lane_reset);
|
|
+
|
|
+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN |
|
|
+ HDPTX_I_BGR_EN;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+
|
|
+ for (i = 0; i < 50; i++) {
|
|
+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS);
|
|
+
|
|
+ if (val & HDPTX_O_PHY_RDY && val & HDPTX_O_PLL_LOCK_DONE)
|
|
+ break;
|
|
+ udelay(100);
|
|
+ }
|
|
+
|
|
+ if (i == 50) {
|
|
+ dev_err(hdptx->dev, "hdptx phy lane can't ready!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_err(hdptx->dev, "hdptx phy lane locked!\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hdptx_post_enable_pll(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ u32 val = 0;
|
|
+ int i;
|
|
+
|
|
+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN |
|
|
+ HDPTX_I_BGR_EN;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+ udelay(10);
|
|
+ reset_control_deassert(hdptx->init_reset);
|
|
+ udelay(10);
|
|
+ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+ udelay(10);
|
|
+ reset_control_deassert(hdptx->cmn_reset);
|
|
+
|
|
+ for (i = 0; i < 20; i++) {
|
|
+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS);
|
|
+
|
|
+ if (val & HDPTX_O_PHY_CLK_RDY)
|
|
+ break;
|
|
+ udelay(20);
|
|
+ }
|
|
+
|
|
+ if (i == 20) {
|
|
+ dev_err(hdptx->dev, "hdptx phy pll can't lock!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_err(hdptx->dev, "hdptx phy pll locked!\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hdptx_post_power_up(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ u32 val = 0;
|
|
+ int i;
|
|
+
|
|
+ val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | HDPTX_I_BIAS_EN |
|
|
+ HDPTX_I_BGR_EN;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+ udelay(10);
|
|
+ reset_control_deassert(hdptx->init_reset);
|
|
+ udelay(10);
|
|
+ val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+ udelay(10);
|
|
+ reset_control_deassert(hdptx->cmn_reset);
|
|
+
|
|
+ for (i = 0; i < 20; i++) {
|
|
+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS);
|
|
+
|
|
+ if (val & HDPTX_O_PLL_LOCK_DONE)
|
|
+ break;
|
|
+ udelay(20);
|
|
+ }
|
|
+
|
|
+ if (i == 20) {
|
|
+ dev_err(hdptx->dev, "hdptx phy can't lock!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ udelay(20);
|
|
+
|
|
+ reset_control_deassert(hdptx->lane_reset);
|
|
+
|
|
+ for (i = 0; i < 50; i++) {
|
|
+ val = hdptx_grf_read(hdptx, GRF_HDPTX_STATUS);
|
|
+
|
|
+ if (val & HDPTX_O_PHY_RDY)
|
|
+ break;
|
|
+ udelay(100);
|
|
+ }
|
|
+
|
|
+ if (i == 50) {
|
|
+ dev_err(hdptx->dev, "hdptx phy can't ready!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_err(hdptx->dev, "hdptx phy locked!\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void hdptx_phy_disable(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ /* reset phy and apb, or phy locked flag may keep 1 */
|
|
+ reset_control_assert(hdptx->phy_reset);
|
|
+ udelay(20);
|
|
+ reset_control_deassert(hdptx->phy_reset);
|
|
+
|
|
+ reset_control_assert(hdptx->apb_reset);
|
|
+ udelay(20);
|
|
+ reset_control_deassert(hdptx->apb_reset);
|
|
+
|
|
+ hdptx_write(hdptx, LANE_REG0300, 0x82);
|
|
+ hdptx_write(hdptx, SB_REG010F, 0xc1);
|
|
+ hdptx_write(hdptx, SB_REG0110, 0x1);
|
|
+ hdptx_write(hdptx, LANE_REG0301, 0x80);
|
|
+ hdptx_write(hdptx, LANE_REG0401, 0x80);
|
|
+ hdptx_write(hdptx, LANE_REG0501, 0x80);
|
|
+ hdptx_write(hdptx, LANE_REG0601, 0x80);
|
|
+
|
|
+ reset_control_assert(hdptx->lane_reset);
|
|
+ reset_control_assert(hdptx->cmn_reset);
|
|
+ reset_control_assert(hdptx->init_reset);
|
|
+
|
|
+ val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16;
|
|
+ hdptx_grf_write(hdptx, GRF_HDPTX_CON0, val);
|
|
+}
|
|
+
|
|
+static void hdptx_earc_config(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RCAL_OPT_CODE_MASK,
|
|
+ SB_RX_RCAL_OPT_CODE(1));
|
|
+ hdptx_write(hdptx, SB_REG011C, 0x04);
|
|
+ hdptx_update_bits(hdptx, SB_REG011B, SB_AFC_TOL_MASK,
|
|
+ SB_AFC_TOL(3));
|
|
+ hdptx_write(hdptx, SB_REG0109, 0x05);
|
|
+ hdptx_update_bits(hdptx, SB_REG0120, SB_EARC_EN_MASK | SB_EARC_AFC_EN_MASK,
|
|
+ SB_EARC_EN(1) | SB_EARC_AFC_EN(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG011B, SB_EARC_SIG_DET_BYPASS_MASK,
|
|
+ SB_EARC_SIG_DET_BYPASS(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG011F, SB_PWM_AFC_CTRL_MASK | SB_RCAL_RSTN_MASK,
|
|
+ SB_PWM_AFC_CTRL(0xc) | SB_RCAL_RSTN(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG0115, SB_READY_DELAY_TIME_MASK,
|
|
+ SB_READY_DELAY_TIME(2));
|
|
+ hdptx_update_bits(hdptx, SB_REG0113, SB_RX_RTERM_CTRL_MASK,
|
|
+ SB_RX_RTERM_CTRL(3));
|
|
+ hdptx_update_bits(hdptx, SB_REG0102, ANA_SB_RXTERM_OFFSP_MASK,
|
|
+ ANA_SB_RXTERM_OFFSP(3));
|
|
+ hdptx_update_bits(hdptx, SB_REG0103, ANA_SB_RXTERM_OFFSN_MASK,
|
|
+ ANA_SB_RXTERM_OFFSN(3));
|
|
+ hdptx_write(hdptx, SB_REG011A, 0x03);
|
|
+ hdptx_write(hdptx, SB_REG0118, 0x0a);
|
|
+ hdptx_write(hdptx, SB_REG011E, 0x6a);
|
|
+ hdptx_write(hdptx, SB_REG011D, 0x67);
|
|
+ hdptx_update_bits(hdptx, SB_REG0117, FAST_PULSE_TIME_MASK,
|
|
+ FAST_PULSE_TIME(4));
|
|
+ hdptx_update_bits(hdptx, SB_REG0114, SB_TG_SB_EN_DELAY_TIME_MASK |
|
|
+ SB_TG_RXTERM_EN_DELAY_TIME_MASK,
|
|
+ SB_TG_SB_EN_DELAY_TIME(2) |
|
|
+ SB_TG_RXTERM_EN_DELAY_TIME(2));
|
|
+ hdptx_update_bits(hdptx, SB_REG0105, ANA_SB_TX_HLVL_PROG_MASK,
|
|
+ ANA_SB_TX_HLVL_PROG(7));
|
|
+ hdptx_update_bits(hdptx, SB_REG0106, ANA_SB_TX_LLVL_PROG_MASK,
|
|
+ ANA_SB_TX_LLVL_PROG(7));
|
|
+ hdptx_update_bits(hdptx, SB_REG010F, ANA_SB_VREG_GAIN_CTRL_MASK,
|
|
+ ANA_SB_VREG_GAIN_CTRL(0));
|
|
+ hdptx_update_bits(hdptx, SB_REG0110, ANA_SB_VREG_REF_SEL_MASK,
|
|
+ ANA_SB_VREG_REF_SEL(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG0115, SB_TG_OSC_EN_DELAY_TIME_MASK,
|
|
+ SB_TG_OSC_EN_DELAY_TIME(2));
|
|
+ hdptx_update_bits(hdptx, SB_REG0116, AFC_RSTN_DELAY_TIME_MASK,
|
|
+ AFC_RSTN_DELAY_TIME(2));
|
|
+ hdptx_update_bits(hdptx, SB_REG0109, ANA_SB_DMRX_AFC_DIV_RATIO_MASK,
|
|
+ ANA_SB_DMRX_AFC_DIV_RATIO(5));
|
|
+ hdptx_update_bits(hdptx, SB_REG0103, OVRD_SB_RX_RESCAL_DONE_MASK,
|
|
+ OVRD_SB_RX_RESCAL_DONE(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG0104, OVRD_SB_EN_MASK,
|
|
+ OVRD_SB_EN(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG0102, OVRD_SB_RXTERM_EN_MASK,
|
|
+ OVRD_SB_RXTERM_EN(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG0105, OVRD_SB_EARC_CMDC_EN_MASK,
|
|
+ OVRD_SB_EARC_CMDC_EN(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_EN_MASK |
|
|
+ OVRD_SB_VREG_LPF_BYPASS_MASK,
|
|
+ OVRD_SB_VREG_EN(1) | OVRD_SB_VREG_LPF_BYPASS(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG0123, OVRD_SB_READY_MASK,
|
|
+ OVRD_SB_READY(1));
|
|
+ udelay(1000);
|
|
+ hdptx_update_bits(hdptx, SB_REG0103, SB_RX_RESCAL_DONE_MASK,
|
|
+ SB_RX_RESCAL_DONE(1));
|
|
+ udelay(50);
|
|
+ hdptx_update_bits(hdptx, SB_REG0104, SB_EN_MASK, SB_EN(1));
|
|
+ udelay(50);
|
|
+ hdptx_update_bits(hdptx, SB_REG0102, SB_RXTERM_EN_MASK,
|
|
+ SB_RXTERM_EN(1));
|
|
+ udelay(50);
|
|
+ hdptx_update_bits(hdptx, SB_REG0105, SB_EARC_CMDC_EN_MASK,
|
|
+ SB_EARC_CMDC_EN(1));
|
|
+ hdptx_update_bits(hdptx, SB_REG010F, SB_VREG_EN_MASK,
|
|
+ SB_VREG_EN(1));
|
|
+ udelay(50);
|
|
+ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK,
|
|
+ OVRD_SB_VREG_LPF_BYPASS(1));
|
|
+ udelay(250);
|
|
+ hdptx_update_bits(hdptx, SB_REG010F, OVRD_SB_VREG_LPF_BYPASS_MASK,
|
|
+ OVRD_SB_VREG_LPF_BYPASS(0));
|
|
+ udelay(100);
|
|
+ hdptx_update_bits(hdptx, SB_REG0123, SB_READY_MASK, SB_READY(1));
|
|
+}
|
|
+
|
|
+static bool hdptx_phy_clk_pll_calc(unsigned int data_rate,
|
|
+ struct ropll_config *cfg)
|
|
+{
|
|
+ unsigned int fref = 24000;
|
|
+ unsigned int sdc;
|
|
+ unsigned int fout = data_rate / 2;
|
|
+ unsigned int fvco;
|
|
+ u32 mdiv, sdiv, n = 8;
|
|
+ unsigned long k = 0, lc, k_sub, lc_sub;
|
|
+
|
|
+ for (sdiv = 16; sdiv >= 1; sdiv--) {
|
|
+ if (sdiv % 2 && sdiv != 1)
|
|
+ continue;
|
|
+
|
|
+ fvco = fout * sdiv;
|
|
+
|
|
+ if (fvco < 2000000 || fvco > 4000000)
|
|
+ continue;
|
|
+
|
|
+ mdiv = DIV_ROUND_UP(fvco, fref);
|
|
+ if (mdiv < 20 || mdiv > 255)
|
|
+ continue;
|
|
+
|
|
+ if (fref * mdiv - fvco) {
|
|
+ for (sdc = 264000; sdc <= 750000; sdc += fref)
|
|
+ if (sdc * n > fref * mdiv)
|
|
+ break;
|
|
+
|
|
+ if (sdc > 750000)
|
|
+ continue;
|
|
+
|
|
+ rational_best_approximation(fref * mdiv - fvco,
|
|
+ sdc / 16,
|
|
+ GENMASK(6, 0),
|
|
+ GENMASK(7, 0),
|
|
+ &k, &lc);
|
|
+
|
|
+ rational_best_approximation(sdc * n - fref * mdiv,
|
|
+ sdc,
|
|
+ GENMASK(6, 0),
|
|
+ GENMASK(7, 0),
|
|
+ &k_sub, &lc_sub);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (sdiv < 1)
|
|
+ return false;
|
|
+
|
|
+ if (cfg) {
|
|
+ cfg->pms_mdiv = mdiv;
|
|
+ cfg->pms_mdiv_afc = mdiv;
|
|
+ cfg->pms_pdiv = 1;
|
|
+ cfg->pms_refdiv = 1;
|
|
+ cfg->pms_sdiv = sdiv - 1;
|
|
+
|
|
+ cfg->sdm_en = k > 0 ? 1 : 0;
|
|
+ if (cfg->sdm_en) {
|
|
+ cfg->sdm_deno = lc;
|
|
+ cfg->sdm_num_sign = 1;
|
|
+ cfg->sdm_num = k;
|
|
+ cfg->sdc_n = n - 3;
|
|
+ cfg->sdc_num = k_sub;
|
|
+ cfg->sdc_deno = lc_sub;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static int hdptx_ropll_cmn_config(struct rockchip_hdptx_phy *hdptx, unsigned long bit_rate)
|
|
+{
|
|
+ int bus_width = phy_get_bus_width(hdptx->phy);
|
|
+ u8 color_depth = (bus_width & COLOR_DEPTH_MASK) ? 1 : 0;
|
|
+ struct ropll_config *cfg = ropll_tmds_cfg;
|
|
+ struct ropll_config rc = {0};
|
|
+
|
|
+ dev_info(hdptx->dev, "%s bus_width:%x rate:%lu\n", __func__, bus_width, bit_rate);
|
|
+ hdptx->rate = bit_rate * 100;
|
|
+
|
|
+ if (color_depth)
|
|
+ bit_rate = bit_rate * 10 / 8;
|
|
+
|
|
+ for (; cfg->bit_rate != ~0; cfg++)
|
|
+ if (bit_rate == cfg->bit_rate)
|
|
+ break;
|
|
+
|
|
+ if (cfg->bit_rate == ~0) {
|
|
+ if (hdptx_phy_clk_pll_calc(bit_rate, &rc)) {
|
|
+ cfg = &rc;
|
|
+ } else {
|
|
+ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev_dbg(hdptx->dev, "mdiv=%u, sdiv=%u\n",
|
|
+ cfg->pms_mdiv, cfg->pms_sdiv + 1);
|
|
+ dev_dbg(hdptx->dev, "sdm_en=%u, k_sign=%u, k=%u, lc=%u",
|
|
+ cfg->sdm_en, cfg->sdm_num_sign, cfg->sdm_num, cfg->sdm_deno);
|
|
+ dev_dbg(hdptx->dev, "n=%u, k_sub=%u, lc_sub=%u\n",
|
|
+ cfg->sdc_n + 3, cfg->sdc_num, cfg->sdc_deno);
|
|
+
|
|
+ hdptx_pre_power_up(hdptx);
|
|
+
|
|
+ reset_control_assert(hdptx->ropll_reset);
|
|
+ udelay(20);
|
|
+ reset_control_deassert(hdptx->ropll_reset);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG0008, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0009, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG000A, 0x83);
|
|
+ hdptx_write(hdptx, CMN_REG000B, 0x06);
|
|
+ hdptx_write(hdptx, CMN_REG000C, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG000D, 0xb8);
|
|
+ hdptx_write(hdptx, CMN_REG000E, 0x0f);
|
|
+ hdptx_write(hdptx, CMN_REG000F, 0x0f);
|
|
+ hdptx_write(hdptx, CMN_REG0010, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0011, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0012, 0x26);
|
|
+ hdptx_write(hdptx, CMN_REG0013, 0x22);
|
|
+ hdptx_write(hdptx, CMN_REG0014, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0015, 0x77);
|
|
+ hdptx_write(hdptx, CMN_REG0016, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG0017, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0018, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0019, 0x48);
|
|
+ hdptx_write(hdptx, CMN_REG001A, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG001B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG001C, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG001D, 0x64);
|
|
+ hdptx_write(hdptx, CMN_REG001E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG001F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0020, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0021, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0022, 0x11);
|
|
+ hdptx_write(hdptx, CMN_REG0023, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0024, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0025, 0x53);
|
|
+ hdptx_write(hdptx, CMN_REG0026, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0027, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0028, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0029, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG002A, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002C, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002D, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002E, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG002F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0030, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0031, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0032, 0x0b);
|
|
+ hdptx_write(hdptx, CMN_REG0033, 0x23);
|
|
+ hdptx_write(hdptx, CMN_REG0034, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0035, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0038, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0039, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003A, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003C, 0x80);
|
|
+ hdptx_write(hdptx, CMN_REG003D, 0x40);
|
|
+ hdptx_write(hdptx, CMN_REG003E, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG003F, 0x83);
|
|
+ hdptx_write(hdptx, CMN_REG0040, 0x06);
|
|
+ hdptx_write(hdptx, CMN_REG0041, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0042, 0x78);
|
|
+ hdptx_write(hdptx, CMN_REG0043, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0044, 0x46);
|
|
+ hdptx_write(hdptx, CMN_REG0045, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0046, 0xff);
|
|
+ hdptx_write(hdptx, CMN_REG0047, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0048, 0x44);
|
|
+ hdptx_write(hdptx, CMN_REG0049, 0xfa);
|
|
+ hdptx_write(hdptx, CMN_REG004A, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG004B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG004C, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG004D, 0x64);
|
|
+ hdptx_write(hdptx, CMN_REG004E, 0x34);
|
|
+ hdptx_write(hdptx, CMN_REG004F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0050, 0x00);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv);
|
|
+ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4));
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG005C, 0x25);
|
|
+ hdptx_write(hdptx, CMN_REG005D, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG005E, 0x4f);
|
|
+ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK,
|
|
+ ROPLL_SDM_EN(cfg->sdm_en));
|
|
+ if (!cfg->sdm_en)
|
|
+ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG005F, 0x01);
|
|
+
|
|
+ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK,
|
|
+ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign));
|
|
+ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num);
|
|
+ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno);
|
|
+
|
|
+ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK,
|
|
+ ROPLL_SDC_N_RBR(cfg->sdc_n));
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num);
|
|
+ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG006B, 0x04);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG0073, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0074, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0075, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0076, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0077, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG0078, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG0079, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007C, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007D, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007E, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0080, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0081, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0082, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0083, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0084, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0085, 0x03);
|
|
+
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK,
|
|
+ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv));
|
|
+
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK,
|
|
+ PLL_PCG_CLK_SEL(color_depth));
|
|
+
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_EN, PLL_PCG_CLK_EN);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG0087, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0089, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG008A, 0x55);
|
|
+ hdptx_write(hdptx, CMN_REG008B, 0x25);
|
|
+ hdptx_write(hdptx, CMN_REG008C, 0x2c);
|
|
+ hdptx_write(hdptx, CMN_REG008D, 0x22);
|
|
+ hdptx_write(hdptx, CMN_REG008E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG008F, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0090, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0091, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0092, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0093, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0095, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0097, 0x02);
|
|
+ hdptx_write(hdptx, CMN_REG0099, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG009A, 0x11);
|
|
+ hdptx_write(hdptx, CMN_REG009B, 0x00);
|
|
+
|
|
+ return hdptx_post_enable_pll(hdptx);
|
|
+}
|
|
+
|
|
+static int hdptx_ropll_tmds_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate)
|
|
+{
|
|
+ u32 bit_rate = rate & DATA_RATE_MASK;
|
|
+
|
|
+ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)) {
|
|
+ int ret;
|
|
+
|
|
+ ret = hdptx_ropll_cmn_config(hdptx, bit_rate);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ hdptx_write(hdptx, SB_REG0114, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0115, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0116, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0117, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0200, 0x06);
|
|
+
|
|
+ if (bit_rate >= 3400000) {
|
|
+ /* For 1/40 bitrate clk */
|
|
+ hdptx_write(hdptx, LNTOP_REG0201, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0202, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0203, 0x0f);
|
|
+ hdptx_write(hdptx, LNTOP_REG0204, 0xff);
|
|
+ hdptx_write(hdptx, LNTOP_REG0205, 0xff);
|
|
+ } else {
|
|
+ /* For 1/10 bitrate clk */
|
|
+ hdptx_write(hdptx, LNTOP_REG0201, 0x07);
|
|
+ hdptx_write(hdptx, LNTOP_REG0202, 0xc1);
|
|
+ hdptx_write(hdptx, LNTOP_REG0203, 0xf0);
|
|
+ hdptx_write(hdptx, LNTOP_REG0204, 0x7c);
|
|
+ hdptx_write(hdptx, LNTOP_REG0205, 0x1f);
|
|
+ }
|
|
+
|
|
+ hdptx_write(hdptx, LNTOP_REG0206, 0x07);
|
|
+ hdptx_write(hdptx, LNTOP_REG0207, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0303, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0307, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG030A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG030B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0310, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0311, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0312, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG0316, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG031B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG031E, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG031F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0320, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0403, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0407, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG040A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG040B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0410, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0411, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0412, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG0416, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG041B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG041E, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG041F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0420, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0503, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0507, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG050A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG050B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0510, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0511, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0512, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG0516, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG051B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG051E, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG051F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0520, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0603, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0607, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG060A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG060B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0610, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0611, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0612, 0x00);
|
|
+ hdptx_write(hdptx, LANE_REG0616, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG061B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG061E, 0x08);
|
|
+ hdptx_write(hdptx, LANE_REG061F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0620, 0xa0);
|
|
+
|
|
+ hdptx_write(hdptx, LANE_REG0303, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0403, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0503, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0603, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0305, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0405, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0505, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0605, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0306, 0x1c);
|
|
+ hdptx_write(hdptx, LANE_REG0406, 0x1c);
|
|
+ hdptx_write(hdptx, LANE_REG0506, 0x1c);
|
|
+ hdptx_write(hdptx, LANE_REG0606, 0x1c);
|
|
+
|
|
+ if (hdptx->earc_en)
|
|
+ hdptx_earc_config(hdptx);
|
|
+
|
|
+ return hdptx_post_enable_lane(hdptx);
|
|
+}
|
|
+
|
|
+static int hdptx_ropll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate)
|
|
+{
|
|
+ u32 bit_rate = rate & DATA_RATE_MASK;
|
|
+ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0;
|
|
+ struct ropll_config *cfg = ropll_frl_cfg;
|
|
+
|
|
+ for (; cfg->bit_rate != ~0; cfg++)
|
|
+ if (bit_rate == cfg->bit_rate)
|
|
+ break;
|
|
+
|
|
+ if (cfg->bit_rate == ~0) {
|
|
+ dev_err(hdptx->dev, "%s can't find pll cfg\n", __func__);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ hdptx_pre_power_up(hdptx);
|
|
+
|
|
+ reset_control_assert(hdptx->ropll_reset);
|
|
+ usleep_range(10, 20);
|
|
+ reset_control_deassert(hdptx->ropll_reset);
|
|
+
|
|
+ hdptx_write(hdptx, CMN_REG0008, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0009, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG000A, 0x83);
|
|
+ hdptx_write(hdptx, CMN_REG000B, 0x06);
|
|
+ hdptx_write(hdptx, CMN_REG000C, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG000D, 0xb8);
|
|
+ hdptx_write(hdptx, CMN_REG000E, 0x0f);
|
|
+ hdptx_write(hdptx, CMN_REG000F, 0x0f);
|
|
+ hdptx_write(hdptx, CMN_REG0010, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0011, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0012, 0x26);
|
|
+ hdptx_write(hdptx, CMN_REG0013, 0x22);
|
|
+ hdptx_write(hdptx, CMN_REG0014, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0015, 0x77);
|
|
+ hdptx_write(hdptx, CMN_REG0016, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG0017, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0018, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0019, 0x48);
|
|
+ hdptx_write(hdptx, CMN_REG001A, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG001B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG001C, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG001D, 0x64);
|
|
+ hdptx_write(hdptx, CMN_REG001E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG001F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0020, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0021, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0022, 0x11);
|
|
+ hdptx_write(hdptx, CMN_REG0023, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0025, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0026, 0x53);
|
|
+ hdptx_write(hdptx, CMN_REG0027, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0028, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0029, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG002A, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG002B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002C, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002D, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002E, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG002F, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0030, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0031, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0032, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0033, 0x0b);
|
|
+ hdptx_write(hdptx, CMN_REG0034, 0x23);
|
|
+ hdptx_write(hdptx, CMN_REG0035, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0038, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0039, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003A, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003C, 0x80);
|
|
+ hdptx_write(hdptx, CMN_REG003D, 0x40);
|
|
+ hdptx_write(hdptx, CMN_REG003E, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG003F, 0x83);
|
|
+ hdptx_write(hdptx, CMN_REG0040, 0x06);
|
|
+ hdptx_write(hdptx, CMN_REG0041, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0042, 0xb8);
|
|
+ hdptx_write(hdptx, CMN_REG0043, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0044, 0x46);
|
|
+ hdptx_write(hdptx, CMN_REG0045, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0046, 0xff);
|
|
+ hdptx_write(hdptx, CMN_REG0047, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0048, 0x44);
|
|
+ hdptx_write(hdptx, CMN_REG0049, 0xfa);
|
|
+ hdptx_write(hdptx, CMN_REG004A, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG004B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG004C, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG004D, 0x64);
|
|
+ hdptx_write(hdptx, CMN_REG004E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG004F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0050, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0051, cfg->pms_mdiv);
|
|
+ hdptx_write(hdptx, CMN_REG0055, cfg->pms_mdiv_afc);
|
|
+ hdptx_write(hdptx, CMN_REG0059, (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
|
|
+ hdptx_write(hdptx, CMN_REG005A, (cfg->pms_sdiv << 4));
|
|
+ hdptx_write(hdptx, CMN_REG005C, 0x25);
|
|
+ hdptx_write(hdptx, CMN_REG005D, 0x0c);
|
|
+ hdptx_update_bits(hdptx, CMN_REG005E, ROPLL_SDM_EN_MASK,
|
|
+ ROPLL_SDM_EN(cfg->sdm_en));
|
|
+ if (!cfg->sdm_en)
|
|
+ hdptx_update_bits(hdptx, CMN_REG005E, 0xf, 0);
|
|
+ hdptx_write(hdptx, CMN_REG005F, 0x01);
|
|
+ hdptx_update_bits(hdptx, CMN_REG0064, ROPLL_SDM_NUM_SIGN_RBR_MASK,
|
|
+ ROPLL_SDM_NUM_SIGN_RBR(cfg->sdm_num_sign));
|
|
+ hdptx_write(hdptx, CMN_REG0065, cfg->sdm_num);
|
|
+ hdptx_write(hdptx, CMN_REG0060, cfg->sdm_deno);
|
|
+ hdptx_update_bits(hdptx, CMN_REG0069, ROPLL_SDC_N_RBR_MASK,
|
|
+ ROPLL_SDC_N_RBR(cfg->sdc_n));
|
|
+ hdptx_write(hdptx, CMN_REG006C, cfg->sdc_num);
|
|
+ hdptx_write(hdptx, CMN_REG0070, cfg->sdc_deno);
|
|
+ hdptx_write(hdptx, CMN_REG006B, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0073, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0074, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0075, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0076, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0077, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG0078, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG0079, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007C, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007D, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007E, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0080, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0081, 0x09);
|
|
+ hdptx_write(hdptx, CMN_REG0082, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0083, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0084, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0085, 0x03);
|
|
+ hdptx_write(hdptx, CMN_REG0086, 0x01);
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK,
|
|
+ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv));
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK,
|
|
+ PLL_PCG_CLK_SEL(color_depth));
|
|
+ hdptx_write(hdptx, CMN_REG0087, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG0089, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG008A, 0x55);
|
|
+ hdptx_write(hdptx, CMN_REG008B, 0x25);
|
|
+ hdptx_write(hdptx, CMN_REG008C, 0x2c);
|
|
+ hdptx_write(hdptx, CMN_REG008D, 0x22);
|
|
+ hdptx_write(hdptx, CMN_REG008E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG008F, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0090, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0091, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0092, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0093, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0094, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0097, 0x02);
|
|
+ hdptx_write(hdptx, CMN_REG0099, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG009A, 0x11);
|
|
+ hdptx_write(hdptx, CMN_REG009B, 0x10);
|
|
+ hdptx_write(hdptx, SB_REG0114, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0115, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0116, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0117, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0200, 0x04);
|
|
+ hdptx_write(hdptx, LNTOP_REG0201, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0202, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0203, 0xf0);
|
|
+ hdptx_write(hdptx, LNTOP_REG0204, 0xff);
|
|
+ hdptx_write(hdptx, LNTOP_REG0205, 0xff);
|
|
+ hdptx_write(hdptx, LNTOP_REG0206, 0x05);
|
|
+ hdptx_write(hdptx, LNTOP_REG0207, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0303, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0307, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG030A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG030B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0310, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0311, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0312, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0316, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG031B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG031F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0320, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0403, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0407, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG040A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG040B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0410, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0411, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0412, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0416, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG041B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG041F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0420, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0503, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0507, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG050A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG050B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0510, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0511, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0512, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0516, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG051B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG051F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0520, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0603, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0607, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG060A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG060B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0610, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0611, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0612, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0616, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG061B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG061F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0620, 0xa0);
|
|
+
|
|
+ if (hdptx->earc_en)
|
|
+ hdptx_earc_config(hdptx);
|
|
+
|
|
+ return hdptx_post_power_up(hdptx);
|
|
+}
|
|
+
|
|
+static int hdptx_lcpll_frl_mode_config(struct rockchip_hdptx_phy *hdptx, u32 rate)
|
|
+{
|
|
+ u32 bit_rate = rate & DATA_RATE_MASK;
|
|
+ u8 color_depth = (rate & COLOR_DEPTH_MASK) ? 1 : 0;
|
|
+ struct lcpll_config *cfg = lcpll_cfg;
|
|
+
|
|
+ for (; cfg->bit_rate != ~0; cfg++)
|
|
+ if (bit_rate == cfg->bit_rate)
|
|
+ break;
|
|
+
|
|
+ if (cfg->bit_rate == ~0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ hdptx_pre_power_up(hdptx);
|
|
+
|
|
+ hdptx_update_bits(hdptx, CMN_REG0008, LCPLL_EN_MASK |
|
|
+ LCPLL_LCVCO_MODE_EN_MASK, LCPLL_EN(1) |
|
|
+ LCPLL_LCVCO_MODE_EN(cfg->lcvco_mode_en));
|
|
+ hdptx_write(hdptx, CMN_REG0009, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG000A, 0x83);
|
|
+ hdptx_write(hdptx, CMN_REG000B, 0x06);
|
|
+ hdptx_write(hdptx, CMN_REG000C, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG000D, 0xb8);
|
|
+ hdptx_write(hdptx, CMN_REG000E, 0x0f);
|
|
+ hdptx_write(hdptx, CMN_REG000F, 0x0f);
|
|
+ hdptx_write(hdptx, CMN_REG0010, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0011, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0012, 0x26);
|
|
+ hdptx_write(hdptx, CMN_REG0013, 0x22);
|
|
+ hdptx_write(hdptx, CMN_REG0014, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0015, 0x77);
|
|
+ hdptx_write(hdptx, CMN_REG0016, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG0017, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0018, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0019, 0x48);
|
|
+ hdptx_write(hdptx, CMN_REG001A, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG001B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG001C, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG001D, 0x64);
|
|
+ hdptx_update_bits(hdptx, CMN_REG001E, LCPLL_PI_EN_MASK |
|
|
+ LCPLL_100M_CLK_EN_MASK,
|
|
+ LCPLL_PI_EN(cfg->pi_en) |
|
|
+ LCPLL_100M_CLK_EN(cfg->clk_en_100m));
|
|
+ hdptx_write(hdptx, CMN_REG001F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0020, cfg->pms_mdiv);
|
|
+ hdptx_write(hdptx, CMN_REG0021, cfg->pms_mdiv_afc);
|
|
+ hdptx_write(hdptx, CMN_REG0022, (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
|
|
+ hdptx_write(hdptx, CMN_REG0023, (cfg->pms_sdiv << 4) | cfg->pms_sdiv);
|
|
+ hdptx_write(hdptx, CMN_REG0025, 0x10);
|
|
+ hdptx_write(hdptx, CMN_REG0026, 0x53);
|
|
+ hdptx_write(hdptx, CMN_REG0027, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0028, 0x0d);
|
|
+ hdptx_write(hdptx, CMN_REG0029, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG002A, cfg->sdm_deno);
|
|
+ hdptx_write(hdptx, CMN_REG002B, cfg->sdm_num_sign);
|
|
+ hdptx_write(hdptx, CMN_REG002C, cfg->sdm_num);
|
|
+ hdptx_update_bits(hdptx, CMN_REG002D, LCPLL_SDC_N_MASK,
|
|
+ LCPLL_SDC_N(cfg->sdc_n));
|
|
+ hdptx_write(hdptx, CMN_REG002E, 0x02);
|
|
+ hdptx_write(hdptx, CMN_REG002F, 0x0d);
|
|
+ hdptx_write(hdptx, CMN_REG0030, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0031, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0032, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0033, 0x0b);
|
|
+ hdptx_write(hdptx, CMN_REG0034, 0x23);
|
|
+ hdptx_write(hdptx, CMN_REG0035, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0038, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0039, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003A, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003C, 0x80);
|
|
+ hdptx_write(hdptx, CMN_REG003D, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG003E, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG003F, 0x83);
|
|
+ hdptx_write(hdptx, CMN_REG0040, 0x06);
|
|
+ hdptx_write(hdptx, CMN_REG0041, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0042, 0xb8);
|
|
+ hdptx_write(hdptx, CMN_REG0043, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0044, 0x46);
|
|
+ hdptx_write(hdptx, CMN_REG0045, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0046, 0xff);
|
|
+ hdptx_write(hdptx, CMN_REG0047, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0048, 0x44);
|
|
+ hdptx_write(hdptx, CMN_REG0049, 0xfa);
|
|
+ hdptx_write(hdptx, CMN_REG004A, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG004B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG004C, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG004D, 0x64);
|
|
+ hdptx_write(hdptx, CMN_REG004E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG004F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0050, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0051, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0055, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0059, 0x11);
|
|
+ hdptx_write(hdptx, CMN_REG005A, 0x03);
|
|
+ hdptx_write(hdptx, CMN_REG005C, 0x05);
|
|
+ hdptx_write(hdptx, CMN_REG005D, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG005E, 0x07);
|
|
+ hdptx_write(hdptx, CMN_REG005F, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0060, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0064, 0x07);
|
|
+ hdptx_write(hdptx, CMN_REG0065, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0069, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG006B, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG006C, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0070, 0x01);
|
|
+ hdptx_write(hdptx, CMN_REG0073, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0074, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0075, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0076, 0x30);
|
|
+ hdptx_write(hdptx, CMN_REG0077, 0x08);
|
|
+ hdptx_write(hdptx, CMN_REG0078, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG0079, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007B, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007C, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007D, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007E, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG007F, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0080, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0081, 0x09);
|
|
+ hdptx_write(hdptx, CMN_REG0082, 0x04);
|
|
+ hdptx_write(hdptx, CMN_REG0083, 0x24);
|
|
+ hdptx_write(hdptx, CMN_REG0084, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0085, 0x03);
|
|
+ hdptx_write(hdptx, CMN_REG0086, 0x01);
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_POSTDIV_SEL_MASK,
|
|
+ PLL_PCG_POSTDIV_SEL(cfg->pms_sdiv));
|
|
+ hdptx_update_bits(hdptx, CMN_REG0086, PLL_PCG_CLK_SEL_MASK,
|
|
+ PLL_PCG_CLK_SEL(color_depth));
|
|
+ hdptx_write(hdptx, CMN_REG0087, 0x0c);
|
|
+ hdptx_write(hdptx, CMN_REG0089, 0x02);
|
|
+ hdptx_write(hdptx, CMN_REG008A, 0x55);
|
|
+ hdptx_write(hdptx, CMN_REG008B, 0x25);
|
|
+ hdptx_write(hdptx, CMN_REG008C, 0x2c);
|
|
+ hdptx_write(hdptx, CMN_REG008D, 0x22);
|
|
+ hdptx_write(hdptx, CMN_REG008E, 0x14);
|
|
+ hdptx_write(hdptx, CMN_REG008F, 0x20);
|
|
+ hdptx_write(hdptx, CMN_REG0090, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0091, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0092, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0093, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0095, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0097, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG0099, 0x00);
|
|
+ hdptx_write(hdptx, CMN_REG009A, 0x11);
|
|
+ hdptx_write(hdptx, CMN_REG009B, 0x10);
|
|
+ hdptx_write(hdptx, SB_REG0114, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0115, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0116, 0x00);
|
|
+ hdptx_write(hdptx, SB_REG0117, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0200, 0x04);
|
|
+ hdptx_write(hdptx, LNTOP_REG0201, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0202, 0x00);
|
|
+ hdptx_write(hdptx, LNTOP_REG0203, 0xf0);
|
|
+ hdptx_write(hdptx, LNTOP_REG0204, 0xff);
|
|
+ hdptx_write(hdptx, LNTOP_REG0205, 0xff);
|
|
+ hdptx_write(hdptx, LNTOP_REG0206, 0x05);
|
|
+ hdptx_write(hdptx, LNTOP_REG0207, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0303, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0307, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG030A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG030B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG030E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0310, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0311, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0312, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0316, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG031B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG031F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0320, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0403, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0407, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG040A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG040B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG040E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0410, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0411, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0412, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0416, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG041B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG041F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0420, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0503, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0507, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG050A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG050B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG050E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0510, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0511, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0512, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0516, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG051B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG051F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0520, 0xa0);
|
|
+ hdptx_write(hdptx, LANE_REG0603, 0x0c);
|
|
+ hdptx_write(hdptx, LANE_REG0607, 0x20);
|
|
+ hdptx_write(hdptx, LANE_REG060A, 0x17);
|
|
+ hdptx_write(hdptx, LANE_REG060B, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060C, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060D, 0x77);
|
|
+ hdptx_write(hdptx, LANE_REG060E, 0x38);
|
|
+ hdptx_write(hdptx, LANE_REG0610, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0611, 0x0f);
|
|
+ hdptx_write(hdptx, LANE_REG0612, 0x3c);
|
|
+ hdptx_write(hdptx, LANE_REG0616, 0x02);
|
|
+ hdptx_write(hdptx, LANE_REG061B, 0x01);
|
|
+ hdptx_write(hdptx, LANE_REG061F, 0x15);
|
|
+ hdptx_write(hdptx, LANE_REG0620, 0xa0);
|
|
+
|
|
+ hdptx_write(hdptx, LANE_REG0303, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0403, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0503, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0603, 0x2f);
|
|
+ hdptx_write(hdptx, LANE_REG0305, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0405, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0505, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0605, 0x03);
|
|
+ hdptx_write(hdptx, LANE_REG0306, 0xfc);
|
|
+ hdptx_write(hdptx, LANE_REG0406, 0xfc);
|
|
+ hdptx_write(hdptx, LANE_REG0506, 0xfc);
|
|
+ hdptx_write(hdptx, LANE_REG0606, 0xfc);
|
|
+
|
|
+ hdptx_write(hdptx, LANE_REG0305, 0x4f);
|
|
+ hdptx_write(hdptx, LANE_REG0405, 0x4f);
|
|
+ hdptx_write(hdptx, LANE_REG0505, 0x4f);
|
|
+ hdptx_write(hdptx, LANE_REG0605, 0x4f);
|
|
+ hdptx_write(hdptx, LANE_REG0304, 0x14);
|
|
+ hdptx_write(hdptx, LANE_REG0404, 0x14);
|
|
+ hdptx_write(hdptx, LANE_REG0504, 0x14);
|
|
+ hdptx_write(hdptx, LANE_REG0604, 0x14);
|
|
+
|
|
+ if (hdptx->earc_en)
|
|
+ hdptx_earc_config(hdptx);
|
|
+
|
|
+ return hdptx_post_power_up(hdptx);
|
|
+}
|
|
+
|
|
+static int rockchip_hdptx_phy_power_on(struct phy *phy)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy);
|
|
+ int bus_width = phy_get_bus_width(hdptx->phy);
|
|
+ int bit_rate = bus_width & DATA_RATE_MASK;
|
|
+ int ret;
|
|
+
|
|
+ if (!hdptx->count) {
|
|
+ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks);
|
|
+ if (ret) {
|
|
+ dev_err(hdptx->dev, "failed to enable clocks\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev_info(hdptx->dev, "bus_width:0x%x,bit_rate:%d\n", bus_width, bit_rate);
|
|
+ if (bus_width & HDMI_EARC_MASK)
|
|
+ hdptx->earc_en = true;
|
|
+ else
|
|
+ hdptx->earc_en = false;
|
|
+
|
|
+ if (bus_width & HDMI_MODE_MASK) {
|
|
+ if (bit_rate > 24000000)
|
|
+ return hdptx_lcpll_frl_mode_config(hdptx, bus_width);
|
|
+ else
|
|
+ return hdptx_ropll_frl_mode_config(hdptx, bus_width);
|
|
+ } else {
|
|
+ return hdptx_ropll_tmds_mode_config(hdptx, bus_width);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int rockchip_hdptx_phy_power_off(struct phy *phy)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = phy_get_drvdata(phy);
|
|
+
|
|
+ if (hdptx->count)
|
|
+ return 0;
|
|
+
|
|
+ if (!(hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE))
|
|
+ return 0;
|
|
+
|
|
+ hdptx_phy_disable(hdptx);
|
|
+ clk_bulk_disable(hdptx->nr_clks, hdptx->clks);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct phy_ops rockchip_hdptx_phy_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .power_on = rockchip_hdptx_phy_power_on,
|
|
+ .power_off = rockchip_hdptx_phy_power_off,
|
|
+};
|
|
+
|
|
+static const struct of_device_id rockchip_hdptx_phy_of_match[] = {
|
|
+ { .compatible = "rockchip,rk3588-hdptx-phy-hdmi",
|
|
+ },
|
|
+ {}
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, rockchip_hdptx_phy_of_match);
|
|
+
|
|
+static void rockchip_hdptx_phy_runtime_disable(void *data)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = data;
|
|
+
|
|
+ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks);
|
|
+ pm_runtime_disable(hdptx->dev);
|
|
+}
|
|
+
|
|
+static unsigned long hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
|
|
+ unsigned long parent_rate)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw);
|
|
+
|
|
+ return hdptx->rate;
|
|
+}
|
|
+
|
|
+static long hdptx_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
+ unsigned long *parent_rate)
|
|
+{
|
|
+ struct ropll_config *cfg = ropll_tmds_cfg;
|
|
+ u32 bit_rate = rate / 100;
|
|
+
|
|
+ if (rate > HDMI20_MAX_RATE)
|
|
+ return rate;
|
|
+
|
|
+ for (; cfg->bit_rate != ~0; cfg++)
|
|
+ if (bit_rate == cfg->bit_rate)
|
|
+ break;
|
|
+
|
|
+ if (cfg->bit_rate == ~0 && !hdptx_phy_clk_pll_calc(bit_rate, NULL))
|
|
+ return -EINVAL;
|
|
+
|
|
+ return rate;
|
|
+}
|
|
+
|
|
+static int hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
+ unsigned long parent_rate)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw);
|
|
+
|
|
+ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)
|
|
+ hdptx_phy_disable(hdptx);
|
|
+
|
|
+ rate = rate / 100;
|
|
+
|
|
+ return hdptx_ropll_cmn_config(hdptx, rate);
|
|
+}
|
|
+
|
|
+static int hdptx_phy_clk_enable(struct clk_hw *hw)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw);
|
|
+ int ret;
|
|
+
|
|
+ if (hdptx->count) {
|
|
+ hdptx->count++;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = clk_bulk_enable(hdptx->nr_clks, hdptx->clks);
|
|
+ if (ret) {
|
|
+ dev_err(hdptx->dev, "failed to enable clocks\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (hdptx->rate) {
|
|
+ ret = hdptx_ropll_cmn_config(hdptx, hdptx->rate / 100);
|
|
+ if (ret < 0) {
|
|
+ dev_err(hdptx->dev, "hdmi phy pll init failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ hdptx->count++;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void hdptx_phy_clk_disable(struct clk_hw *hw)
|
|
+{
|
|
+ struct rockchip_hdptx_phy *hdptx = to_rockchip_hdptx_phy(hw);
|
|
+
|
|
+ if (hdptx->count > 1) {
|
|
+ hdptx->count--;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (hdptx_grf_read(hdptx, GRF_HDPTX_STATUS) & HDPTX_O_PLL_LOCK_DONE)
|
|
+ hdptx_phy_disable(hdptx);
|
|
+ clk_bulk_disable(hdptx->nr_clks, hdptx->clks);
|
|
+ hdptx->count--;
|
|
+}
|
|
+
|
|
+static const struct clk_ops hdptx_phy_clk_ops = {
|
|
+ .recalc_rate = hdptx_phy_clk_recalc_rate,
|
|
+ .round_rate = hdptx_phy_clk_round_rate,
|
|
+ .set_rate = hdptx_phy_clk_set_rate,
|
|
+ .enable = hdptx_phy_clk_enable,
|
|
+ .disable = hdptx_phy_clk_disable,
|
|
+};
|
|
+
|
|
+static int rockchip_hdptx_phy_clk_register(struct rockchip_hdptx_phy *hdptx)
|
|
+{
|
|
+ struct device *dev = hdptx->dev;
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct device_node *clk_np;
|
|
+ struct platform_device *pdev;
|
|
+ struct clk_init_data init = {};
|
|
+ struct clk *refclk;
|
|
+ const char *parent_name;
|
|
+ int ret;
|
|
+
|
|
+ clk_np = of_get_child_by_name(np, "clk-port");
|
|
+ if (!clk_np)
|
|
+ return 0;
|
|
+
|
|
+ pdev = of_platform_device_create(clk_np, NULL, dev);
|
|
+ if (!pdev)
|
|
+ return 0;
|
|
+
|
|
+ refclk = devm_clk_get(dev, "ref");
|
|
+ if (IS_ERR(refclk)) {
|
|
+ dev_err(dev, "failed to get ref clock\n");
|
|
+ return PTR_ERR(refclk);
|
|
+ }
|
|
+
|
|
+ parent_name = __clk_get_name(refclk);
|
|
+
|
|
+ init.parent_names = &parent_name;
|
|
+ init.num_parents = 1;
|
|
+ init.flags = CLK_GET_RATE_NOCACHE;
|
|
+ if (!hdptx->id)
|
|
+ init.name = "clk_hdmiphy_pixel0";
|
|
+ else
|
|
+ init.name = "clk_hdmiphy_pixel1";
|
|
+ init.ops = &hdptx_phy_clk_ops;
|
|
+
|
|
+ /* optional override of the clock name */
|
|
+ of_property_read_string(np, "clock-output-names", &init.name);
|
|
+
|
|
+ hdptx->hw.init = &init;
|
|
+
|
|
+ hdptx->dclk = devm_clk_register(&pdev->dev, &hdptx->hw);
|
|
+ if (IS_ERR(hdptx->dclk)) {
|
|
+ ret = PTR_ERR(hdptx->dclk);
|
|
+ dev_err(dev, "failed to register clock: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = of_clk_add_provider(clk_np, of_clk_src_simple_get, hdptx->dclk);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to register OF clock provider: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rockchip_hdptx_phy_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct rockchip_hdptx_phy *hdptx;
|
|
+ struct phy_provider *phy_provider;
|
|
+ struct resource *res;
|
|
+ void __iomem *regs;
|
|
+ int ret;
|
|
+
|
|
+ hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL);
|
|
+ if (!hdptx)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ hdptx->dev = dev;
|
|
+
|
|
+ hdptx->id = of_alias_get_id(dev->of_node, "hdptxhdmi");
|
|
+ if (hdptx->id < 0)
|
|
+ hdptx->id = 0;
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ regs = devm_ioremap_resource(dev, res);
|
|
+ if (IS_ERR(regs))
|
|
+ return PTR_ERR(regs);
|
|
+
|
|
+ ret = devm_clk_bulk_get_all(dev, &hdptx->clks);
|
|
+ if (ret < 1)
|
|
+ return dev_err_probe(dev, ret, "failed to get clocks\n");
|
|
+
|
|
+ hdptx->nr_clks = ret;
|
|
+
|
|
+ ret = clk_bulk_prepare(hdptx->nr_clks, hdptx->clks);
|
|
+ if (ret) {
|
|
+ dev_err(hdptx->dev, "failed to prepare clocks\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ hdptx->regmap = devm_regmap_init_mmio(dev, regs,
|
|
+ &rockchip_hdptx_phy_regmap_config);
|
|
+ if (IS_ERR(hdptx->regmap)) {
|
|
+ ret = PTR_ERR(hdptx->regmap);
|
|
+ dev_err(dev, "failed to init regmap: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->phy_reset = devm_reset_control_get(dev, "phy");
|
|
+ if (IS_ERR(hdptx->phy_reset)) {
|
|
+ ret = PTR_ERR(hdptx->phy_reset);
|
|
+ dev_err(dev, "failed to get phy reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->apb_reset = devm_reset_control_get(dev, "apb");
|
|
+ if (IS_ERR(hdptx->apb_reset)) {
|
|
+ ret = PTR_ERR(hdptx->apb_reset);
|
|
+ dev_err(dev, "failed to get apb reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->init_reset = devm_reset_control_get(dev, "init");
|
|
+ if (IS_ERR(hdptx->init_reset)) {
|
|
+ ret = PTR_ERR(hdptx->init_reset);
|
|
+ dev_err(dev, "failed to get init reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->cmn_reset = devm_reset_control_get(dev, "cmn");
|
|
+ if (IS_ERR(hdptx->cmn_reset)) {
|
|
+ ret = PTR_ERR(hdptx->cmn_reset);
|
|
+ dev_err(dev, "failed to get apb reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->lane_reset = devm_reset_control_get(dev, "lane");
|
|
+ if (IS_ERR(hdptx->lane_reset)) {
|
|
+ ret = PTR_ERR(hdptx->lane_reset);
|
|
+ dev_err(dev, "failed to get lane reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->ropll_reset = devm_reset_control_get(dev, "ropll");
|
|
+ if (IS_ERR(hdptx->ropll_reset)) {
|
|
+ ret = PTR_ERR(hdptx->ropll_reset);
|
|
+ dev_err(dev, "failed to get ropll reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->lcpll_reset = devm_reset_control_get(dev, "lcpll");
|
|
+ if (IS_ERR(hdptx->lcpll_reset)) {
|
|
+ ret = PTR_ERR(hdptx->lcpll_reset);
|
|
+ dev_err(dev, "failed to get lcpll reset: %d\n", ret);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
|
+ if (IS_ERR(hdptx->grf)) {
|
|
+ ret = PTR_ERR(hdptx->grf);
|
|
+ dev_err(hdptx->dev, "Unable to get rockchip,grf\n");
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ hdptx->phy = devm_phy_create(dev, NULL, &rockchip_hdptx_phy_ops);
|
|
+ if (IS_ERR(hdptx->phy)) {
|
|
+ dev_err(dev, "failed to create HDMI PHY\n");
|
|
+ ret = PTR_ERR(hdptx->phy);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ phy_set_drvdata(hdptx->phy, hdptx);
|
|
+ phy_set_bus_width(hdptx->phy, 8);
|
|
+
|
|
+ pm_runtime_enable(dev);
|
|
+ ret = devm_add_action_or_reset(dev, rockchip_hdptx_phy_runtime_disable,
|
|
+ hdptx);
|
|
+ if (ret)
|
|
+ goto err_regsmap;
|
|
+
|
|
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
|
+ if (IS_ERR(phy_provider)) {
|
|
+ dev_err(dev, "failed to register PHY provider\n");
|
|
+ ret = PTR_ERR(phy_provider);
|
|
+ goto err_regsmap;
|
|
+ }
|
|
+
|
|
+ reset_control_deassert(hdptx->apb_reset);
|
|
+ reset_control_deassert(hdptx->cmn_reset);
|
|
+ reset_control_deassert(hdptx->init_reset);
|
|
+
|
|
+ ret = rockchip_hdptx_phy_clk_register(hdptx);
|
|
+ if (ret)
|
|
+ goto err_regsmap;
|
|
+
|
|
+ platform_set_drvdata(pdev, hdptx);
|
|
+ dev_info(dev, "hdptx phy init success\n");
|
|
+ return 0;
|
|
+
|
|
+err_regsmap:
|
|
+ clk_bulk_unprepare(hdptx->nr_clks, hdptx->clks);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct platform_driver rockchip_hdptx_phy_driver = {
|
|
+ .probe = rockchip_hdptx_phy_probe,
|
|
+ .driver = {
|
|
+ .name = "rockchip-hdptx-phy-hdmi",
|
|
+ .of_match_table = of_match_ptr(rockchip_hdptx_phy_of_match),
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(rockchip_hdptx_phy_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("Samsung HDMI-DP Transmitter Combphy Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
|
new file mode 100644
|
|
index 000000000..bb0beafb8
|
|
--- /dev/null
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
|
|
@@ -0,0 +1,1749 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+/*
|
|
+ * Rockchip USBDP Combo PHY with Samsung IP block driver
|
|
+ *
|
|
+ * Copyright (C) 2021 Rockchip Electronics Co., Ltd
|
|
+ */
|
|
+
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/bits.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/clk-provider.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/iopoll.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/phy/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/reset.h>
|
|
+#include <linux/usb/ch9.h>
|
|
+#include <linux/usb/typec_dp.h>
|
|
+#include <linux/usb/typec_mux.h>
|
|
+
|
|
+/* RK3588 USBDP PHY Register Definitions */
|
|
+
|
|
+#define UDPHY_PCS 0x4000
|
|
+#define UDPHY_PMA 0x8000
|
|
+
|
|
+/* VO0 GRF Registers */
|
|
+#define RK3588_GRF_VO0_CON0 0x0000
|
|
+#define RK3588_GRF_VO0_CON2 0x0008
|
|
+#define DP_SINK_HPD_CFG BIT(11)
|
|
+#define DP_SINK_HPD_SEL BIT(10)
|
|
+#define DP_AUX_DIN_SEL BIT(9)
|
|
+#define DP_AUX_DOUT_SEL BIT(8)
|
|
+#define DP_LANE_SEL_N(n) GENMASK(2 * (n) + 1, 2 * (n))
|
|
+#define DP_LANE_SEL_ALL GENMASK(7, 0)
|
|
+#define PHY_AUX_DP_DATA_POL_NORMAL 0
|
|
+#define PHY_AUX_DP_DATA_POL_INVERT 1
|
|
+
|
|
+/* PMA CMN Registers */
|
|
+#define CMN_LANE_MUX_AND_EN_OFFSET 0x0288 /* cmn_reg00A2 */
|
|
+#define CMN_DP_LANE_MUX_N(n) BIT((n) + 4)
|
|
+#define CMN_DP_LANE_EN_N(n) BIT(n)
|
|
+#define CMN_DP_LANE_MUX_ALL GENMASK(7, 4)
|
|
+#define CMN_DP_LANE_EN_ALL GENMASK(3, 0)
|
|
+#define PHY_LANE_MUX_USB 0
|
|
+#define PHY_LANE_MUX_DP 1
|
|
+
|
|
+#define CMN_DP_LINK_OFFSET 0x28c /*cmn_reg00A3 */
|
|
+#define CMN_DP_TX_LINK_BW GENMASK(6, 5)
|
|
+#define CMN_DP_TX_LANE_SWAP_EN BIT(2)
|
|
+
|
|
+#define CMN_SSC_EN_OFFSET 0x2d0 /* cmn_reg00B4 */
|
|
+#define CMN_ROPLL_SSC_EN BIT(1)
|
|
+#define CMN_LCPLL_SSC_EN BIT(0)
|
|
+
|
|
+#define CMN_ANA_LCPLL_DONE_OFFSET 0x0350 /* cmn_reg00D4 */
|
|
+#define CMN_ANA_LCPLL_LOCK_DONE BIT(7)
|
|
+#define CMN_ANA_LCPLL_AFC_DONE BIT(6)
|
|
+
|
|
+#define CMN_ANA_ROPLL_DONE_OFFSET 0x0354 /* cmn_reg00D5 */
|
|
+#define CMN_ANA_ROPLL_LOCK_DONE BIT(1)
|
|
+#define CMN_ANA_ROPLL_AFC_DONE BIT(0)
|
|
+
|
|
+#define CMN_DP_RSTN_OFFSET 0x038c /* cmn_reg00E3 */
|
|
+#define CMN_DP_INIT_RSTN BIT(3)
|
|
+#define CMN_DP_CMN_RSTN BIT(2)
|
|
+#define CMN_CDR_WTCHDG_EN BIT(1)
|
|
+#define CMN_CDR_WTCHDG_MSK_CDR_EN BIT(0)
|
|
+
|
|
+#define TRSV_ANA_TX_CLK_OFFSET_N(n) (0x854 + (n) * 0x800) /* trsv_reg0215 */
|
|
+#define LN_ANA_TX_SER_TXCLK_INV BIT(1)
|
|
+
|
|
+#define TRSV_LN0_MON_RX_CDR_DONE_OFFSET 0x0b84 /* trsv_reg02E1 */
|
|
+#define TRSV_LN0_MON_RX_CDR_LOCK_DONE BIT(0)
|
|
+
|
|
+#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */
|
|
+#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0)
|
|
+
|
|
+
|
|
+#define BIT_WRITEABLE_SHIFT 16
|
|
+
|
|
+enum {
|
|
+ DP_BW_RBR,
|
|
+ DP_BW_HBR,
|
|
+ DP_BW_HBR2,
|
|
+ DP_BW_HBR3,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ UDPHY_MODE_NONE = 0,
|
|
+ UDPHY_MODE_USB = BIT(0),
|
|
+ UDPHY_MODE_DP = BIT(1),
|
|
+ UDPHY_MODE_DP_USB = BIT(1) | BIT(0),
|
|
+};
|
|
+
|
|
+struct udphy_grf_reg {
|
|
+ unsigned int offset;
|
|
+ unsigned int bitend;
|
|
+ unsigned int bitstart;
|
|
+ unsigned int disable;
|
|
+ unsigned int enable;
|
|
+};
|
|
+
|
|
+struct udphy_grf_cfg {
|
|
+ /* u2phy-grf */
|
|
+ struct udphy_grf_reg bvalid_phy_con;
|
|
+ struct udphy_grf_reg bvalid_grf_con;
|
|
+
|
|
+ /* usb-grf */
|
|
+ struct udphy_grf_reg usb3otg0_cfg;
|
|
+ struct udphy_grf_reg usb3otg1_cfg;
|
|
+
|
|
+ /* usbdpphy-grf */
|
|
+ struct udphy_grf_reg low_pwrn;
|
|
+ struct udphy_grf_reg rx_lfps;
|
|
+};
|
|
+
|
|
+struct udphy_vogrf_cfg {
|
|
+ /* vo-grf */
|
|
+ struct udphy_grf_reg hpd_trigger;
|
|
+};
|
|
+
|
|
+struct dp_tx_drv_ctrl {
|
|
+ u32 trsv_reg0204;
|
|
+ u32 trsv_reg0205;
|
|
+ u32 trsv_reg0206;
|
|
+ u32 trsv_reg0207;
|
|
+};
|
|
+
|
|
+struct rockchip_udphy;
|
|
+
|
|
+struct rockchip_udphy_cfg {
|
|
+ /* resets to be requested */
|
|
+ const char * const *rst_list;
|
|
+ int num_rsts;
|
|
+
|
|
+ struct udphy_grf_cfg grfcfg;
|
|
+ struct udphy_vogrf_cfg vogrfcfg[2];
|
|
+ const struct dp_tx_drv_ctrl (*dp_tx_ctrl_cfg[4])[4];
|
|
+ const struct dp_tx_drv_ctrl (*dp_tx_ctrl_cfg_typec[4])[4];
|
|
+ int (*combophy_init)(struct rockchip_udphy *udphy);
|
|
+ int (*dp_phy_set_rate)(struct rockchip_udphy *udphy,
|
|
+ struct phy_configure_opts_dp *dp);
|
|
+ int (*dp_phy_set_voltages)(struct rockchip_udphy *udphy,
|
|
+ struct phy_configure_opts_dp *dp);
|
|
+ int (*hpd_event_trigger)(struct rockchip_udphy *udphy, bool hpd);
|
|
+ int (*dplane_enable)(struct rockchip_udphy *udphy, int dp_lanes);
|
|
+ int (*dplane_select)(struct rockchip_udphy *udphy);
|
|
+};
|
|
+
|
|
+struct rockchip_udphy {
|
|
+ struct device *dev;
|
|
+ struct regmap *pma_regmap;
|
|
+ struct regmap *u2phygrf;
|
|
+ struct regmap *udphygrf;
|
|
+ struct regmap *usbgrf;
|
|
+ struct regmap *vogrf;
|
|
+ struct typec_switch_dev *sw;
|
|
+ struct typec_mux_dev *mux;
|
|
+ struct mutex mutex; /* mutex to protect access to individual PHYs */
|
|
+
|
|
+ /* clocks and rests */
|
|
+ int num_clks;
|
|
+ struct clk_bulk_data *clks;
|
|
+ struct clk *refclk;
|
|
+ struct reset_control **rsts;
|
|
+
|
|
+ /* PHY status management */
|
|
+ bool flip;
|
|
+ bool mode_change;
|
|
+ u8 mode;
|
|
+ u8 status;
|
|
+
|
|
+ /* utilized for USB */
|
|
+ bool hs; /* flag for high-speed */
|
|
+
|
|
+ /* utilized for DP */
|
|
+ struct gpio_desc *sbu1_dc_gpio;
|
|
+ struct gpio_desc *sbu2_dc_gpio;
|
|
+ u32 lane_mux_sel[4];
|
|
+ u32 dp_lane_sel[4];
|
|
+ u32 dp_aux_dout_sel;
|
|
+ u32 dp_aux_din_sel;
|
|
+ bool dp_sink_hpd_sel;
|
|
+ bool dp_sink_hpd_cfg;
|
|
+ u8 bw;
|
|
+ int id;
|
|
+
|
|
+ /* PHY const config */
|
|
+ const struct rockchip_udphy_cfg *cfgs;
|
|
+};
|
|
+
|
|
+static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr[4][4] = {
|
|
+ /* voltage swing 0, pre-emphasis 0->3 */
|
|
+ {
|
|
+ { 0x20, 0x10, 0x42, 0xe5 },
|
|
+ { 0x26, 0x14, 0x42, 0xe5 },
|
|
+ { 0x29, 0x18, 0x42, 0xe5 },
|
|
+ { 0x2b, 0x1c, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 1, pre-emphasis 0->2 */
|
|
+ {
|
|
+ { 0x23, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2a, 0x17, 0x43, 0xe7 },
|
|
+ { 0x2b, 0x1a, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 2, pre-emphasis 0->1 */
|
|
+ {
|
|
+ { 0x27, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2b, 0x17, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 3, pre-emphasis 0 */
|
|
+ {
|
|
+ { 0x29, 0x10, 0x43, 0xe7 },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr_typec[4][4] = {
|
|
+ /* voltage swing 0, pre-emphasis 0->3 */
|
|
+ {
|
|
+ { 0x20, 0x10, 0x42, 0xe5 },
|
|
+ { 0x26, 0x14, 0x42, 0xe5 },
|
|
+ { 0x29, 0x18, 0x42, 0xe5 },
|
|
+ { 0x2b, 0x1c, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 1, pre-emphasis 0->2 */
|
|
+ {
|
|
+ { 0x23, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2a, 0x17, 0x43, 0xe7 },
|
|
+ { 0x2b, 0x1a, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 2, pre-emphasis 0->1 */
|
|
+ {
|
|
+ { 0x27, 0x10, 0x43, 0x67 },
|
|
+ { 0x2b, 0x17, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 3, pre-emphasis 0 */
|
|
+ {
|
|
+ { 0x29, 0x10, 0x43, 0xe7 },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr2[4][4] = {
|
|
+ /* voltage swing 0, pre-emphasis 0->3 */
|
|
+ {
|
|
+ { 0x21, 0x10, 0x42, 0xe5 },
|
|
+ { 0x26, 0x14, 0x42, 0xe5 },
|
|
+ { 0x26, 0x16, 0x43, 0xe5 },
|
|
+ { 0x2a, 0x19, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 1, pre-emphasis 0->2 */
|
|
+ {
|
|
+ { 0x24, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2a, 0x17, 0x43, 0xe7 },
|
|
+ { 0x2b, 0x1a, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 2, pre-emphasis 0->1 */
|
|
+ {
|
|
+ { 0x28, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2b, 0x17, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 3, pre-emphasis 0 */
|
|
+ {
|
|
+ { 0x28, 0x10, 0x43, 0xe7 },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr3[4][4] = {
|
|
+ /* voltage swing 0, pre-emphasis 0->3 */
|
|
+ {
|
|
+ { 0x21, 0x10, 0x42, 0xe5 },
|
|
+ { 0x26, 0x14, 0x42, 0xe5 },
|
|
+ { 0x26, 0x16, 0x43, 0xe5 },
|
|
+ { 0x29, 0x18, 0x43, 0xe7 },
|
|
+ },
|
|
+
|
|
+ /* voltage swing 1, pre-emphasis 0->2 */
|
|
+ {
|
|
+ { 0x24, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2a, 0x18, 0x43, 0xe7 },
|
|
+ { 0x2b, 0x1b, 0x43, 0xe7 }
|
|
+ },
|
|
+
|
|
+ /* voltage swing 2, pre-emphasis 0->1 */
|
|
+ {
|
|
+ { 0x27, 0x10, 0x42, 0xe7 },
|
|
+ { 0x2b, 0x18, 0x43, 0xe7 }
|
|
+ },
|
|
+
|
|
+ /* voltage swing 3, pre-emphasis 0 */
|
|
+ {
|
|
+ { 0x28, 0x10, 0x43, 0xe7 },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct reg_sequence rk3588_udphy_24m_refclk_cfg[] = {
|
|
+ {0x0090, 0x68}, {0x0094, 0x68},
|
|
+ {0x0128, 0x24}, {0x012c, 0x44},
|
|
+ {0x0130, 0x3f}, {0x0134, 0x44},
|
|
+ {0x015c, 0xa9}, {0x0160, 0x71},
|
|
+ {0x0164, 0x71}, {0x0168, 0xa9},
|
|
+ {0x0174, 0xa9}, {0x0178, 0x71},
|
|
+ {0x017c, 0x71}, {0x0180, 0xa9},
|
|
+ {0x018c, 0x41}, {0x0190, 0x00},
|
|
+ {0x0194, 0x05}, {0x01ac, 0x2a},
|
|
+ {0x01b0, 0x17}, {0x01b4, 0x17},
|
|
+ {0x01b8, 0x2a}, {0x01c8, 0x04},
|
|
+ {0x01cc, 0x08}, {0x01d0, 0x08},
|
|
+ {0x01d4, 0x04}, {0x01d8, 0x20},
|
|
+ {0x01dc, 0x01}, {0x01e0, 0x09},
|
|
+ {0x01e4, 0x03}, {0x01f0, 0x29},
|
|
+ {0x01f4, 0x02}, {0x01f8, 0x02},
|
|
+ {0x01fc, 0x29}, {0x0208, 0x2a},
|
|
+ {0x020c, 0x17}, {0x0210, 0x17},
|
|
+ {0x0214, 0x2a}, {0x0224, 0x20},
|
|
+ {0x03f0, 0x0a}, {0x03f4, 0x07},
|
|
+ {0x03f8, 0x07}, {0x03fc, 0x0c},
|
|
+ {0x0404, 0x12}, {0x0408, 0x1a},
|
|
+ {0x040c, 0x1a}, {0x0410, 0x3f},
|
|
+ {0x0ce0, 0x68}, {0x0ce8, 0xd0},
|
|
+ {0x0cf0, 0x87}, {0x0cf8, 0x70},
|
|
+ {0x0d00, 0x70}, {0x0d08, 0xa9},
|
|
+ {0x1ce0, 0x68}, {0x1ce8, 0xd0},
|
|
+ {0x1cf0, 0x87}, {0x1cf8, 0x70},
|
|
+ {0x1d00, 0x70}, {0x1d08, 0xa9},
|
|
+ {0x0a3c, 0xd0}, {0x0a44, 0xd0},
|
|
+ {0x0a48, 0x01}, {0x0a4c, 0x0d},
|
|
+ {0x0a54, 0xe0}, {0x0a5c, 0xe0},
|
|
+ {0x0a64, 0xa8}, {0x1a3c, 0xd0},
|
|
+ {0x1a44, 0xd0}, {0x1a48, 0x01},
|
|
+ {0x1a4c, 0x0d}, {0x1a54, 0xe0},
|
|
+ {0x1a5c, 0xe0}, {0x1a64, 0xa8}
|
|
+};
|
|
+
|
|
+static const struct reg_sequence rk3588_udphy_26m_refclk_cfg[] = {
|
|
+ {0x0830, 0x07}, {0x085c, 0x80},
|
|
+ {0x1030, 0x07}, {0x105c, 0x80},
|
|
+ {0x1830, 0x07}, {0x185c, 0x80},
|
|
+ {0x2030, 0x07}, {0x205c, 0x80},
|
|
+ {0x0228, 0x38}, {0x0104, 0x44},
|
|
+ {0x0248, 0x44}, {0x038C, 0x02},
|
|
+ {0x0878, 0x04}, {0x1878, 0x04},
|
|
+ {0x0898, 0x77}, {0x1898, 0x77},
|
|
+ {0x0054, 0x01}, {0x00e0, 0x38},
|
|
+ {0x0060, 0x24}, {0x0064, 0x77},
|
|
+ {0x0070, 0x76}, {0x0234, 0xE8},
|
|
+ {0x0AF4, 0x15}, {0x1AF4, 0x15},
|
|
+ {0x081C, 0xE5}, {0x181C, 0xE5},
|
|
+ {0x099C, 0x48}, {0x199C, 0x48},
|
|
+ {0x09A4, 0x07}, {0x09A8, 0x22},
|
|
+ {0x19A4, 0x07}, {0x19A8, 0x22},
|
|
+ {0x09B8, 0x3E}, {0x19B8, 0x3E},
|
|
+ {0x09E4, 0x02}, {0x19E4, 0x02},
|
|
+ {0x0A34, 0x1E}, {0x1A34, 0x1E},
|
|
+ {0x0A98, 0x2F}, {0x1A98, 0x2F},
|
|
+ {0x0c30, 0x0E}, {0x0C48, 0x06},
|
|
+ {0x1C30, 0x0E}, {0x1C48, 0x06},
|
|
+ {0x028C, 0x18}, {0x0AF0, 0x00},
|
|
+ {0x1AF0, 0x00}
|
|
+};
|
|
+
|
|
+static const struct reg_sequence rk3588_udphy_init_sequence[] = {
|
|
+ {0x0104, 0x44}, {0x0234, 0xE8},
|
|
+ {0x0248, 0x44}, {0x028C, 0x18},
|
|
+ {0x081C, 0xE5}, {0x0878, 0x00},
|
|
+ {0x0994, 0x1C}, {0x0AF0, 0x00},
|
|
+ {0x181C, 0xE5}, {0x1878, 0x00},
|
|
+ {0x1994, 0x1C}, {0x1AF0, 0x00},
|
|
+ {0x0428, 0x60}, {0x0D58, 0x33},
|
|
+ {0x1D58, 0x33}, {0x0990, 0x74},
|
|
+ {0x0D64, 0x17}, {0x08C8, 0x13},
|
|
+ {0x1990, 0x74}, {0x1D64, 0x17},
|
|
+ {0x18C8, 0x13}, {0x0D90, 0x40},
|
|
+ {0x0DA8, 0x40}, {0x0DC0, 0x40},
|
|
+ {0x0DD8, 0x40}, {0x1D90, 0x40},
|
|
+ {0x1DA8, 0x40}, {0x1DC0, 0x40},
|
|
+ {0x1DD8, 0x40}, {0x03C0, 0x30},
|
|
+ {0x03C4, 0x06}, {0x0E10, 0x00},
|
|
+ {0x1E10, 0x00}, {0x043C, 0x0F},
|
|
+ {0x0D2C, 0xFF}, {0x1D2C, 0xFF},
|
|
+ {0x0D34, 0x0F}, {0x1D34, 0x0F},
|
|
+ {0x08FC, 0x2A}, {0x0914, 0x28},
|
|
+ {0x0A30, 0x03}, {0x0E38, 0x05},
|
|
+ {0x0ECC, 0x27}, {0x0ED0, 0x22},
|
|
+ {0x0ED4, 0x26}, {0x18FC, 0x2A},
|
|
+ {0x1914, 0x28}, {0x1A30, 0x03},
|
|
+ {0x1E38, 0x05}, {0x1ECC, 0x27},
|
|
+ {0x1ED0, 0x22}, {0x1ED4, 0x26},
|
|
+ {0x0048, 0x0F}, {0x0060, 0x3C},
|
|
+ {0x0064, 0xF7}, {0x006C, 0x20},
|
|
+ {0x0070, 0x7D}, {0x0074, 0x68},
|
|
+ {0x0AF4, 0x1A}, {0x1AF4, 0x1A},
|
|
+ {0x0440, 0x3F}, {0x10D4, 0x08},
|
|
+ {0x20D4, 0x08}, {0x00D4, 0x30},
|
|
+ {0x0024, 0x6e},
|
|
+};
|
|
+
|
|
+static inline int grfreg_write(struct regmap *base,
|
|
+ const struct udphy_grf_reg *reg, bool en)
|
|
+{
|
|
+ u32 val, mask, tmp;
|
|
+
|
|
+ tmp = en ? reg->enable : reg->disable;
|
|
+ mask = GENMASK(reg->bitend, reg->bitstart);
|
|
+ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
|
|
+
|
|
+ return regmap_write(base, reg->offset, val);
|
|
+}
|
|
+
|
|
+static int udphy_clk_init(struct rockchip_udphy *udphy, struct device *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
|
|
+ if (udphy->num_clks < 1)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* used for configure phy reference clock frequency */
|
|
+ for (i = 0; i < udphy->num_clks; i++) {
|
|
+ if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
|
|
+ udphy->refclk = udphy->clks[i].clk;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!udphy->refclk)
|
|
+ dev_warn(udphy->dev, "no refclk found\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_reset_init(struct rockchip_udphy *udphy, struct device *dev)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int idx;
|
|
+
|
|
+ udphy->rsts = devm_kcalloc(dev, cfg->num_rsts,
|
|
+ sizeof(*udphy->rsts), GFP_KERNEL);
|
|
+ if (!udphy->rsts)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (idx = 0; idx < cfg->num_rsts; idx++) {
|
|
+ struct reset_control *rst;
|
|
+ const char *name = cfg->rst_list[idx];
|
|
+
|
|
+ rst = devm_reset_control_get(dev, name);
|
|
+ if (IS_ERR(rst)) {
|
|
+ dev_err(dev, "failed to get %s reset\n", name);
|
|
+ devm_kfree(dev, (void *)udphy->rsts);
|
|
+ return PTR_ERR(rst);
|
|
+ }
|
|
+
|
|
+ udphy->rsts[idx] = rst;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_reset_assert_all(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int idx, ret;
|
|
+
|
|
+ for (idx = 0; idx < cfg->num_rsts; idx++) {
|
|
+ ret = reset_control_assert(udphy->rsts[idx]);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_reset_deassert_all(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int idx, ret;
|
|
+
|
|
+ for (idx = 0; idx < cfg->num_rsts; idx++) {
|
|
+ ret = reset_control_deassert(udphy->rsts[idx]);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_get_rst_idx(const char * const *list, int num, char *name)
|
|
+{
|
|
+ int idx;
|
|
+
|
|
+ for (idx = 0; idx < num; idx++) {
|
|
+ if (!strcmp(list[idx], name))
|
|
+ return idx;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int udphy_reset_assert(struct rockchip_udphy *udphy, char *name)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int idx;
|
|
+
|
|
+ idx = udphy_get_rst_idx(cfg->rst_list, cfg->num_rsts, name);
|
|
+ if (idx < 0)
|
|
+ return idx;
|
|
+
|
|
+ return reset_control_assert(udphy->rsts[idx]);
|
|
+}
|
|
+
|
|
+static int udphy_reset_deassert(struct rockchip_udphy *udphy, char *name)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int idx;
|
|
+
|
|
+ idx = udphy_get_rst_idx(cfg->rst_list, cfg->num_rsts, name);
|
|
+ if (idx < 0)
|
|
+ return idx;
|
|
+
|
|
+ return reset_control_deassert(udphy->rsts[idx]);
|
|
+}
|
|
+
|
|
+static void udphy_u3_port_disable(struct rockchip_udphy *udphy, u8 disable)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ const struct udphy_grf_reg *preg;
|
|
+
|
|
+ preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
|
|
+ grfreg_write(udphy->usbgrf, preg, disable);
|
|
+}
|
|
+
|
|
+static void udphy_usb_bvalid_enable(struct rockchip_udphy *udphy, u8 enable)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+
|
|
+ grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_phy_con, enable);
|
|
+ grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_grf_con, enable);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * In usb/dp combo phy driver, here are 2 ways to mapping lanes.
|
|
+ *
|
|
+ * 1 Type-C Mapping table (DP_Alt_Mode V1.0b remove ABF pin mapping)
|
|
+ * ---------------------------------------------------------------------------
|
|
+ * Type-C Pin B11-B10 A2-A3 A11-A10 B2-B3
|
|
+ * PHY Pad ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
|
|
+ * C/E(Normal) dpln3 dpln2 dpln0 dpln1
|
|
+ * C/E(Flip ) dpln0 dpln1 dpln3 dpln2
|
|
+ * D/F(Normal) usbrx usbtx dpln0 dpln1
|
|
+ * D/F(Flip ) dpln0 dpln1 usbrx usbtx
|
|
+ * A(Normal ) dpln3 dpln1 dpln2 dpln0
|
|
+ * A(Flip ) dpln2 dpln0 dpln3 dpln1
|
|
+ * B(Normal ) usbrx usbtx dpln1 dpln0
|
|
+ * B(Flip ) dpln1 dpln0 usbrx usbtx
|
|
+ * ---------------------------------------------------------------------------
|
|
+ *
|
|
+ * 2 Mapping the lanes in dtsi
|
|
+ * if all 4 lane assignment for dp function, define rockchip,dp-lane-mux = <x x x x>;
|
|
+ * sample as follow:
|
|
+ * ---------------------------------------------------------------------------
|
|
+ * B11-B10 A2-A3 A11-A10 B2-B3
|
|
+ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
|
|
+ * <0 1 2 3> dpln0 dpln1 dpln2 dpln3
|
|
+ * <2 3 0 1> dpln2 dpln3 dpln0 dpln1
|
|
+ * ---------------------------------------------------------------------------
|
|
+ * if 2 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x x>;
|
|
+ * sample as follow:
|
|
+ * ---------------------------------------------------------------------------
|
|
+ * B11-B10 A2-A3 A11-A10 B2-B3
|
|
+ * rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
|
|
+ * <0 1> dpln0 dpln1 usbrx usbtx
|
|
+ * <2 3> usbrx usbtx dpln0 dpln1
|
|
+ * ---------------------------------------------------------------------------
|
|
+ */
|
|
+
|
|
+static int udphy_dplane_select(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+
|
|
+ if (cfg->dplane_select)
|
|
+ return cfg->dplane_select(udphy);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_dplane_get(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ int dp_lanes;
|
|
+
|
|
+ switch (udphy->mode) {
|
|
+ case UDPHY_MODE_DP:
|
|
+ dp_lanes = 4;
|
|
+ break;
|
|
+ case UDPHY_MODE_DP_USB:
|
|
+ dp_lanes = 2;
|
|
+ break;
|
|
+ case UDPHY_MODE_USB:
|
|
+ fallthrough;
|
|
+ default:
|
|
+ dp_lanes = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return dp_lanes;
|
|
+}
|
|
+
|
|
+static int udphy_dplane_enable(struct rockchip_udphy *udphy, int dp_lanes)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (cfg->dplane_enable)
|
|
+ ret = cfg->dplane_enable(udphy, dp_lanes);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int upphy_set_typec_default_mapping(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ if (udphy->flip) {
|
|
+ udphy->dp_lane_sel[0] = 0;
|
|
+ udphy->dp_lane_sel[1] = 1;
|
|
+ udphy->dp_lane_sel[2] = 3;
|
|
+ udphy->dp_lane_sel[3] = 2;
|
|
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
|
|
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
|
|
+ udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
|
|
+ udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
|
|
+ gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
|
|
+ gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
|
|
+ } else {
|
|
+ udphy->dp_lane_sel[0] = 2;
|
|
+ udphy->dp_lane_sel[1] = 3;
|
|
+ udphy->dp_lane_sel[2] = 1;
|
|
+ udphy->dp_lane_sel[3] = 0;
|
|
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
|
|
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
|
|
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
|
+ udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
|
|
+ udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
|
|
+ gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
|
|
+ gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
|
|
+ }
|
|
+
|
|
+ udphy->mode = UDPHY_MODE_DP_USB;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_orien_sw_set(struct typec_switch_dev *sw,
|
|
+ enum typec_orientation orien)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = typec_switch_get_drvdata(sw);
|
|
+
|
|
+ mutex_lock(&udphy->mutex);
|
|
+
|
|
+ if (orien == TYPEC_ORIENTATION_NONE) {
|
|
+ gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
|
|
+ gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
|
|
+ /* unattached */
|
|
+ udphy_usb_bvalid_enable(udphy, false);
|
|
+ goto unlock_ret;
|
|
+ }
|
|
+
|
|
+ udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
|
|
+ upphy_set_typec_default_mapping(udphy);
|
|
+ udphy_usb_bvalid_enable(udphy, true);
|
|
+
|
|
+unlock_ret:
|
|
+ mutex_unlock(&udphy->mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_setup_orien_switch(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ struct typec_switch_desc sw_desc = { };
|
|
+
|
|
+ sw_desc.drvdata = udphy;
|
|
+ sw_desc.fwnode = dev_fwnode(udphy->dev);
|
|
+ sw_desc.set = udphy_orien_sw_set;
|
|
+
|
|
+ udphy->sw = typec_switch_register(udphy->dev, &sw_desc);
|
|
+ if (IS_ERR(udphy->sw)) {
|
|
+ dev_err(udphy->dev, "Error register typec orientation switch: %ld\n",
|
|
+ PTR_ERR(udphy->sw));
|
|
+ return PTR_ERR(udphy->sw);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void udphy_orien_switch_unregister(void *data)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = data;
|
|
+
|
|
+ typec_switch_unregister(udphy->sw);
|
|
+}
|
|
+
|
|
+static int udphy_setup(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int ret = 0;
|
|
+
|
|
+ ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "failed to enable clk\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (cfg->combophy_init) {
|
|
+ ret = cfg->combophy_init(udphy);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "failed to init combophy\n");
|
|
+ clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_disable(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
|
|
+ udphy_reset_assert_all(udphy);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_parse_lane_mux_data(struct rockchip_udphy *udphy, struct device_node *np)
|
|
+{
|
|
+ struct property *prop;
|
|
+ int ret, i, len, num_lanes;
|
|
+
|
|
+ prop = of_find_property(np, "rockchip,dp-lane-mux", &len);
|
|
+ if (!prop) {
|
|
+ dev_dbg(udphy->dev, "failed to find dp lane mux, following dp alt mode\n");
|
|
+ udphy->mode = UDPHY_MODE_USB;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ num_lanes = len / sizeof(u32);
|
|
+
|
|
+ if (num_lanes != 2 && num_lanes != 4) {
|
|
+ dev_err(udphy->dev, "invalid number of lane mux\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32_array(np, "rockchip,dp-lane-mux", udphy->dp_lane_sel, num_lanes);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "get dp lane mux failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_lanes; i++) {
|
|
+ int j;
|
|
+
|
|
+ if (udphy->dp_lane_sel[i] > 3) {
|
|
+ dev_err(udphy->dev, "lane mux between 0 and 3, exceeding the range\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ udphy->lane_mux_sel[udphy->dp_lane_sel[i]] = PHY_LANE_MUX_DP;
|
|
+
|
|
+ for (j = i + 1; j < num_lanes; j++) {
|
|
+ if (udphy->dp_lane_sel[i] == udphy->dp_lane_sel[j]) {
|
|
+ dev_err(udphy->dev, "set repeat lane mux value\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ udphy->mode = UDPHY_MODE_DP;
|
|
+ if (num_lanes == 2) {
|
|
+ udphy->mode |= UDPHY_MODE_USB;
|
|
+ udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_get_initial_status(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ int ret;
|
|
+ u32 value;
|
|
+
|
|
+ ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "failed to enable clk\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ udphy_reset_deassert_all(udphy);
|
|
+
|
|
+ regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
|
|
+ if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
|
|
+ udphy->status = UDPHY_MODE_DP;
|
|
+ else
|
|
+ udphy_disable(udphy);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_parse_dt(struct rockchip_udphy *udphy, struct device *dev)
|
|
+{
|
|
+ struct device_node *np = dev->of_node;
|
|
+ enum usb_device_speed maximum_speed;
|
|
+ int ret;
|
|
+
|
|
+ udphy->u2phygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,u2phy-grf");
|
|
+ if (IS_ERR(udphy->u2phygrf)) {
|
|
+ if (PTR_ERR(udphy->u2phygrf) == -ENODEV) {
|
|
+ dev_warn(dev, "missing u2phy-grf dt node\n");
|
|
+ udphy->u2phygrf = NULL;
|
|
+ } else {
|
|
+ return PTR_ERR(udphy->u2phygrf);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ udphy->udphygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbdpphy-grf");
|
|
+ if (IS_ERR(udphy->udphygrf)) {
|
|
+ if (PTR_ERR(udphy->udphygrf) == -ENODEV) {
|
|
+ dev_warn(dev, "missing usbdpphy-grf dt node\n");
|
|
+ udphy->udphygrf = NULL;
|
|
+ } else {
|
|
+ return PTR_ERR(udphy->udphygrf);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ udphy->usbgrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usb-grf");
|
|
+ if (IS_ERR(udphy->usbgrf)) {
|
|
+ if (PTR_ERR(udphy->usbgrf) == -ENODEV) {
|
|
+ dev_warn(dev, "missing usb-grf dt node\n");
|
|
+ udphy->usbgrf = NULL;
|
|
+ } else {
|
|
+ return PTR_ERR(udphy->usbgrf);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ udphy->vogrf = syscon_regmap_lookup_by_phandle(np, "rockchip,vo-grf");
|
|
+ if (IS_ERR(udphy->vogrf)) {
|
|
+ if (PTR_ERR(udphy->vogrf) == -ENODEV) {
|
|
+ dev_warn(dev, "missing vo-grf dt node\n");
|
|
+ udphy->vogrf = NULL;
|
|
+ } else {
|
|
+ return PTR_ERR(udphy->vogrf);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = udphy_parse_lane_mux_data(udphy, np);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ udphy->sbu1_dc_gpio = devm_gpiod_get_optional(dev, "sbu1-dc", GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(udphy->sbu1_dc_gpio))
|
|
+ return PTR_ERR(udphy->sbu1_dc_gpio);
|
|
+
|
|
+ udphy->sbu2_dc_gpio = devm_gpiod_get_optional(dev, "sbu2-dc", GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(udphy->sbu2_dc_gpio))
|
|
+ return PTR_ERR(udphy->sbu2_dc_gpio);
|
|
+
|
|
+ if (device_property_present(dev, "maximum-speed")) {
|
|
+ maximum_speed = usb_get_maximum_speed(dev);
|
|
+ udphy->hs = maximum_speed <= USB_SPEED_HIGH ? true : false;
|
|
+ }
|
|
+
|
|
+ ret = udphy_clk_init(udphy, dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = udphy_reset_init(udphy, dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_power_on(struct rockchip_udphy *udphy, u8 mode)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!(udphy->mode & mode)) {
|
|
+ dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (udphy->status == UDPHY_MODE_NONE) {
|
|
+ udphy->mode_change = false;
|
|
+ ret = udphy_setup(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (udphy->mode & UDPHY_MODE_USB)
|
|
+ udphy_u3_port_disable(udphy, false);
|
|
+ } else if (udphy->mode_change) {
|
|
+ udphy->mode_change = false;
|
|
+ udphy->status = UDPHY_MODE_NONE;
|
|
+ if (udphy->mode == UDPHY_MODE_DP)
|
|
+ udphy_u3_port_disable(udphy, true);
|
|
+
|
|
+ ret = udphy_disable(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ ret = udphy_setup(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ udphy->status |= mode;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_power_off(struct rockchip_udphy *udphy, u8 mode)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!(udphy->mode & mode)) {
|
|
+ dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!udphy->status)
|
|
+ return 0;
|
|
+
|
|
+ udphy->status &= ~mode;
|
|
+
|
|
+ if (udphy->status == UDPHY_MODE_NONE) {
|
|
+ ret = udphy_disable(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rockchip_dp_phy_power_on(struct phy *phy)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = phy_get_drvdata(phy);
|
|
+ int ret, dp_lanes;
|
|
+
|
|
+ mutex_lock(&udphy->mutex);
|
|
+
|
|
+ dp_lanes = udphy_dplane_get(udphy);
|
|
+ phy_set_bus_width(phy, dp_lanes);
|
|
+
|
|
+ ret = udphy_power_on(udphy, UDPHY_MODE_DP);
|
|
+ if (ret)
|
|
+ goto unlock;
|
|
+
|
|
+ ret = udphy_dplane_enable(udphy, dp_lanes);
|
|
+ if (ret)
|
|
+ goto unlock;
|
|
+
|
|
+ ret = udphy_dplane_select(udphy);
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&udphy->mutex);
|
|
+ /*
|
|
+ * If data send by aux channel too fast after phy power on,
|
|
+ * the aux may be not ready which will cause aux error. Adding
|
|
+ * delay to avoid this issue.
|
|
+ */
|
|
+ usleep_range(10000, 11000);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rockchip_dp_phy_power_off(struct phy *phy)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = phy_get_drvdata(phy);
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&udphy->mutex);
|
|
+ ret = udphy_dplane_enable(udphy, 0);
|
|
+ if (ret)
|
|
+ goto unlock;
|
|
+
|
|
+ ret = udphy_power_off(udphy, UDPHY_MODE_DP);
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&udphy->mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rockchip_dp_phy_verify_link_rate(unsigned int link_rate)
|
|
+{
|
|
+ switch (link_rate) {
|
|
+ case 1620:
|
|
+ case 2700:
|
|
+ case 5400:
|
|
+ case 8100:
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rockchip_dp_phy_verify_config(struct rockchip_udphy *udphy,
|
|
+ struct phy_configure_opts_dp *dp)
|
|
+{
|
|
+ int i, ret;
|
|
+
|
|
+ /* If changing link rate was required, verify it's supported. */
|
|
+ ret = rockchip_dp_phy_verify_link_rate(dp->link_rate);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Verify lane count. */
|
|
+ switch (dp->lanes) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ case 4:
|
|
+ /* valid lane count. */
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * If changing voltages is required, check swing and pre-emphasis
|
|
+ * levels, per-lane.
|
|
+ */
|
|
+ if (dp->set_voltages) {
|
|
+ /* Lane count verified previously. */
|
|
+ for (i = 0; i < dp->lanes; i++) {
|
|
+ if (dp->voltage[i] > 3 || dp->pre[i] > 3)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /*
|
|
+ * Sum of voltage swing and pre-emphasis levels cannot
|
|
+ * exceed 3.
|
|
+ */
|
|
+ if (dp->voltage[i] + dp->pre[i] > 3)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rockchip_dp_phy_configure(struct phy *phy,
|
|
+ union phy_configure_opts *opts)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = phy_get_drvdata(phy);
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int ret;
|
|
+
|
|
+ ret = rockchip_dp_phy_verify_config(udphy, &opts->dp);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (opts->dp.set_rate && cfg->dp_phy_set_rate) {
|
|
+ ret = cfg->dp_phy_set_rate(udphy, &opts->dp);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev,
|
|
+ "rockchip_hdptx_phy_set_rate failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (opts->dp.set_voltages && cfg->dp_phy_set_voltages) {
|
|
+ ret = cfg->dp_phy_set_voltages(udphy, &opts->dp);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev,
|
|
+ "rockchip_dp_phy_set_voltages failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct phy_ops rockchip_dp_phy_ops = {
|
|
+ .power_on = rockchip_dp_phy_power_on,
|
|
+ .power_off = rockchip_dp_phy_power_off,
|
|
+ .configure = rockchip_dp_phy_configure,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int rockchip_u3phy_init(struct phy *phy)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = phy_get_drvdata(phy);
|
|
+ int ret = 0;
|
|
+
|
|
+ mutex_lock(&udphy->mutex);
|
|
+ /* DP only or high-speed, disable U3 port */
|
|
+ if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
|
|
+ udphy_u3_port_disable(udphy, true);
|
|
+ goto unlock;
|
|
+ }
|
|
+
|
|
+ ret = udphy_power_on(udphy, UDPHY_MODE_USB);
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&udphy->mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rockchip_u3phy_exit(struct phy *phy)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = phy_get_drvdata(phy);
|
|
+ int ret = 0;
|
|
+
|
|
+ mutex_lock(&udphy->mutex);
|
|
+ /* DP only or high-speed */
|
|
+ if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
|
|
+ goto unlock;
|
|
+
|
|
+ ret = udphy_power_off(udphy, UDPHY_MODE_USB);
|
|
+
|
|
+unlock:
|
|
+ mutex_unlock(&udphy->mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct phy_ops rockchip_u3phy_ops = {
|
|
+ .init = rockchip_u3phy_init,
|
|
+ .exit = rockchip_u3phy_exit,
|
|
+ .owner = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int usbdp_typec_mux_set(struct typec_mux_dev *mux,
|
|
+ struct typec_mux_state *state)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = typec_mux_get_drvdata(mux);
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ u8 mode;
|
|
+
|
|
+ mutex_lock(&udphy->mutex);
|
|
+
|
|
+ switch (state->mode) {
|
|
+ case TYPEC_DP_STATE_C:
|
|
+ fallthrough;
|
|
+ case TYPEC_DP_STATE_E:
|
|
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
|
+ mode = UDPHY_MODE_DP;
|
|
+ break;
|
|
+ case TYPEC_DP_STATE_D:
|
|
+ fallthrough;
|
|
+ default:
|
|
+ if (udphy->flip) {
|
|
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
|
|
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
|
|
+ } else {
|
|
+ udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
|
|
+ udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
|
|
+ udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
|
|
+ udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
|
|
+ }
|
|
+ mode = UDPHY_MODE_DP_USB;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
|
|
+ struct typec_displayport_data *data = state->data;
|
|
+
|
|
+ if (!data) {
|
|
+ if (cfg->hpd_event_trigger)
|
|
+ cfg->hpd_event_trigger(udphy, false);
|
|
+ } else if (data->status & DP_STATUS_IRQ_HPD) {
|
|
+ if (cfg->hpd_event_trigger) {
|
|
+ cfg->hpd_event_trigger(udphy, false);
|
|
+ usleep_range(750, 800);
|
|
+ cfg->hpd_event_trigger(udphy, true);
|
|
+ }
|
|
+ } else if (data->status & DP_STATUS_HPD_STATE) {
|
|
+ if (udphy->mode != mode) {
|
|
+ udphy->mode = mode;
|
|
+ udphy->mode_change = true;
|
|
+ }
|
|
+ if (cfg->hpd_event_trigger)
|
|
+ cfg->hpd_event_trigger(udphy, true);
|
|
+ } else {
|
|
+ if (cfg->hpd_event_trigger)
|
|
+ cfg->hpd_event_trigger(udphy, false);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&udphy->mutex);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int udphy_setup_typec_mux(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ struct typec_mux_desc mux_desc = {};
|
|
+
|
|
+ mux_desc.drvdata = udphy;
|
|
+ mux_desc.fwnode = dev_fwnode(udphy->dev);
|
|
+ mux_desc.set = usbdp_typec_mux_set;
|
|
+
|
|
+ udphy->mux = typec_mux_register(udphy->dev, &mux_desc);
|
|
+ if (IS_ERR(udphy->mux)) {
|
|
+ dev_err(udphy->dev, "Error register typec mux: %ld\n",
|
|
+ PTR_ERR(udphy->mux));
|
|
+ return PTR_ERR(udphy->mux);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void udphy_typec_mux_unregister(void *data)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = data;
|
|
+
|
|
+ typec_mux_unregister(udphy->mux);
|
|
+}
|
|
+
|
|
+static u32 udphy_dp_get_max_link_rate(struct rockchip_udphy *udphy, struct device_node *np)
|
|
+{
|
|
+ u32 max_link_rate;
|
|
+ int ret;
|
|
+
|
|
+ ret = of_property_read_u32(np, "max-link-rate", &max_link_rate);
|
|
+ if (ret)
|
|
+ return 8100;
|
|
+
|
|
+ ret = rockchip_dp_phy_verify_link_rate(max_link_rate);
|
|
+ if (ret) {
|
|
+ dev_warn(udphy->dev, "invalid max-link-rate value:%d\n", max_link_rate);
|
|
+ max_link_rate = 8100;
|
|
+ }
|
|
+
|
|
+ return max_link_rate;
|
|
+}
|
|
+
|
|
+static const struct regmap_config rockchip_udphy_pma_regmap_cfg = {
|
|
+ .reg_bits = 32,
|
|
+ .reg_stride = 4,
|
|
+ .val_bits = 32,
|
|
+ .fast_io = true,
|
|
+ .max_register = 0x20dc,
|
|
+};
|
|
+
|
|
+static int rockchip_udphy_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct device_node *child_np;
|
|
+ struct phy_provider *phy_provider;
|
|
+ struct resource *res;
|
|
+ struct rockchip_udphy *udphy;
|
|
+ const struct rockchip_udphy_cfg *phy_cfgs;
|
|
+ void __iomem *base;
|
|
+ int id, ret;
|
|
+
|
|
+ udphy = devm_kzalloc(dev, sizeof(*udphy), GFP_KERNEL);
|
|
+ if (!udphy)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ id = of_alias_get_id(dev->of_node, "usbdp");
|
|
+ if (id < 0)
|
|
+ id = 0;
|
|
+ udphy->id = id;
|
|
+
|
|
+ phy_cfgs = device_get_match_data(dev);
|
|
+ if (!phy_cfgs) {
|
|
+ dev_err(dev, "missing match data\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ udphy->cfgs = phy_cfgs;
|
|
+
|
|
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
|
+ if (IS_ERR(base))
|
|
+ return PTR_ERR(base);
|
|
+
|
|
+ udphy->pma_regmap = devm_regmap_init_mmio(dev, base + UDPHY_PMA,
|
|
+ &rockchip_udphy_pma_regmap_cfg);
|
|
+ if (IS_ERR(udphy->pma_regmap))
|
|
+ return PTR_ERR(udphy->pma_regmap);
|
|
+
|
|
+ ret = udphy_parse_dt(udphy, dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = udphy_get_initial_status(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ mutex_init(&udphy->mutex);
|
|
+ udphy->dev = dev;
|
|
+ platform_set_drvdata(pdev, udphy);
|
|
+
|
|
+ if (device_property_present(dev, "orientation-switch")) {
|
|
+ ret = udphy_setup_orien_switch(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = devm_add_action_or_reset(dev, udphy_orien_switch_unregister, udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (device_property_present(dev, "mode-switch")) {
|
|
+ ret = udphy_setup_typec_mux(udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = devm_add_action_or_reset(dev, udphy_typec_mux_unregister, udphy);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ for_each_available_child_of_node(np, child_np) {
|
|
+ struct phy *phy;
|
|
+
|
|
+ if (of_node_name_eq(child_np, "dp-port")) {
|
|
+ phy = devm_phy_create(dev, child_np, &rockchip_dp_phy_ops);
|
|
+ if (IS_ERR(phy)) {
|
|
+ dev_err(dev, "failed to create dp phy: %pOFn\n", child_np);
|
|
+ goto put_child;
|
|
+ }
|
|
+
|
|
+ phy_set_bus_width(phy, udphy_dplane_get(udphy));
|
|
+ phy->attrs.max_link_rate = udphy_dp_get_max_link_rate(udphy, child_np);
|
|
+ } else if (of_node_name_eq(child_np, "usb3-port")) {
|
|
+ phy = devm_phy_create(dev, child_np, &rockchip_u3phy_ops);
|
|
+ if (IS_ERR(phy)) {
|
|
+ dev_err(dev, "failed to create usb phy: %pOFn\n", child_np);
|
|
+ goto put_child;
|
|
+ }
|
|
+ } else
|
|
+ continue;
|
|
+
|
|
+ phy_set_drvdata(phy, udphy);
|
|
+ }
|
|
+
|
|
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
|
+ if (IS_ERR(phy_provider)) {
|
|
+ dev_err(dev, "failed to register phy provider\n");
|
|
+ goto put_child;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+put_child:
|
|
+ of_node_put(child_np);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rk3588_udphy_refclk_set(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ unsigned long rate;
|
|
+ int ret;
|
|
+
|
|
+ /* configure phy reference clock */
|
|
+ rate = clk_get_rate(udphy->refclk);
|
|
+ dev_dbg(udphy->dev, "refclk freq %ld\n", rate);
|
|
+
|
|
+ switch (rate) {
|
|
+ case 24000000:
|
|
+ ret = regmap_multi_reg_write(udphy->pma_regmap, rk3588_udphy_24m_refclk_cfg,
|
|
+ ARRAY_SIZE(rk3588_udphy_24m_refclk_cfg));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ break;
|
|
+ case 26000000:
|
|
+ /* register default is 26MHz */
|
|
+ ret = regmap_multi_reg_write(udphy->pma_regmap, rk3588_udphy_26m_refclk_cfg,
|
|
+ ARRAY_SIZE(rk3588_udphy_26m_refclk_cfg));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(udphy->dev, "unsupported refclk freq %ld\n", rate);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rk3588_udphy_status_check(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ unsigned int val;
|
|
+ int ret;
|
|
+
|
|
+ /* LCPLL check */
|
|
+ if (udphy->mode & UDPHY_MODE_USB) {
|
|
+ ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET,
|
|
+ val, (val & CMN_ANA_LCPLL_AFC_DONE) &&
|
|
+ (val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "cmn ana lcpll lock timeout\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (!udphy->flip) {
|
|
+ ret = regmap_read_poll_timeout(udphy->pma_regmap,
|
|
+ TRSV_LN0_MON_RX_CDR_DONE_OFFSET, val,
|
|
+ val & TRSV_LN0_MON_RX_CDR_LOCK_DONE,
|
|
+ 200, 100000);
|
|
+ if (ret)
|
|
+ dev_err(udphy->dev, "trsv ln0 mon rx cdr lock timeout\n");
|
|
+ } else {
|
|
+ ret = regmap_read_poll_timeout(udphy->pma_regmap,
|
|
+ TRSV_LN2_MON_RX_CDR_DONE_OFFSET, val,
|
|
+ val & TRSV_LN2_MON_RX_CDR_LOCK_DONE,
|
|
+ 200, 100000);
|
|
+ if (ret)
|
|
+ dev_err(udphy->dev, "trsv ln2 mon rx cdr lock timeout\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rk3588_udphy_init(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ int ret;
|
|
+
|
|
+ /* enable rx lfps for usb */
|
|
+ if (udphy->mode & UDPHY_MODE_USB)
|
|
+ grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true);
|
|
+
|
|
+ /* Step 1: power on pma and deassert apb rstn */
|
|
+ grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true);
|
|
+
|
|
+ udphy_reset_deassert(udphy, "pma_apb");
|
|
+ udphy_reset_deassert(udphy, "pcs_apb");
|
|
+
|
|
+ /* Step 2: set init sequence and phy refclk */
|
|
+ ret = regmap_multi_reg_write(udphy->pma_regmap, rk3588_udphy_init_sequence,
|
|
+ ARRAY_SIZE(rk3588_udphy_init_sequence));
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "init sequence set error %d\n", ret);
|
|
+ goto assert_apb;
|
|
+ }
|
|
+
|
|
+ ret = rk3588_udphy_refclk_set(udphy);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "refclk set error %d\n", ret);
|
|
+ goto assert_apb;
|
|
+ }
|
|
+
|
|
+ /* Step 3: configure lane mux */
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET,
|
|
+ CMN_DP_LANE_MUX_ALL | CMN_DP_LANE_EN_ALL,
|
|
+ FIELD_PREP(CMN_DP_LANE_MUX_N(3), udphy->lane_mux_sel[3]) |
|
|
+ FIELD_PREP(CMN_DP_LANE_MUX_N(2), udphy->lane_mux_sel[2]) |
|
|
+ FIELD_PREP(CMN_DP_LANE_MUX_N(1), udphy->lane_mux_sel[1]) |
|
|
+ FIELD_PREP(CMN_DP_LANE_MUX_N(0), udphy->lane_mux_sel[0]) |
|
|
+ FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
|
|
+
|
|
+ /* Step 4: deassert init rstn and wait for 200ns from datasheet */
|
|
+ if (udphy->mode & UDPHY_MODE_USB)
|
|
+ udphy_reset_deassert(udphy, "init");
|
|
+
|
|
+ if (udphy->mode & UDPHY_MODE_DP) {
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
|
|
+ CMN_DP_INIT_RSTN,
|
|
+ FIELD_PREP(CMN_DP_INIT_RSTN, 0x1));
|
|
+ }
|
|
+
|
|
+ udelay(1);
|
|
+
|
|
+ /* Step 5: deassert cmn/lane rstn */
|
|
+ if (udphy->mode & UDPHY_MODE_USB) {
|
|
+ udphy_reset_deassert(udphy, "cmn");
|
|
+ udphy_reset_deassert(udphy, "lane");
|
|
+ }
|
|
+
|
|
+ /* Step 6: wait for lock done of pll */
|
|
+ ret = rk3588_udphy_status_check(udphy);
|
|
+ if (ret)
|
|
+ goto assert_phy;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+assert_phy:
|
|
+ udphy_reset_assert(udphy, "init");
|
|
+ udphy_reset_assert(udphy, "cmn");
|
|
+ udphy_reset_assert(udphy, "lane");
|
|
+
|
|
+assert_apb:
|
|
+ udphy_reset_assert(udphy, "pma_apb");
|
|
+ udphy_reset_assert(udphy, "pcs_apb");
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int rk3588_udphy_hpd_event_trigger(struct rockchip_udphy *udphy, bool hpd)
|
|
+{
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+
|
|
+ udphy->dp_sink_hpd_sel = true;
|
|
+ udphy->dp_sink_hpd_cfg = hpd;
|
|
+
|
|
+ grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rk3588_udphy_dplane_enable(struct rockchip_udphy *udphy, int dp_lanes)
|
|
+{
|
|
+ int i;
|
|
+ u32 val = 0;
|
|
+
|
|
+ for (i = 0; i < dp_lanes; i++)
|
|
+ val |= BIT(udphy->dp_lane_sel[i]);
|
|
+
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, CMN_DP_LANE_EN_ALL,
|
|
+ FIELD_PREP(CMN_DP_LANE_EN_ALL, val));
|
|
+
|
|
+ if (!dp_lanes)
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
|
|
+ CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rk3588_udphy_dplane_select(struct rockchip_udphy *udphy)
|
|
+{
|
|
+ u32 value = 0;
|
|
+
|
|
+ switch (udphy->mode) {
|
|
+ case UDPHY_MODE_DP:
|
|
+ value |= 2 << udphy->dp_lane_sel[2] * 2;
|
|
+ value |= 3 << udphy->dp_lane_sel[3] * 2;
|
|
+ fallthrough;
|
|
+ case UDPHY_MODE_DP_USB:
|
|
+ value |= 0 << udphy->dp_lane_sel[0] * 2;
|
|
+ value |= 1 << udphy->dp_lane_sel[1] * 2;
|
|
+ break;
|
|
+ case UDPHY_MODE_USB:
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ regmap_write(udphy->vogrf, udphy->id ? RK3588_GRF_VO0_CON2 : RK3588_GRF_VO0_CON0,
|
|
+ ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
|
|
+ FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
|
|
+ FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rk3588_dp_phy_set_rate(struct rockchip_udphy *udphy,
|
|
+ struct phy_configure_opts_dp *dp)
|
|
+{
|
|
+ u32 val;
|
|
+ int ret;
|
|
+
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
|
|
+ CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
|
|
+
|
|
+ switch (dp->link_rate) {
|
|
+ case 1620:
|
|
+ udphy->bw = DP_BW_RBR;
|
|
+ break;
|
|
+ case 2700:
|
|
+ udphy->bw = DP_BW_HBR;
|
|
+ break;
|
|
+ case 5400:
|
|
+ udphy->bw = DP_BW_HBR2;
|
|
+ break;
|
|
+ case 8100:
|
|
+ udphy->bw = DP_BW_HBR3;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_DP_LINK_OFFSET, CMN_DP_TX_LINK_BW,
|
|
+ FIELD_PREP(CMN_DP_TX_LINK_BW, udphy->bw));
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_SSC_EN_OFFSET, CMN_ROPLL_SSC_EN,
|
|
+ FIELD_PREP(CMN_ROPLL_SSC_EN, dp->ssc));
|
|
+ regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, CMN_DP_CMN_RSTN,
|
|
+ FIELD_PREP(CMN_DP_CMN_RSTN, 0x1));
|
|
+
|
|
+ ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_ROPLL_DONE_OFFSET, val,
|
|
+ FIELD_GET(CMN_ANA_ROPLL_LOCK_DONE, val) &&
|
|
+ FIELD_GET(CMN_ANA_ROPLL_AFC_DONE, val),
|
|
+ 0, 1000);
|
|
+ if (ret) {
|
|
+ dev_err(udphy->dev, "ROPLL is not lock\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rk3588_dp_phy_set_voltage(struct rockchip_udphy *udphy, u8 bw,
|
|
+ u32 voltage, u32 pre, u32 lane)
|
|
+{
|
|
+ u32 offset = 0x800 * lane;
|
|
+ u32 val;
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+ const struct dp_tx_drv_ctrl (*dp_ctrl)[4];
|
|
+
|
|
+ dp_ctrl = udphy->mux ? cfg->dp_tx_ctrl_cfg_typec[bw] : cfg->dp_tx_ctrl_cfg[bw];
|
|
+ val = dp_ctrl[voltage][pre].trsv_reg0204;
|
|
+ regmap_write(udphy->pma_regmap, 0x0810 + offset, val);
|
|
+
|
|
+ val = dp_ctrl[voltage][pre].trsv_reg0205;
|
|
+ regmap_write(udphy->pma_regmap, 0x0814 + offset, val);
|
|
+
|
|
+ val = dp_ctrl[voltage][pre].trsv_reg0206;
|
|
+ regmap_write(udphy->pma_regmap, 0x0818 + offset, val);
|
|
+
|
|
+ val = dp_ctrl[voltage][pre].trsv_reg0207;
|
|
+ regmap_write(udphy->pma_regmap, 0x081c + offset, val);
|
|
+}
|
|
+
|
|
+static int rk3588_dp_phy_set_voltages(struct rockchip_udphy *udphy,
|
|
+ struct phy_configure_opts_dp *dp)
|
|
+{
|
|
+ u32 i, lane;
|
|
+
|
|
+ for (i = 0; i < dp->lanes; i++) {
|
|
+ lane = udphy->dp_lane_sel[i];
|
|
+ switch (dp->link_rate) {
|
|
+ case 1620:
|
|
+ case 2700:
|
|
+ regmap_update_bits(udphy->pma_regmap, TRSV_ANA_TX_CLK_OFFSET_N(lane),
|
|
+ LN_ANA_TX_SER_TXCLK_INV,
|
|
+ FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV,
|
|
+ udphy->lane_mux_sel[lane]));
|
|
+ break;
|
|
+ case 5400:
|
|
+ case 8100:
|
|
+ regmap_update_bits(udphy->pma_regmap, TRSV_ANA_TX_CLK_OFFSET_N(lane),
|
|
+ LN_ANA_TX_SER_TXCLK_INV,
|
|
+ FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV, 0x0));
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ rk3588_dp_phy_set_voltage(udphy, udphy->bw, dp->voltage[i], dp->pre[i], lane);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __maybe_unused udphy_resume(struct device *dev)
|
|
+{
|
|
+ struct rockchip_udphy *udphy = dev_get_drvdata(dev);
|
|
+ const struct rockchip_udphy_cfg *cfg = udphy->cfgs;
|
|
+
|
|
+ if (udphy->dp_sink_hpd_sel)
|
|
+ cfg->hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops udphy_pm_ops = {
|
|
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, udphy_resume)
|
|
+};
|
|
+
|
|
+static const char * const rk3588_udphy_rst_l[] = {
|
|
+ "init", "cmn", "lane", "pcs_apb", "pma_apb"
|
|
+};
|
|
+
|
|
+static const struct rockchip_udphy_cfg rk3588_udphy_cfgs = {
|
|
+ .num_rsts = ARRAY_SIZE(rk3588_udphy_rst_l),
|
|
+ .rst_list = rk3588_udphy_rst_l,
|
|
+ .grfcfg = {
|
|
+ /* u2phy-grf */
|
|
+ .bvalid_phy_con = { 0x0008, 1, 0, 0x2, 0x3 },
|
|
+ .bvalid_grf_con = { 0x0010, 3, 2, 0x2, 0x3 },
|
|
+
|
|
+ /* usb-grf */
|
|
+ .usb3otg0_cfg = { 0x001c, 15, 0, 0x1100, 0x0188 },
|
|
+ .usb3otg1_cfg = { 0x0034, 15, 0, 0x1100, 0x0188 },
|
|
+
|
|
+ /* usbdpphy-grf */
|
|
+ .low_pwrn = { 0x0004, 13, 13, 0, 1 },
|
|
+ .rx_lfps = { 0x0004, 14, 14, 0, 1 },
|
|
+ },
|
|
+ .vogrfcfg = {
|
|
+ {
|
|
+ .hpd_trigger = { 0x0000, 11, 10, 1, 3 },
|
|
+ },
|
|
+ {
|
|
+ .hpd_trigger = { 0x0008, 11, 10, 1, 3 },
|
|
+ },
|
|
+ },
|
|
+ .dp_tx_ctrl_cfg = {
|
|
+ rk3588_dp_tx_drv_ctrl_rbr_hbr,
|
|
+ rk3588_dp_tx_drv_ctrl_rbr_hbr,
|
|
+ rk3588_dp_tx_drv_ctrl_hbr2,
|
|
+ rk3588_dp_tx_drv_ctrl_hbr3,
|
|
+ },
|
|
+ .dp_tx_ctrl_cfg_typec = {
|
|
+ rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
|
|
+ rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
|
|
+ rk3588_dp_tx_drv_ctrl_hbr2,
|
|
+ rk3588_dp_tx_drv_ctrl_hbr3,
|
|
+ },
|
|
+ .combophy_init = rk3588_udphy_init,
|
|
+ .dp_phy_set_rate = rk3588_dp_phy_set_rate,
|
|
+ .dp_phy_set_voltages = rk3588_dp_phy_set_voltages,
|
|
+ .hpd_event_trigger = rk3588_udphy_hpd_event_trigger,
|
|
+ .dplane_enable = rk3588_udphy_dplane_enable,
|
|
+ .dplane_select = rk3588_udphy_dplane_select,
|
|
+};
|
|
+
|
|
+static const struct of_device_id rockchip_udphy_dt_match[] = {
|
|
+ {
|
|
+ .compatible = "rockchip,rk3588-usbdp-phy",
|
|
+ .data = &rk3588_udphy_cfgs
|
|
+ },
|
|
+ { /* sentinel */ }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, rockchip_udphy_dt_match);
|
|
+
|
|
+static struct platform_driver rockchip_udphy_driver = {
|
|
+ .probe = rockchip_udphy_probe,
|
|
+ .driver = {
|
|
+ .name = "rockchip-usbdp-phy",
|
|
+ .of_match_table = rockchip_udphy_dt_match,
|
|
+ .pm = &udphy_pm_ops,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(rockchip_udphy_driver);
|
|
+
|
|
+MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
|
|
+MODULE_AUTHOR("Zhang Yubing <yubing.zhang@rock-chips.com>");
|
|
+MODULE_DESCRIPTION("Rockchip USBDP Combo PHY driver");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
|
|
index 058d5b853..a3ef7d703 100644
|
|
--- a/drivers/usb/typec/tcpm/tcpm.c
|
|
+++ b/drivers/usb/typec/tcpm/tcpm.c
|
|
@@ -6588,9 +6588,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
|
|
port->partner_desc.identity = &port->partner_ident;
|
|
port->port_type = port->typec_caps.type;
|
|
|
|
- port->role_sw = usb_role_switch_get(port->dev);
|
|
+ port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
|
|
if (!port->role_sw)
|
|
- port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
|
|
+ port->role_sw = usb_role_switch_get(port->dev);
|
|
if (IS_ERR(port->role_sw)) {
|
|
err = PTR_ERR(port->role_sw);
|
|
goto out_destroy_wq;
|
|
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
|
|
index 6a46baa07..9042039f2 100644
|
|
--- a/include/drm/bridge/dw_hdmi.h
|
|
+++ b/include/drm/bridge/dw_hdmi.h
|
|
@@ -6,12 +6,14 @@
|
|
#ifndef __DW_HDMI__
|
|
#define __DW_HDMI__
|
|
|
|
+#include <drm/drm_property.h>
|
|
#include <sound/hdmi-codec.h>
|
|
|
|
struct drm_display_info;
|
|
struct drm_display_mode;
|
|
struct drm_encoder;
|
|
struct dw_hdmi;
|
|
+struct dw_hdmi_qp;
|
|
struct platform_device;
|
|
|
|
/**
|
|
@@ -92,6 +94,13 @@ enum dw_hdmi_phy_type {
|
|
DW_HDMI_PHY_VENDOR_PHY = 0xfe,
|
|
};
|
|
|
|
+struct dw_hdmi_audio_tmds_n {
|
|
+ unsigned long tmds;
|
|
+ unsigned int n_32k;
|
|
+ unsigned int n_44k1;
|
|
+ unsigned int n_48k;
|
|
+};
|
|
+
|
|
struct dw_hdmi_mpll_config {
|
|
unsigned long mpixelclock;
|
|
struct {
|
|
@@ -112,6 +121,15 @@ struct dw_hdmi_phy_config {
|
|
u16 vlev_ctr; /* voltage level control */
|
|
};
|
|
|
|
+struct dw_hdmi_link_config {
|
|
+ bool dsc_mode;
|
|
+ bool frl_mode;
|
|
+ int frl_lanes;
|
|
+ int rate_per_lane;
|
|
+ int hcactive;
|
|
+ u8 pps_payload[128];
|
|
+};
|
|
+
|
|
struct dw_hdmi_phy_ops {
|
|
int (*init)(struct dw_hdmi *hdmi, void *data,
|
|
const struct drm_display_info *display,
|
|
@@ -123,14 +141,52 @@ struct dw_hdmi_phy_ops {
|
|
void (*setup_hpd)(struct dw_hdmi *hdmi, void *data);
|
|
};
|
|
|
|
+struct dw_hdmi_qp_phy_ops {
|
|
+ int (*init)(struct dw_hdmi_qp *hdmi, void *data,
|
|
+ struct drm_display_mode *mode);
|
|
+ void (*disable)(struct dw_hdmi_qp *hdmi, void *data);
|
|
+ enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi,
|
|
+ void *data);
|
|
+ void (*update_hpd)(struct dw_hdmi_qp *hdmi, void *data,
|
|
+ bool force, bool disabled, bool rxsense);
|
|
+ void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data);
|
|
+ void (*set_mode)(struct dw_hdmi_qp *dw_hdmi, void *data,
|
|
+ u32 mode_mask, bool enable);
|
|
+};
|
|
+
|
|
+struct dw_hdmi_property_ops {
|
|
+ void (*attach_properties)(struct drm_connector *connector,
|
|
+ unsigned int color, int version,
|
|
+ void *data);
|
|
+ void (*destroy_properties)(struct drm_connector *connector,
|
|
+ void *data);
|
|
+ int (*set_property)(struct drm_connector *connector,
|
|
+ struct drm_connector_state *state,
|
|
+ struct drm_property *property,
|
|
+ u64 val,
|
|
+ void *data);
|
|
+ int (*get_property)(struct drm_connector *connector,
|
|
+ const struct drm_connector_state *state,
|
|
+ struct drm_property *property,
|
|
+ u64 *val,
|
|
+ void *data);
|
|
+};
|
|
+
|
|
struct dw_hdmi_plat_data {
|
|
struct regmap *regm;
|
|
|
|
+ //[CC:] not in dowstream
|
|
unsigned int output_port;
|
|
|
|
+ unsigned long input_bus_format;
|
|
unsigned long input_bus_encoding;
|
|
+ unsigned int max_tmdsclk;
|
|
+ int id;
|
|
bool use_drm_infoframe;
|
|
bool ycbcr_420_allowed;
|
|
+ bool unsupported_yuv_input;
|
|
+ bool unsupported_deep_color;
|
|
+ bool is_hdmi_qp;
|
|
|
|
/*
|
|
* Private data passed to all the .mode_valid() and .configure_phy()
|
|
@@ -139,6 +195,7 @@ struct dw_hdmi_plat_data {
|
|
void *priv_data;
|
|
|
|
/* Platform-specific mode validation (optional). */
|
|
+ //[CC:] downstream changed "struct dw_hdmi *hdmi" to "struct drm_connector *connector"
|
|
enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data,
|
|
const struct drm_display_info *info,
|
|
const struct drm_display_mode *mode);
|
|
@@ -150,18 +207,51 @@ struct dw_hdmi_plat_data {
|
|
|
|
/* Vendor PHY support */
|
|
const struct dw_hdmi_phy_ops *phy_ops;
|
|
+ const struct dw_hdmi_qp_phy_ops *qp_phy_ops;
|
|
const char *phy_name;
|
|
void *phy_data;
|
|
unsigned int phy_force_vendor;
|
|
|
|
+ /* split mode */
|
|
+ bool split_mode;
|
|
+ bool first_screen;
|
|
+ struct dw_hdmi_qp *left;
|
|
+ struct dw_hdmi_qp *right;
|
|
+
|
|
/* Synopsys PHY support */
|
|
const struct dw_hdmi_mpll_config *mpll_cfg;
|
|
+ const struct dw_hdmi_mpll_config *mpll_cfg_420;
|
|
const struct dw_hdmi_curr_ctrl *cur_ctr;
|
|
const struct dw_hdmi_phy_config *phy_config;
|
|
int (*configure_phy)(struct dw_hdmi *hdmi, void *data,
|
|
unsigned long mpixelclock);
|
|
|
|
unsigned int disable_cec : 1;
|
|
+
|
|
+ //[CC:] 7b29b5f29585 ("drm/rockchip: dw_hdmi: Support HDMI 2.0 YCbCr 4:2:0")
|
|
+ unsigned long (*get_input_bus_format)(void *data);
|
|
+ unsigned long (*get_output_bus_format)(void *data);
|
|
+ unsigned long (*get_enc_in_encoding)(void *data);
|
|
+ unsigned long (*get_enc_out_encoding)(void *data);
|
|
+
|
|
+ unsigned long (*get_quant_range)(void *data);
|
|
+ struct drm_property *(*get_hdr_property)(void *data);
|
|
+ struct drm_property_blob *(*get_hdr_blob)(void *data);
|
|
+ bool (*get_color_changed)(void *data);
|
|
+ int (*get_yuv422_format)(struct drm_connector *connector,
|
|
+ struct edid *edid);
|
|
+ int (*get_edid_dsc_info)(void *data, struct edid *edid);
|
|
+ int (*get_next_hdr_data)(void *data, struct edid *edid,
|
|
+ struct drm_connector *connector);
|
|
+ struct dw_hdmi_link_config *(*get_link_cfg)(void *data);
|
|
+ void (*set_grf_cfg)(void *data);
|
|
+ void (*convert_to_split_mode)(struct drm_display_mode *mode);
|
|
+ void (*convert_to_origin_mode)(struct drm_display_mode *mode);
|
|
+ int (*dclk_set)(void *data, bool enable);
|
|
+
|
|
+ /* Vendor Property support */
|
|
+ const struct dw_hdmi_property_ops *property_ops;
|
|
+ struct drm_connector *connector;
|
|
};
|
|
|
|
struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
|
@@ -172,6 +262,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
|
|
struct drm_encoder *encoder,
|
|
const struct dw_hdmi_plat_data *plat_data);
|
|
|
|
+void dw_hdmi_suspend(struct dw_hdmi *hdmi);
|
|
void dw_hdmi_resume(struct dw_hdmi *hdmi);
|
|
|
|
void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
|
|
@@ -205,6 +296,32 @@ enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
|
|
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_set_quant_range(struct dw_hdmi *hdmi);
|
|
+void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val);
|
|
+bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi);
|
|
+int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi);
|
|
+//void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap);
|
|
+
|
|
+void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi);
|
|
+struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
|
+ struct drm_encoder *encoder,
|
|
+ struct dw_hdmi_plat_data *plat_data);
|
|
+void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi);
|
|
+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi);
|
|
+//void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change);
|
|
+//void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap);
|
|
+int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi);
|
|
+void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate);
|
|
+void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt);
|
|
+void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, u8 *channel_status,
|
|
+ bool ref2stream);
|
|
+void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca);
|
|
+//void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi,
|
|
+// struct hdmi_codec_params *hparms);
|
|
+//void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi);
|
|
+//void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi);
|
|
+int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn,
|
|
+ struct device *codec_dev);
|
|
|
|
bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi);
|
|
|
|
diff --git a/include/dt-bindings/clock/rockchip,rk3588-cru.h b/include/dt-bindings/clock/rockchip,rk3588-cru.h
|
|
index 5790b1391..50ba72980 100644
|
|
--- a/include/dt-bindings/clock/rockchip,rk3588-cru.h
|
|
+++ b/include/dt-bindings/clock/rockchip,rk3588-cru.h
|
|
@@ -733,8 +733,9 @@
|
|
#define ACLK_AV1_PRE 718
|
|
#define PCLK_AV1_PRE 719
|
|
#define HCLK_SDIO_PRE 720
|
|
+#define PCLK_VO1GRF 721
|
|
|
|
-#define CLK_NR_CLKS (HCLK_SDIO_PRE + 1)
|
|
+#define CLK_NR_CLKS (PCLK_VO1GRF + 1)
|
|
|
|
/* scmi-clocks indices */
|
|
|
|
diff --git a/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h
|
|
index 6e66a802b..668f199df 100644
|
|
--- a/include/dt-bindings/soc/rockchip,vop2.h
|
|
+++ b/include/dt-bindings/soc/rockchip,vop2.h
|
|
@@ -10,5 +10,9 @@
|
|
#define ROCKCHIP_VOP2_EP_LVDS0 5
|
|
#define ROCKCHIP_VOP2_EP_MIPI1 6
|
|
#define ROCKCHIP_VOP2_EP_LVDS1 7
|
|
+#define ROCKCHIP_VOP2_EP_HDMI1 8
|
|
+#define ROCKCHIP_VOP2_EP_EDP1 9
|
|
+#define ROCKCHIP_VOP2_EP_DP0 10
|
|
+#define ROCKCHIP_VOP2_EP_DP1 11
|
|
|
|
#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */
|
|
diff --git a/include/linux/math.h b/include/linux/math.h
|
|
index dd4152711..f80bfb375 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; })
|
|
|
|
diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
|
|
new file mode 100644
|
|
index 000000000..246192fa2
|
|
--- /dev/null
|
|
+++ b/include/uapi/drm/rockchip_drm.h
|
|
@@ -0,0 +1,134 @@
|
|
+/*
|
|
+ *
|
|
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
|
|
+ * Authors:
|
|
+ * Mark Yao <yzq@rock-chips.com>
|
|
+ *
|
|
+ * base on exynos_drm.h
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
|
+ * option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _UAPI_ROCKCHIP_DRM_H
|
|
+#define _UAPI_ROCKCHIP_DRM_H
|
|
+
|
|
+#include <drm/drm.h>
|
|
+#include <drm/drm_file.h>
|
|
+
|
|
+/*
|
|
+ * Send vcnt event instead of blocking,
|
|
+ * like _DRM_VBLANK_EVENT
|
|
+ */
|
|
+#define _DRM_ROCKCHIP_VCNT_EVENT 0x80000000
|
|
+#define DRM_EVENT_ROCKCHIP_CRTC_VCNT 0xf
|
|
+
|
|
+/* memory type definitions. */
|
|
+enum drm_rockchip_gem_mem_type {
|
|
+ /* Physically Continuous memory. */
|
|
+ ROCKCHIP_BO_CONTIG = 1 << 0,
|
|
+ /* cachable mapping. */
|
|
+ ROCKCHIP_BO_CACHABLE = 1 << 1,
|
|
+ /* write-combine mapping. */
|
|
+ ROCKCHIP_BO_WC = 1 << 2,
|
|
+ ROCKCHIP_BO_SECURE = 1 << 3,
|
|
+ /* keep kmap for cma buffer or alloc kmap for other type memory */
|
|
+ ROCKCHIP_BO_ALLOC_KMAP = 1 << 4,
|
|
+ ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE |
|
|
+ ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE | ROCKCHIP_BO_ALLOC_KMAP,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * User-desired buffer creation information structure.
|
|
+ *
|
|
+ * @size: user-desired memory allocation size.
|
|
+ * @flags: user request for setting memory type or cache attributes.
|
|
+ * @handle: returned a handle to created gem object.
|
|
+ * - this handle will be set by gem module of kernel side.
|
|
+ */
|
|
+struct drm_rockchip_gem_create {
|
|
+ uint64_t size;
|
|
+ uint32_t flags;
|
|
+ uint32_t handle;
|
|
+};
|
|
+
|
|
+struct drm_rockchip_gem_phys {
|
|
+ uint32_t handle;
|
|
+ uint32_t phy_addr;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * A structure for getting buffer offset.
|
|
+ *
|
|
+ * @handle: a pointer to gem object created.
|
|
+ * @pad: just padding to be 64-bit aligned.
|
|
+ * @offset: relatived offset value of the memory region allocated.
|
|
+ * - this value should be set by user.
|
|
+ */
|
|
+struct drm_rockchip_gem_map_off {
|
|
+ uint32_t handle;
|
|
+ uint32_t pad;
|
|
+ uint64_t offset;
|
|
+};
|
|
+
|
|
+/* acquire type definitions. */
|
|
+enum drm_rockchip_gem_cpu_acquire_type {
|
|
+ DRM_ROCKCHIP_GEM_CPU_ACQUIRE_SHARED = 0x0,
|
|
+ DRM_ROCKCHIP_GEM_CPU_ACQUIRE_EXCLUSIVE = 0x1,
|
|
+};
|
|
+
|
|
+enum rockchip_crtc_feture {
|
|
+ ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE,
|
|
+ ROCKCHIP_DRM_CRTC_FEATURE_HDR10,
|
|
+ ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR,
|
|
+};
|
|
+
|
|
+enum rockchip_plane_feture {
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_SCALE,
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_ALPHA,
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR,
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR,
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_AFBDC,
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_PDAF_POS,
|
|
+ ROCKCHIP_DRM_PLANE_FEATURE_MAX,
|
|
+};
|
|
+
|
|
+enum rockchip_cabc_mode {
|
|
+ ROCKCHIP_DRM_CABC_MODE_DISABLE,
|
|
+ ROCKCHIP_DRM_CABC_MODE_NORMAL,
|
|
+ ROCKCHIP_DRM_CABC_MODE_LOWPOWER,
|
|
+ ROCKCHIP_DRM_CABC_MODE_USERSPACE,
|
|
+};
|
|
+
|
|
+struct drm_rockchip_vcnt_event {
|
|
+ struct drm_pending_event base;
|
|
+};
|
|
+
|
|
+#define DRM_ROCKCHIP_GEM_CREATE 0x00
|
|
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x01
|
|
+#define DRM_ROCKCHIP_GEM_CPU_ACQUIRE 0x02
|
|
+#define DRM_ROCKCHIP_GEM_CPU_RELEASE 0x03
|
|
+#define DRM_ROCKCHIP_GEM_GET_PHYS 0x04
|
|
+#define DRM_ROCKCHIP_GET_VCNT_EVENT 0x05
|
|
+
|
|
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
|
|
+ DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
|
|
+
|
|
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \
|
|
+ DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
|
|
+
|
|
+#define DRM_IOCTL_ROCKCHIP_GEM_CPU_ACQUIRE DRM_IOWR(DRM_COMMAND_BASE + \
|
|
+ DRM_ROCKCHIP_GEM_CPU_ACQUIRE, struct drm_rockchip_gem_cpu_acquire)
|
|
+
|
|
+#define DRM_IOCTL_ROCKCHIP_GEM_CPU_RELEASE DRM_IOWR(DRM_COMMAND_BASE + \
|
|
+ DRM_ROCKCHIP_GEM_CPU_RELEASE, struct drm_rockchip_gem_cpu_release)
|
|
+
|
|
+#define DRM_IOCTL_ROCKCHIP_GEM_GET_PHYS DRM_IOWR(DRM_COMMAND_BASE + \
|
|
+ DRM_ROCKCHIP_GEM_GET_PHYS, struct drm_rockchip_gem_phys)
|
|
+
|
|
+#define DRM_IOCTL_ROCKCHIP_GET_VCNT_EVENT DRM_IOWR(DRM_COMMAND_BASE + \
|
|
+ DRM_ROCKCHIP_GET_VCNT_EVENT, union drm_wait_vblank)
|
|
+
|
|
+#endif /* _UAPI_ROCKCHIP_DRM_H */
|