armbian-build/patch/kernel/rockchip-rk3588-edge/0023-Add-Initial-HDMITX-VOP2-For-RK3588.patch
M. Efe Çetin af3b4fafec
Update rk3588-edge to 6.7-rc.1 and add support for VOP2, Crypto, HW RNG (#5928)
* Update rk3588-edge to 6.7-rc1 kernel
* Update patches for rk3588-edge and add support for crypto, trng, hdmi tx, vop2
2023-11-19 22:30:26 +01:00

13412 lines
406 KiB
Diff

From e4e39588f8b46db104817795a703b1f701da9c36 Mon Sep 17 00:00:00 2001
From: Sebastian Reichel <sebastian.reichel@collabora.com>
Date: Tue, 13 Jun 2023 16:45:05 +0200
Subject: [PATCH 1/9] clk: rockchip: rk3588: fix pclk_vo0grf and pclk_vo1grf
Currently pclk_vo1grf is not exposed, but it should be referenced
from the vo1_grf syscon, which needs it enabled. That syscon will
be required for HDMI-RX functionality among other things.
Apart from that pclk_vo0grf and pclk_vo1grf are both linked gates
and need the VO's hclk enabled in addition to their parent clock.
No Fixes tag has been added, since the logic requiring these clocks
is not yet upstream anyways.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
(cherry picked from commit a5f467748264d9a337cb40083efde04c748d8c3e)
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/clk/rockchip/clk-rk3588.c | 11 +++++------
include/dt-bindings/clock/rockchip,rk3588-cru.h | 3 ++-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c
index 6994165e0395..c6b605d6d769 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/include/dt-bindings/clock/rockchip,rk3588-cru.h b/include/dt-bindings/clock/rockchip,rk3588-cru.h
index 5790b1391201..50ba72980190 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 */
--
2.42.1
From 7ec0cb44173dd1a5357a66afa7f3b5de956df7ee Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Thu, 14 Sep 2023 19:10:30 +0300
Subject: [PATCH 2/9] phy: rockchip: Add Samsung HDMI/DP Combo PHY HDMI driver
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/Kconfig | 7 +
drivers/phy/rockchip/Makefile | 1 +
.../phy-rockchip-samsung-hdptx-hdmi.c | 2347 +++++++++++++++++
3 files changed, 2355 insertions(+)
create mode 100644 drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index d21b458c1d18..62c18e25b8e0 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
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index 25d2e1355db7..d266414a1ffb 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -8,6 +8,7 @@ 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
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 000000000000..036db08777cf
--- /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");
--
2.42.1
From 171fd53717525f0f6dc56e90e6f38a9038c5c779 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Wed, 1 Nov 2023 18:50:38 +0200
Subject: [PATCH 3/9] drm/bridge: synopsys: Add initial support for DW HDMI QP
TX Controller
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/Makefile | 2 +-
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 2997 ++++++++++++++++++
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 831 +++++
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 89 +
drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 4 +
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2742 +++++++++++++++-
include/drm/bridge/dw_hdmi.h | 117 +
7 files changed, 6716 insertions(+), 66 deletions(-)
create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile
index ce715562e9e5..8354e4879f70 100644
--- a/drivers/gpu/drm/bridge/synopsys/Makefile
+++ b/drivers/gpu/drm/bridge/synopsys/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-qp.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
new file mode 100644
index 000000000000..935b116c2a3d
--- /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", &reg, &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 000000000000..4cac70f2d11d
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
@@ -0,0 +1,831 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ * Algea Cao <algea.cao@rock-chips.com>
+ */
+#ifndef __DW_HDMI_QP_H__
+#define __DW_HDMI_QP_H__
+/* Main Unit Registers */
+#define CORE_ID 0x0
+#define VER_NUMBER 0x4
+#define VER_TYPE 0x8
+#define CONFIG_REG 0xc
+#define CONFIG_CEC BIT(28)
+#define CONFIG_AUD_UD BIT(23)
+#define CORE_TIMESTAMP_HHMM 0x14
+#define CORE_TIMESTAMP_MMDD 0x18
+#define CORE_TIMESTAMP_YYYY 0x1c
+/* Reset Manager Registers */
+#define GLOBAL_SWRESET_REQUEST 0x40
+#define EARCRX_CMDC_SWINIT_P BIT(27)
+#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10)
+#define GLOBAL_SWDISABLE 0x44
+#define CEC_SWDISABLE BIT(17)
+#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10)
+#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6)
+#define RESET_MANAGER_CONFIG0 0x48
+#define RESET_MANAGER_STATUS0 0x50
+#define RESET_MANAGER_STATUS1 0x54
+#define RESET_MANAGER_STATUS2 0x58
+/* Timer Base Registers */
+#define TIMER_BASE_CONFIG0 0x80
+#define TIMER_BASE_STATUS0 0x84
+/* CMU Registers */
+#define CMU_CONFIG0 0xa0
+#define CMU_CONFIG1 0xa4
+#define CMU_CONFIG2 0xa8
+#define CMU_CONFIG3 0xac
+#define CMU_STATUS 0xb0
+#define DISPLAY_CLK_MONITOR 0x3f
+#define DISPLAY_CLK_LOCKED 0X15
+#define EARC_BPCLK_OFF BIT(9)
+#define AUDCLK_OFF BIT(7)
+#define LINKQPCLK_OFF BIT(5)
+#define VIDQPCLK_OFF BIT(3)
+#define IPI_CLK_OFF BIT(1)
+#define CMU_IPI_CLK_FREQ 0xb4
+#define CMU_VIDQPCLK_FREQ 0xb8
+#define CMU_LINKQPCLK_FREQ 0xbc
+#define CMU_AUDQPCLK_FREQ 0xc0
+#define CMU_EARC_BPCLK_FREQ 0xc4
+/* I2CM Registers */
+#define I2CM_SM_SCL_CONFIG0 0xe0
+#define I2CM_FM_SCL_CONFIG0 0xe4
+#define I2CM_CONFIG0 0xe8
+#define I2CM_CONTROL0 0xec
+#define I2CM_STATUS0 0xf0
+#define I2CM_INTERFACE_CONTROL0 0xf4
+#define I2CM_ADDR 0xff000
+#define I2CM_SLVADDR 0xfe0
+#define I2CM_WR_MASK 0x1e
+#define I2CM_EXT_READ BIT(4)
+#define I2CM_SHORT_READ BIT(3)
+#define I2CM_FM_READ BIT(2)
+#define I2CM_FM_WRITE BIT(1)
+#define I2CM_FM_EN BIT(0)
+#define I2CM_INTERFACE_CONTROL1 0xf8
+#define I2CM_SEG_PTR 0x7f80
+#define I2CM_SEG_ADDR 0x7f
+#define I2CM_INTERFACE_WRDATA_0_3 0xfc
+#define I2CM_INTERFACE_WRDATA_4_7 0x100
+#define I2CM_INTERFACE_WRDATA_8_11 0x104
+#define I2CM_INTERFACE_WRDATA_12_15 0x108
+#define I2CM_INTERFACE_RDDATA_0_3 0x10c
+#define I2CM_INTERFACE_RDDATA_4_7 0x110
+#define I2CM_INTERFACE_RDDATA_8_11 0x114
+#define I2CM_INTERFACE_RDDATA_12_15 0x118
+/* SCDC Registers */
+#define SCDC_CONFIG0 0x140
+#define SCDC_I2C_FM_EN BIT(12)
+#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6)
+#define SCDC_UPD_FLAGS_POLL_EN BIT(4)
+#define SCDC_CONTROL0 0x148
+#define SCDC_STATUS0 0x150
+#define STATUS_UPDATE BIT(0)
+#define FRL_START BIT(4)
+#define FLT_UPDATE BIT(5)
+/* FLT Registers */
+#define FLT_CONFIG0 0x160
+#define FLT_CONFIG1 0x164
+#define FLT_CONFIG2 0x168
+#define FLT_CONTROL0 0x170
+/* Main Unit 2 Registers */
+#define MAINUNIT_STATUS0 0x180
+/* Video Interface Registers */
+#define VIDEO_INTERFACE_CONFIG0 0x800
+#define VIDEO_INTERFACE_CONFIG1 0x804
+#define VIDEO_INTERFACE_CONFIG2 0x808
+#define VIDEO_INTERFACE_CONTROL0 0x80c
+#define VIDEO_INTERFACE_STATUS0 0x814
+/* Video Packing Registers */
+#define VIDEO_PACKING_CONFIG0 0x81c
+/* Audio Interface Registers */
+#define AUDIO_INTERFACE_CONFIG0 0x820
+#define AUD_IF_SEL_MSK 0x3
+#define AUD_IF_SPDIF 0x2
+#define AUD_IF_I2S 0x1
+#define AUD_IF_PAI 0x0
+#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2)
+#define AUD_FIFO_INIT_ON_OVF_EN BIT(2)
+#define I2S_LINES_EN_MSK GENMASK(7, 4)
+#define I2S_LINES_EN(x) BIT(x + 4)
+#define I2S_BPCUV_RCV_MSK BIT(12)
+#define I2S_BPCUV_RCV_EN BIT(12)
+#define I2S_BPCUV_RCV_DIS 0
+#define SPDIF_LINES_EN GENMASK(19, 16)
+#define AUD_FORMAT_MSK GENMASK(26, 24)
+#define AUD_3DOBA (0x7 << 24)
+#define AUD_3DASP (0x6 << 24)
+#define AUD_MSOBA (0x5 << 24)
+#define AUD_MSASP (0x4 << 24)
+#define AUD_HBR (0x3 << 24)
+#define AUD_DST (0x2 << 24)
+#define AUD_OBA (0x1 << 24)
+#define AUD_ASP (0x0 << 24)
+#define AUDIO_INTERFACE_CONFIG1 0x824
+#define AUDIO_INTERFACE_CONTROL0 0x82c
+#define AUDIO_FIFO_CLR_P BIT(0)
+#define AUDIO_INTERFACE_STATUS0 0x834
+/* Frame Composer Registers */
+#define FRAME_COMPOSER_CONFIG0 0x840
+#define FRAME_COMPOSER_CONFIG1 0x844
+#define FRAME_COMPOSER_CONFIG2 0x848
+#define FRAME_COMPOSER_CONFIG3 0x84c
+#define FRAME_COMPOSER_CONFIG4 0x850
+#define FRAME_COMPOSER_CONFIG5 0x854
+#define FRAME_COMPOSER_CONFIG6 0x858
+#define FRAME_COMPOSER_CONFIG7 0x85c
+#define FRAME_COMPOSER_CONFIG8 0x860
+#define FRAME_COMPOSER_CONFIG9 0x864
+#define FRAME_COMPOSER_CONTROL0 0x86c
+/* Video Monitor Registers */
+#define VIDEO_MONITOR_CONFIG0 0x880
+#define VIDEO_MONITOR_STATUS0 0x884
+#define VIDEO_MONITOR_STATUS1 0x888
+#define VIDEO_MONITOR_STATUS2 0x88c
+#define VIDEO_MONITOR_STATUS3 0x890
+#define VIDEO_MONITOR_STATUS4 0x894
+#define VIDEO_MONITOR_STATUS5 0x898
+#define VIDEO_MONITOR_STATUS6 0x89c
+/* HDCP2 Logic Registers */
+#define HDCP2LOGIC_CONFIG0 0x8e0
+#define HDCP2_BYPASS BIT(0)
+#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4
+#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8
+/* HDCP14 Registers */
+#define HDCP14_CONFIG0 0x900
+#define HDCP14_CONFIG1 0x904
+#define HDCP14_CONFIG2 0x908
+#define HDCP14_CONFIG3 0x90c
+#define HDCP14_KEY_SEED 0x914
+#define HDCP14_KEY_H 0x918
+#define HDCP14_KEY_L 0x91c
+#define HDCP14_KEY_STATUS 0x920
+#define HDCP14_AKSV_H 0x924
+#define HDCP14_AKSV_L 0x928
+#define HDCP14_AN_H 0x92c
+#define HDCP14_AN_L 0x930
+#define HDCP14_STATUS0 0x934
+#define HDCP14_STATUS1 0x938
+/* Scrambler Registers */
+#define SCRAMB_CONFIG0 0x960
+/* Video Configuration Registers */
+#define LINK_CONFIG0 0x968
+#define OPMODE_FRL_4LANES BIT(8)
+#define OPMODE_DVI BIT(4)
+#define OPMODE_FRL BIT(0)
+/* TMDS FIFO Registers */
+#define TMDS_FIFO_CONFIG0 0x970
+#define TMDS_FIFO_CONTROL0 0x974
+/* FRL RSFEC Registers */
+#define FRL_RSFEC_CONFIG0 0xa20
+#define FRL_RSFEC_STATUS0 0xa30
+/* FRL Packetizer Registers */
+#define FRL_PKTZ_CONFIG0 0xa40
+#define FRL_PKTZ_CONTROL0 0xa44
+#define FRL_PKTZ_CONTROL1 0xa50
+#define FRL_PKTZ_STATUS1 0xa54
+/* Packet Scheduler Registers */
+#define PKTSCHED_CONFIG0 0xa80
+#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84
+#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88
+#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c
+#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90
+#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94
+#define PKTSCHED_PKT_CONFIG0 0xa98
+#define PKTSCHED_PKT_CONFIG1 0xa9c
+#define PKTSCHED_DRMI_FIELDRATE BIT(13)
+#define PKTSCHED_AVI_FIELDRATE BIT(12)
+#define PKTSCHED_PKT_CONFIG2 0xaa0
+#define PKTSCHED_PKT_CONFIG3 0xaa4
+#define PKTSCHED_PKT_EN 0xaa8
+#define PKTSCHED_DRMI_TX_EN BIT(17)
+#define PKTSCHED_AUDI_TX_EN BIT(15)
+#define PKTSCHED_AVI_TX_EN BIT(13)
+#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10)
+#define PKTSCHED_AMD_TX_EN BIT(8)
+#define PKTSCHED_GCP_TX_EN BIT(3)
+#define PKTSCHED_AUDS_TX_EN BIT(2)
+#define PKTSCHED_ACR_TX_EN BIT(1)
+#define PKTSCHED_NULL_TX_EN BIT(0)
+#define PKTSCHED_PKT_CONTROL0 0xaac
+#define PKTSCHED_PKT_SEND 0xab0
+#define PKTSCHED_PKT_STATUS0 0xab4
+#define PKTSCHED_PKT_STATUS1 0xab8
+#define PKT_NULL_CONTENTS0 0xb00
+#define PKT_NULL_CONTENTS1 0xb04
+#define PKT_NULL_CONTENTS2 0xb08
+#define PKT_NULL_CONTENTS3 0xb0c
+#define PKT_NULL_CONTENTS4 0xb10
+#define PKT_NULL_CONTENTS5 0xb14
+#define PKT_NULL_CONTENTS6 0xb18
+#define PKT_NULL_CONTENTS7 0xb1c
+#define PKT_ACP_CONTENTS0 0xb20
+#define PKT_ACP_CONTENTS1 0xb24
+#define PKT_ACP_CONTENTS2 0xb28
+#define PKT_ACP_CONTENTS3 0xb2c
+#define PKT_ACP_CONTENTS4 0xb30
+#define PKT_ACP_CONTENTS5 0xb34
+#define PKT_ACP_CONTENTS6 0xb38
+#define PKT_ACP_CONTENTS7 0xb3c
+#define PKT_ISRC1_CONTENTS0 0xb40
+#define PKT_ISRC1_CONTENTS1 0xb44
+#define PKT_ISRC1_CONTENTS2 0xb48
+#define PKT_ISRC1_CONTENTS3 0xb4c
+#define PKT_ISRC1_CONTENTS4 0xb50
+#define PKT_ISRC1_CONTENTS5 0xb54
+#define PKT_ISRC1_CONTENTS6 0xb58
+#define PKT_ISRC1_CONTENTS7 0xb5c
+#define PKT_ISRC2_CONTENTS0 0xb60
+#define PKT_ISRC2_CONTENTS1 0xb64
+#define PKT_ISRC2_CONTENTS2 0xb68
+#define PKT_ISRC2_CONTENTS3 0xb6c
+#define PKT_ISRC2_CONTENTS4 0xb70
+#define PKT_ISRC2_CONTENTS5 0xb74
+#define PKT_ISRC2_CONTENTS6 0xb78
+#define PKT_ISRC2_CONTENTS7 0xb7c
+#define PKT_GMD_CONTENTS0 0xb80
+#define PKT_GMD_CONTENTS1 0xb84
+#define PKT_GMD_CONTENTS2 0xb88
+#define PKT_GMD_CONTENTS3 0xb8c
+#define PKT_GMD_CONTENTS4 0xb90
+#define PKT_GMD_CONTENTS5 0xb94
+#define PKT_GMD_CONTENTS6 0xb98
+#define PKT_GMD_CONTENTS7 0xb9c
+#define PKT_AMD_CONTENTS0 0xba0
+#define PKT_AMD_CONTENTS1 0xba4
+#define PKT_AMD_CONTENTS2 0xba8
+#define PKT_AMD_CONTENTS3 0xbac
+#define PKT_AMD_CONTENTS4 0xbb0
+#define PKT_AMD_CONTENTS5 0xbb4
+#define PKT_AMD_CONTENTS6 0xbb8
+#define PKT_AMD_CONTENTS7 0xbbc
+#define PKT_VSI_CONTENTS0 0xbc0
+#define PKT_VSI_CONTENTS1 0xbc4
+#define PKT_VSI_CONTENTS2 0xbc8
+#define PKT_VSI_CONTENTS3 0xbcc
+#define PKT_VSI_CONTENTS4 0xbd0
+#define PKT_VSI_CONTENTS5 0xbd4
+#define PKT_VSI_CONTENTS6 0xbd8
+#define PKT_VSI_CONTENTS7 0xbdc
+#define PKT_AVI_CONTENTS0 0xbe0
+#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4)
+#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04
+#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08
+#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80
+#define PKT_AVI_CONTENTS1 0xbe4
+#define PKT_AVI_CONTENTS2 0xbe8
+#define PKT_AVI_CONTENTS3 0xbec
+#define PKT_AVI_CONTENTS4 0xbf0
+#define PKT_AVI_CONTENTS5 0xbf4
+#define PKT_AVI_CONTENTS6 0xbf8
+#define PKT_AVI_CONTENTS7 0xbfc
+#define PKT_SPDI_CONTENTS0 0xc00
+#define PKT_SPDI_CONTENTS1 0xc04
+#define PKT_SPDI_CONTENTS2 0xc08
+#define PKT_SPDI_CONTENTS3 0xc0c
+#define PKT_SPDI_CONTENTS4 0xc10
+#define PKT_SPDI_CONTENTS5 0xc14
+#define PKT_SPDI_CONTENTS6 0xc18
+#define PKT_SPDI_CONTENTS7 0xc1c
+#define PKT_AUDI_CONTENTS0 0xc20
+#define PKT_AUDI_CONTENTS1 0xc24
+#define PKT_AUDI_CONTENTS2 0xc28
+#define PKT_AUDI_CONTENTS3 0xc2c
+#define PKT_AUDI_CONTENTS4 0xc30
+#define PKT_AUDI_CONTENTS5 0xc34
+#define PKT_AUDI_CONTENTS6 0xc38
+#define PKT_AUDI_CONTENTS7 0xc3c
+#define PKT_NVI_CONTENTS0 0xc40
+#define PKT_NVI_CONTENTS1 0xc44
+#define PKT_NVI_CONTENTS2 0xc48
+#define PKT_NVI_CONTENTS3 0xc4c
+#define PKT_NVI_CONTENTS4 0xc50
+#define PKT_NVI_CONTENTS5 0xc54
+#define PKT_NVI_CONTENTS6 0xc58
+#define PKT_NVI_CONTENTS7 0xc5c
+#define PKT_DRMI_CONTENTS0 0xc60
+#define PKT_DRMI_CONTENTS1 0xc64
+#define PKT_DRMI_CONTENTS2 0xc68
+#define PKT_DRMI_CONTENTS3 0xc6c
+#define PKT_DRMI_CONTENTS4 0xc70
+#define PKT_DRMI_CONTENTS5 0xc74
+#define PKT_DRMI_CONTENTS6 0xc78
+#define PKT_DRMI_CONTENTS7 0xc7c
+#define PKT_GHDMI1_CONTENTS0 0xc80
+#define PKT_GHDMI1_CONTENTS1 0xc84
+#define PKT_GHDMI1_CONTENTS2 0xc88
+#define PKT_GHDMI1_CONTENTS3 0xc8c
+#define PKT_GHDMI1_CONTENTS4 0xc90
+#define PKT_GHDMI1_CONTENTS5 0xc94
+#define PKT_GHDMI1_CONTENTS6 0xc98
+#define PKT_GHDMI1_CONTENTS7 0xc9c
+#define PKT_GHDMI2_CONTENTS0 0xca0
+#define PKT_GHDMI2_CONTENTS1 0xca4
+#define PKT_GHDMI2_CONTENTS2 0xca8
+#define PKT_GHDMI2_CONTENTS3 0xcac
+#define PKT_GHDMI2_CONTENTS4 0xcb0
+#define PKT_GHDMI2_CONTENTS5 0xcb4
+#define PKT_GHDMI2_CONTENTS6 0xcb8
+#define PKT_GHDMI2_CONTENTS7 0xcbc
+/* EMP Packetizer Registers */
+#define PKT_EMP_CONFIG0 0xce0
+#define PKT_EMP_CONTROL0 0xcec
+#define PKT_EMP_CONTROL1 0xcf0
+#define PKT_EMP_CONTROL2 0xcf4
+#define PKT_EMP_VTEM_CONTENTS0 0xd00
+#define PKT_EMP_VTEM_CONTENTS1 0xd04
+#define PKT_EMP_VTEM_CONTENTS2 0xd08
+#define PKT_EMP_VTEM_CONTENTS3 0xd0c
+#define PKT_EMP_VTEM_CONTENTS4 0xd10
+#define PKT_EMP_VTEM_CONTENTS5 0xd14
+#define PKT_EMP_VTEM_CONTENTS6 0xd18
+#define PKT_EMP_VTEM_CONTENTS7 0xd1c
+#define PKT0_EMP_CVTEM_CONTENTS0 0xd20
+#define PKT0_EMP_CVTEM_CONTENTS1 0xd24
+#define PKT0_EMP_CVTEM_CONTENTS2 0xd28
+#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c
+#define PKT0_EMP_CVTEM_CONTENTS4 0xd30
+#define PKT0_EMP_CVTEM_CONTENTS5 0xd34
+#define PKT0_EMP_CVTEM_CONTENTS6 0xd38
+#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c
+#define PKT1_EMP_CVTEM_CONTENTS0 0xd40
+#define PKT1_EMP_CVTEM_CONTENTS1 0xd44
+#define PKT1_EMP_CVTEM_CONTENTS2 0xd48
+#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c
+#define PKT1_EMP_CVTEM_CONTENTS4 0xd50
+#define PKT1_EMP_CVTEM_CONTENTS5 0xd54
+#define PKT1_EMP_CVTEM_CONTENTS6 0xd58
+#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c
+#define PKT2_EMP_CVTEM_CONTENTS0 0xd60
+#define PKT2_EMP_CVTEM_CONTENTS1 0xd64
+#define PKT2_EMP_CVTEM_CONTENTS2 0xd68
+#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c
+#define PKT2_EMP_CVTEM_CONTENTS4 0xd70
+#define PKT2_EMP_CVTEM_CONTENTS5 0xd74
+#define PKT2_EMP_CVTEM_CONTENTS6 0xd78
+#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c
+#define PKT3_EMP_CVTEM_CONTENTS0 0xd80
+#define PKT3_EMP_CVTEM_CONTENTS1 0xd84
+#define PKT3_EMP_CVTEM_CONTENTS2 0xd88
+#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c
+#define PKT3_EMP_CVTEM_CONTENTS4 0xd90
+#define PKT3_EMP_CVTEM_CONTENTS5 0xd94
+#define PKT3_EMP_CVTEM_CONTENTS6 0xd98
+#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c
+#define PKT4_EMP_CVTEM_CONTENTS0 0xda0
+#define PKT4_EMP_CVTEM_CONTENTS1 0xda4
+#define PKT4_EMP_CVTEM_CONTENTS2 0xda8
+#define PKT4_EMP_CVTEM_CONTENTS3 0xdac
+#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0
+#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4
+#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8
+#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc
+#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0
+#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4
+#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8
+#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc
+#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0
+#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4
+#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8
+#define PKT5_EMP_CVTEM_CONTENTS7 0xddc
+/* Audio Packetizer Registers */
+#define AUDPKT_CONTROL0 0xe20
+#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12)
+#define AUDPKT_PBIT_FORCE_EN BIT(12)
+#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0)
+#define AUDPKT_CHSTATUS_OVR_EN BIT(0)
+#define AUDPKT_CONTROL1 0xe24
+#define AUDPKT_ACR_CONTROL0 0xe40
+#define AUDPKT_ACR_N_VALUE 0xfffff
+#define AUDPKT_ACR_CONTROL1 0xe44
+#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4)
+#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4)
+#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1)
+#define AUDPKT_ACR_CTS_OVR_EN BIT(1)
+#define AUDPKT_ACR_STATUS0 0xe4c
+#define AUDPKT_CHSTATUS_OVR0 0xe60
+#define AUDPKT_CHSTATUS_OVR1 0xe64
+/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */
+#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0)
+#define AUDPKT_CHSTATUS_SR_22050 0x4
+#define AUDPKT_CHSTATUS_SR_24000 0x6
+#define AUDPKT_CHSTATUS_SR_32000 0x3
+#define AUDPKT_CHSTATUS_SR_44100 0x0
+#define AUDPKT_CHSTATUS_SR_48000 0x2
+#define AUDPKT_CHSTATUS_SR_88200 0x8
+#define AUDPKT_CHSTATUS_SR_96000 0xa
+#define AUDPKT_CHSTATUS_SR_176400 0xc
+#define AUDPKT_CHSTATUS_SR_192000 0xe
+#define AUDPKT_CHSTATUS_SR_768000 0x9
+#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1
+/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */
+#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12)
+#define AUDPKT_CHSTATUS_OSR_8000 0x6
+#define AUDPKT_CHSTATUS_OSR_11025 0xa
+#define AUDPKT_CHSTATUS_OSR_12000 0x2
+#define AUDPKT_CHSTATUS_OSR_16000 0x8
+#define AUDPKT_CHSTATUS_OSR_22050 0xb
+#define AUDPKT_CHSTATUS_OSR_24000 0x9
+#define AUDPKT_CHSTATUS_OSR_32000 0xc
+#define AUDPKT_CHSTATUS_OSR_44100 0xf
+#define AUDPKT_CHSTATUS_OSR_48000 0xd
+#define AUDPKT_CHSTATUS_OSR_88200 0x7
+#define AUDPKT_CHSTATUS_OSR_96000 0x5
+#define AUDPKT_CHSTATUS_OSR_176400 0x3
+#define AUDPKT_CHSTATUS_OSR_192000 0x1
+#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0
+#define AUDPKT_CHSTATUS_OVR2 0xe68
+#define AUDPKT_CHSTATUS_OVR3 0xe6c
+#define AUDPKT_CHSTATUS_OVR4 0xe70
+#define AUDPKT_CHSTATUS_OVR5 0xe74
+#define AUDPKT_CHSTATUS_OVR6 0xe78
+#define AUDPKT_CHSTATUS_OVR7 0xe7c
+#define AUDPKT_CHSTATUS_OVR8 0xe80
+#define AUDPKT_CHSTATUS_OVR9 0xe84
+#define AUDPKT_CHSTATUS_OVR10 0xe88
+#define AUDPKT_CHSTATUS_OVR11 0xe8c
+#define AUDPKT_CHSTATUS_OVR12 0xe90
+#define AUDPKT_CHSTATUS_OVR13 0xe94
+#define AUDPKT_CHSTATUS_OVR14 0xe98
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c
+#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20
+#define AUDPKT_VBIT_OVR0 0xf24
+/* CEC Registers */
+#define CEC_TX_CONTROL 0x1000
+#define CEC_STATUS 0x1004
+#define CEC_CONFIG 0x1008
+#define CEC_ADDR 0x100c
+#define CEC_TX_COUNT 0x1020
+#define CEC_TX_DATA3_0 0x1024
+#define CEC_TX_DATA7_4 0x1028
+#define CEC_TX_DATA11_8 0x102c
+#define CEC_TX_DATA15_12 0x1030
+#define CEC_RX_COUNT_STATUS 0x1040
+#define CEC_RX_DATA3_0 0x1044
+#define CEC_RX_DATA7_4 0x1048
+#define CEC_RX_DATA11_8 0x104c
+#define CEC_RX_DATA15_12 0x1050
+#define CEC_LOCK_CONTROL 0x1054
+#define CEC_RXQUAL_BITTIME_CONFIG 0x1060
+#define CEC_RX_BITTIME_CONFIG 0x1064
+#define CEC_TX_BITTIME_CONFIG 0x1068
+/* eARC RX CMDC Registers */
+#define EARCRX_CMDC_CONFIG0 0x1800
+#define EARCRX_XACTREAD_STOP_CFG BIT(26)
+#define EARCRX_XACTREAD_RETRY_CFG BIT(25)
+#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24)
+#define EARCRX_CMDC_XACT_RESTART_EN BIT(18)
+#define EARCRX_CMDC_CONFIG1 0x1804
+#define EARCRX_CMDC_CONTROL 0x1808
+#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4)
+#define EARCRX_CMDC_DISCOVERY_EN BIT(3)
+#define EARCRX_CONNECTOR_HPD BIT(1)
+#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c
+#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810
+#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814
+#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818
+#define EARCRX_CMDC_STATUS 0x181c
+#define EARCRX_CMDC_XACT_INFO 0x1820
+#define EARCRX_CMDC_XACT_ACTION 0x1824
+#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828
+#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c
+#define EARCRX_CMDC_XACT_WR0 0x1840
+#define EARCRX_CMDC_XACT_WR1 0x1844
+#define EARCRX_CMDC_XACT_WR2 0x1848
+#define EARCRX_CMDC_XACT_WR3 0x184c
+#define EARCRX_CMDC_XACT_WR4 0x1850
+#define EARCRX_CMDC_XACT_WR5 0x1854
+#define EARCRX_CMDC_XACT_WR6 0x1858
+#define EARCRX_CMDC_XACT_WR7 0x185c
+#define EARCRX_CMDC_XACT_WR8 0x1860
+#define EARCRX_CMDC_XACT_WR9 0x1864
+#define EARCRX_CMDC_XACT_WR10 0x1868
+#define EARCRX_CMDC_XACT_WR11 0x186c
+#define EARCRX_CMDC_XACT_WR12 0x1870
+#define EARCRX_CMDC_XACT_WR13 0x1874
+#define EARCRX_CMDC_XACT_WR14 0x1878
+#define EARCRX_CMDC_XACT_WR15 0x187c
+#define EARCRX_CMDC_XACT_WR16 0x1880
+#define EARCRX_CMDC_XACT_WR17 0x1884
+#define EARCRX_CMDC_XACT_WR18 0x1888
+#define EARCRX_CMDC_XACT_WR19 0x188c
+#define EARCRX_CMDC_XACT_WR20 0x1890
+#define EARCRX_CMDC_XACT_WR21 0x1894
+#define EARCRX_CMDC_XACT_WR22 0x1898
+#define EARCRX_CMDC_XACT_WR23 0x189c
+#define EARCRX_CMDC_XACT_WR24 0x18a0
+#define EARCRX_CMDC_XACT_WR25 0x18a4
+#define EARCRX_CMDC_XACT_WR26 0x18a8
+#define EARCRX_CMDC_XACT_WR27 0x18ac
+#define EARCRX_CMDC_XACT_WR28 0x18b0
+#define EARCRX_CMDC_XACT_WR29 0x18b4
+#define EARCRX_CMDC_XACT_WR30 0x18b8
+#define EARCRX_CMDC_XACT_WR31 0x18bc
+#define EARCRX_CMDC_XACT_WR32 0x18c0
+#define EARCRX_CMDC_XACT_WR33 0x18c4
+#define EARCRX_CMDC_XACT_WR34 0x18c8
+#define EARCRX_CMDC_XACT_WR35 0x18cc
+#define EARCRX_CMDC_XACT_WR36 0x18d0
+#define EARCRX_CMDC_XACT_WR37 0x18d4
+#define EARCRX_CMDC_XACT_WR38 0x18d8
+#define EARCRX_CMDC_XACT_WR39 0x18dc
+#define EARCRX_CMDC_XACT_WR40 0x18e0
+#define EARCRX_CMDC_XACT_WR41 0x18e4
+#define EARCRX_CMDC_XACT_WR42 0x18e8
+#define EARCRX_CMDC_XACT_WR43 0x18ec
+#define EARCRX_CMDC_XACT_WR44 0x18f0
+#define EARCRX_CMDC_XACT_WR45 0x18f4
+#define EARCRX_CMDC_XACT_WR46 0x18f8
+#define EARCRX_CMDC_XACT_WR47 0x18fc
+#define EARCRX_CMDC_XACT_WR48 0x1900
+#define EARCRX_CMDC_XACT_WR49 0x1904
+#define EARCRX_CMDC_XACT_WR50 0x1908
+#define EARCRX_CMDC_XACT_WR51 0x190c
+#define EARCRX_CMDC_XACT_WR52 0x1910
+#define EARCRX_CMDC_XACT_WR53 0x1914
+#define EARCRX_CMDC_XACT_WR54 0x1918
+#define EARCRX_CMDC_XACT_WR55 0x191c
+#define EARCRX_CMDC_XACT_WR56 0x1920
+#define EARCRX_CMDC_XACT_WR57 0x1924
+#define EARCRX_CMDC_XACT_WR58 0x1928
+#define EARCRX_CMDC_XACT_WR59 0x192c
+#define EARCRX_CMDC_XACT_WR60 0x1930
+#define EARCRX_CMDC_XACT_WR61 0x1934
+#define EARCRX_CMDC_XACT_WR62 0x1938
+#define EARCRX_CMDC_XACT_WR63 0x193c
+#define EARCRX_CMDC_XACT_WR64 0x1940
+#define EARCRX_CMDC_XACT_RD0 0x1960
+#define EARCRX_CMDC_XACT_RD1 0x1964
+#define EARCRX_CMDC_XACT_RD2 0x1968
+#define EARCRX_CMDC_XACT_RD3 0x196c
+#define EARCRX_CMDC_XACT_RD4 0x1970
+#define EARCRX_CMDC_XACT_RD5 0x1974
+#define EARCRX_CMDC_XACT_RD6 0x1978
+#define EARCRX_CMDC_XACT_RD7 0x197c
+#define EARCRX_CMDC_XACT_RD8 0x1980
+#define EARCRX_CMDC_XACT_RD9 0x1984
+#define EARCRX_CMDC_XACT_RD10 0x1988
+#define EARCRX_CMDC_XACT_RD11 0x198c
+#define EARCRX_CMDC_XACT_RD12 0x1990
+#define EARCRX_CMDC_XACT_RD13 0x1994
+#define EARCRX_CMDC_XACT_RD14 0x1998
+#define EARCRX_CMDC_XACT_RD15 0x199c
+#define EARCRX_CMDC_XACT_RD16 0x19a0
+#define EARCRX_CMDC_XACT_RD17 0x19a4
+#define EARCRX_CMDC_XACT_RD18 0x19a8
+#define EARCRX_CMDC_XACT_RD19 0x19ac
+#define EARCRX_CMDC_XACT_RD20 0x19b0
+#define EARCRX_CMDC_XACT_RD21 0x19b4
+#define EARCRX_CMDC_XACT_RD22 0x19b8
+#define EARCRX_CMDC_XACT_RD23 0x19bc
+#define EARCRX_CMDC_XACT_RD24 0x19c0
+#define EARCRX_CMDC_XACT_RD25 0x19c4
+#define EARCRX_CMDC_XACT_RD26 0x19c8
+#define EARCRX_CMDC_XACT_RD27 0x19cc
+#define EARCRX_CMDC_XACT_RD28 0x19d0
+#define EARCRX_CMDC_XACT_RD29 0x19d4
+#define EARCRX_CMDC_XACT_RD30 0x19d8
+#define EARCRX_CMDC_XACT_RD31 0x19dc
+#define EARCRX_CMDC_XACT_RD32 0x19e0
+#define EARCRX_CMDC_XACT_RD33 0x19e4
+#define EARCRX_CMDC_XACT_RD34 0x19e8
+#define EARCRX_CMDC_XACT_RD35 0x19ec
+#define EARCRX_CMDC_XACT_RD36 0x19f0
+#define EARCRX_CMDC_XACT_RD37 0x19f4
+#define EARCRX_CMDC_XACT_RD38 0x19f8
+#define EARCRX_CMDC_XACT_RD39 0x19fc
+#define EARCRX_CMDC_XACT_RD40 0x1a00
+#define EARCRX_CMDC_XACT_RD41 0x1a04
+#define EARCRX_CMDC_XACT_RD42 0x1a08
+#define EARCRX_CMDC_XACT_RD43 0x1a0c
+#define EARCRX_CMDC_XACT_RD44 0x1a10
+#define EARCRX_CMDC_XACT_RD45 0x1a14
+#define EARCRX_CMDC_XACT_RD46 0x1a18
+#define EARCRX_CMDC_XACT_RD47 0x1a1c
+#define EARCRX_CMDC_XACT_RD48 0x1a20
+#define EARCRX_CMDC_XACT_RD49 0x1a24
+#define EARCRX_CMDC_XACT_RD50 0x1a28
+#define EARCRX_CMDC_XACT_RD51 0x1a2c
+#define EARCRX_CMDC_XACT_RD52 0x1a30
+#define EARCRX_CMDC_XACT_RD53 0x1a34
+#define EARCRX_CMDC_XACT_RD54 0x1a38
+#define EARCRX_CMDC_XACT_RD55 0x1a3c
+#define EARCRX_CMDC_XACT_RD56 0x1a40
+#define EARCRX_CMDC_XACT_RD57 0x1a44
+#define EARCRX_CMDC_XACT_RD58 0x1a48
+#define EARCRX_CMDC_XACT_RD59 0x1a4c
+#define EARCRX_CMDC_XACT_RD60 0x1a50
+#define EARCRX_CMDC_XACT_RD61 0x1a54
+#define EARCRX_CMDC_XACT_RD62 0x1a58
+#define EARCRX_CMDC_XACT_RD63 0x1a5c
+#define EARCRX_CMDC_XACT_RD64 0x1a60
+#define EARCRX_CMDC_SYNC_CONFIG 0x1b00
+/* eARC RX DMAC Registers */
+#define EARCRX_DMAC_PHY_CONTROL 0x1c00
+#define EARCRX_DMAC_CONFIG 0x1c08
+#define EARCRX_DMAC_CONTROL0 0x1c0c
+#define EARCRX_DMAC_AUDIO_EN BIT(1)
+#define EARCRX_DMAC_EN BIT(0)
+#define EARCRX_DMAC_CONTROL1 0x1c10
+#define EARCRX_DMAC_STATUS 0x1c14
+#define EARCRX_DMAC_CHSTATUS0 0x1c18
+#define EARCRX_DMAC_CHSTATUS1 0x1c1c
+#define EARCRX_DMAC_CHSTATUS2 0x1c20
+#define EARCRX_DMAC_CHSTATUS3 0x1c24
+#define EARCRX_DMAC_CHSTATUS4 0x1c28
+#define EARCRX_DMAC_CHSTATUS5 0x1c2c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8
+#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c
+#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40
+#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44
+#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48
+#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c
+#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50
+#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54
+#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58
+#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c
+#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60
+#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64
+#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68
+#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c
+#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70
+#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74
+#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78
+#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c
+#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80
+/* Main Unit Interrupt Registers */
+#define MAIN_INTVEC_INDEX 0x3000
+#define MAINUNIT_0_INT_STATUS 0x3010
+#define MAINUNIT_0_INT_MASK_N 0x3014
+#define MAINUNIT_0_INT_CLEAR 0x3018
+#define MAINUNIT_0_INT_FORCE 0x301c
+#define MAINUNIT_1_INT_STATUS 0x3020
+#define FLT_EXIT_TO_LTSL_IRQ BIT(22)
+#define FLT_EXIT_TO_LTS4_IRQ BIT(21)
+#define FLT_EXIT_TO_LTSP_IRQ BIT(20)
+#define SCDC_NACK_RCVD_IRQ BIT(12)
+#define SCDC_RR_REPLY_STOP_IRQ BIT(11)
+#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10)
+#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9)
+#define SCDC_UPD_FLAGS_RD_IRQ BIT(8)
+#define I2CM_NACK_RCVD_IRQ BIT(2)
+#define I2CM_READ_REQUEST_IRQ BIT(1)
+#define I2CM_OP_DONE_IRQ BIT(0)
+#define MAINUNIT_1_INT_MASK_N 0x3024
+#define I2CM_NACK_RCVD_MASK_N BIT(2)
+#define I2CM_READ_REQUEST_MASK_N BIT(1)
+#define I2CM_OP_DONE_MASK_N BIT(0)
+#define MAINUNIT_1_INT_CLEAR 0x3028
+#define I2CM_NACK_RCVD_CLEAR BIT(2)
+#define I2CM_READ_REQUEST_CLEAR BIT(1)
+#define I2CM_OP_DONE_CLEAR BIT(0)
+#define MAINUNIT_1_INT_FORCE 0x302c
+/* AVPUNIT Interrupt Registers */
+#define AVP_INTVEC_INDEX 0x3800
+#define AVP_0_INT_STATUS 0x3810
+#define AVP_0_INT_MASK_N 0x3814
+#define AVP_0_INT_CLEAR 0x3818
+#define AVP_0_INT_FORCE 0x381c
+#define AVP_1_INT_STATUS 0x3820
+#define AVP_1_INT_MASK_N 0x3824
+#define HDCP14_AUTH_CHG_MASK_N BIT(6)
+#define AVP_1_INT_CLEAR 0x3828
+#define AVP_1_INT_FORCE 0x382c
+#define AVP_2_INT_STATUS 0x3830
+#define AVP_2_INT_MASK_N 0x3834
+#define AVP_2_INT_CLEAR 0x3838
+#define AVP_2_INT_FORCE 0x383c
+#define AVP_3_INT_STATUS 0x3840
+#define AVP_3_INT_MASK_N 0x3844
+#define AVP_3_INT_CLEAR 0x3848
+#define AVP_3_INT_FORCE 0x384c
+#define AVP_4_INT_STATUS 0x3850
+#define AVP_4_INT_MASK_N 0x3854
+#define AVP_4_INT_CLEAR 0x3858
+#define AVP_4_INT_FORCE 0x385c
+#define AVP_5_INT_STATUS 0x3860
+#define AVP_5_INT_MASK_N 0x3864
+#define AVP_5_INT_CLEAR 0x3868
+#define AVP_5_INT_FORCE 0x386c
+#define AVP_6_INT_STATUS 0x3870
+#define AVP_6_INT_MASK_N 0x3874
+#define AVP_6_INT_CLEAR 0x3878
+#define AVP_6_INT_FORCE 0x387c
+/* CEC Interrupt Registers */
+#define CEC_INT_STATUS 0x4000
+#define CEC_INT_MASK_N 0x4004
+#define CEC_INT_CLEAR 0x4008
+#define CEC_INT_FORCE 0x400c
+/* eARC RX Interrupt Registers */
+#define EARCRX_INTVEC_INDEX 0x4800
+#define EARCRX_0_INT_STATUS 0x4810
+#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9)
+#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8)
+#define EARCRX_0_INT_MASK_N 0x4814
+#define EARCRX_0_INT_CLEAR 0x4818
+#define EARCRX_0_INT_FORCE 0x481c
+#define EARCRX_1_INT_STATUS 0x4820
+#define EARCRX_1_INT_MASK_N 0x4824
+#define EARCRX_1_INT_CLEAR 0x4828
+#define EARCRX_1_INT_FORCE 0x482c
+
+#endif /* __DW_HDMI_QP_H__ */
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 52d91a0df85e..0d6fd9578a5a 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 af43a0414b78..8ebdec7254f2 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/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 341550199111..52f457b16900 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/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 6a46baa0737c..9042039f2222 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);
--
2.42.1
From 140267c1c11d90f4889e57ae6d58280b261081c0 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Wed, 1 Nov 2023 19:02:20 +0200
Subject: [PATCH 4/9] drm/rockchip: vop2: Add basic support for rk3588
WARNING: This is work in progress and most features are missing (e.g.
8K support, overlay, etc).
Currently only tested on Rock5B where it enables basic HDMI output on
port 1 (located next to USB-C).
The following command can be used to display a basic test pattern:
modetest -s <connector_id>@<crtc_id>:<mode>
e.g.:
modetest -M rockchip -s 77@73:1920x1080-60
setting mode 1920x1080-60.00Hz on connectors 77, crtc 73
So far, it works for the following modes:
1920x1080-60.00Hz, 1920x1080-50.00Hz, 1920x1080-30.00Hz,
1920x1080-25.00Hz, 1920x1080-24.00Hz, 1280x1024-60.02Hz,
1280x960-60.00Hz, 1280x720-60.00Hz, 1280x720-50.00Hz,
800x600-75.00Hz, 720x576-50.00Hz
For some reason (clocking?!) it's not possible to use refresh rates like
59.94, 29.97, 23.98, etc.
Unexpectedly, it also doesn't work for 2560x1440-75.00Hz,
2048x1152-60.00Hz and 1024x768-60.00Hz.
I don't have a display supporting higher resolution, hence my tests
were limited to the above modes.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/drm_displayid.c | 4 +
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 627 +++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 431 ++++++++++++-
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 23 +-
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 414 +++++++++++-
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 70 ++-
drivers/gpu/drm/rockchip/rockchip_vop2_reg.c | 231 ++++++-
include/dt-bindings/soc/rockchip,vop2.h | 4 +
include/uapi/drm/rockchip_drm.h | 134 ++++
9 files changed, 1916 insertions(+), 22 deletions(-)
create mode 100644 include/uapi/drm/rockchip_drm.h
diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c
index 9edc111be7ee..a578f918f46d 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/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index ab55d7132550..44d49c86b7bd 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 aeb03a57240f..581496f043b1 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 4b2daefeb8c1..6ebea5c36560 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 6862fb146ace..509caa514dca 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <linux/swab.h>
#include <drm/drm.h>
@@ -159,6 +160,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 +194,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,6 +214,9 @@ struct vop2 {
unsigned int enable_count;
struct clk *hclk;
struct clk *aclk;
+ struct clk *pclk;
+ struct reset_control *ahb_rst;
+ struct reset_control *axi_rst;
/* optional internal rgb encoder */
struct rockchip_rgb *rgb;
@@ -217,6 +225,13 @@ struct vop2 {
struct vop2_win win[];
};
+#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 +284,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;
+
+ 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,
- BIT(vp->id) | RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN);
+ regmap_set_bits(vop2->map, RK3568_REG_CFG_DONE, bits);
}
static void vop2_win_disable(struct vop2_win *win)
@@ -846,16 +868,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 +925,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,6 +955,7 @@ 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);
}
@@ -1004,6 +1050,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 +1074,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 +1183,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 +1349,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);
@@ -1462,7 +1544,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 +1559,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 +1570,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,6 +1628,243 @@ 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;
+}
+
+/*
+ * 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)) {
+ /*
+ * 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;
@@ -1564,7 +1894,7 @@ 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;
@@ -1597,10 +1927,22 @@ 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);
+ // rk3568_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags, invpolflags);
+ vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
}
if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
@@ -1668,6 +2010,15 @@ 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);
}
@@ -2299,7 +2650,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 +2658,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);
@@ -2726,7 +3090,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 +3109,25 @@ 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);
+ }
+
+ // [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");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
index 56fd31e05238..cae71df81a6d 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 22288ad7f326..2bc1baf5cbd5 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/include/dt-bindings/soc/rockchip,vop2.h b/include/dt-bindings/soc/rockchip,vop2.h
index 6e66a802b96a..668f199df9f0 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/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h
new file mode 100644
index 000000000000..246192fa2922
--- /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 */
--
2.42.1
From 94b6bcb0b87d896e764615f9c1601ac270300ce8 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Wed, 1 Nov 2023 16:42:58 +0200
Subject: [PATCH 5/9] arm64: dts: rockchip: Add DT nodes for HDMI support in
RK3588
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
.../boot/dts/rockchip/rk3588-rock-5b.dts | 20 ++
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 220 ++++++++++++++++++
2 files changed, 240 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
index 9ee415e6f498..39df9bfbbc5b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
@@ -920,3 +920,23 @@ &usb_host1_xhci {
&usb_host2_xhci {
status = "okay";
};
+
+&hdmi0 {
+ status = "okay";
+};
+
+&hdmi0_in_vp0 {
+ status = "okay";
+};
+
+&hdptxphy_hdmi0 {
+ status = "okay";
+};
+
+&vop_mmu {
+ status = "okay";
+};
+
+&vop {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
index e7ebeda1c799..4481a2e578f8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
@@ -8,6 +8,7 @@
#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>
@@ -580,6 +581,11 @@ 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 */
@@ -1019,12 +1025,23 @@ 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>;
@@ -1055,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>;
@@ -1478,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>;
@@ -1529,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>;
@@ -2760,6 +2959,27 @@ 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>;
--
2.42.1
From 5760547fa8739f1185b4b2523fb801fd678cfbde Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Fri, 3 Nov 2023 19:58:02 +0200
Subject: [PATCH 6/9] drm/rockchip: vop2: Fix display modes handling on rk3588
Improve HDMI0 clocking in order to properly support the failing display
modes mentioned in the fixed commit.
Fixes: cd2e7fe23a00 ("drm/rockchip: vop2: Add basic support for rk3588")
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 670 +++++++++++++++++--
1 file changed, 612 insertions(+), 58 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 509caa514dca..a073d1d5c453 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>
@@ -215,16 +217,35 @@ struct vop2 {
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)
@@ -960,59 +981,6 @@ static void vop2_disable(struct vop2 *vop2)
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)
{
@@ -1458,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;
}
@@ -1644,6 +1633,31 @@ static unsigned long vop2_calc_dclk(unsigned long child_clk, unsigned long max_d
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
@@ -1668,6 +1682,71 @@ static unsigned long vop2_calc_cru_cfg(struct vop2_video_port *vp, int id,
int K = 1;
if (vop2_output_if_is_hdmi(id)) {
+ if (vop2->data->soc_id == 3588 && id == ROCKCHIP_VOP2_EP_HDMI0) {
+ 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
@@ -1870,6 +1949,22 @@ static int us_to_vertical_line(struct drm_display_mode *mode, int us)
return us * mode->clock / mode->htotal / 1000;
}
+// [CC:] rework virtual clock
+static struct vop2_clk *vop2_clk_get(struct vop2 *vop2, const char *name)
+{
+ struct vop2_clk *clk, *n;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry_safe(clk, n, &vop2->clk_list_head, list) {
+ if (!strcmp(clk_hw_get_name(&clk->hw), name))
+ return clk;
+ }
+
+ return NULL;
+}
+
static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
@@ -1897,6 +1992,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
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",
@@ -1940,8 +2037,6 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
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, invpolflags);
vop2_set_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
}
@@ -1993,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);
@@ -2022,6 +2148,59 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
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)
{
@@ -2455,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,
@@ -3038,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);
@@ -3115,6 +3660,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
return PTR_ERR(vop2->pclk);
}
+ vop2->hdmi0_phy_pll = devm_clk_get_optional(vop2->drm->dev, "hdmi0_phy_pll");
+ if (IS_ERR(vop2->hdmi0_phy_pll)) {
+ DRM_DEV_ERROR(vop2->dev, "failed to get hdmi0_phy_pll source\n");
+ return PTR_ERR(vop2->hdmi0_phy_pll);
+ }
+
// [CC:] drop ahb_rst & axi_rst
vop2->ahb_rst = devm_reset_control_get_optional(vop2->dev, "ahb");
if (IS_ERR(vop2->ahb_rst)) {
@@ -3144,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,
--
2.42.1
From 3b1edb31f3ac2f55d62968f2fd7d9b5d430cd3ec Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Fri, 3 Nov 2023 20:05:05 +0200
Subject: [PATCH 7/9] arm64: dts: rockchip: Enable HDMI0 PHY PLL on RK3588
This is necessary for proper handling of various display modes.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
index 39df9bfbbc5b..dacf6a4d8625 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts
@@ -193,6 +193,11 @@ &cpu_l3 {
mem-supply = <&vdd_cpu_lit_mem_s0>;
};
+&display_subsystem {
+ clocks = <&hdptxphy_hdmi_clk0>;
+ clock-names = "hdmi0_phy_pll";
+};
+
&i2c0 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0m2_xfer>;
@@ -933,6 +938,10 @@ &hdptxphy_hdmi0 {
status = "okay";
};
+&hdptxphy_hdmi_clk0 {
+ status = "okay";
+};
+
&vop_mmu {
status = "okay";
};
--
2.42.1
From 3e33bc702048b524d4faaa7d8f151bfe4a48fd2b Mon Sep 17 00:00:00 2001
From: Sebastian Reichel <sebastian.reichel@collabora.com>
Date: Mon, 6 Nov 2023 17:34:54 +0100
Subject: [PATCH 8/9] arm64: dts: rockchip: add HDMI support to rk3588-evb1
Enable all bits required for HDMI output on Rockchip RK3588 EVB1.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
.../boot/dts/rockchip/rk3588-evb1-v10.dts | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
index c0ea050371f9..2ef5c98c4168 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588-evb1-v10.dts
@@ -300,6 +300,11 @@ &cpu_l3 {
mem-supply = <&vdd_cpu_lit_mem_s0>;
};
+&display_subsystem {
+ clocks = <&hdptxphy_hdmi_clk0>;
+ clock-names = "hdmi0_phy_pll";
+};
+
&gmac0 {
clock_in_out = "output";
phy-handle = <&rgmii_phy>;
@@ -1263,3 +1268,27 @@ dwc3_0_role_switch: endpoint@0 {
&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";
+};
--
2.42.1
From f41b3e9a9d7f22bef0735b4fe0007321ce6b6d6b Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Fri, 10 Nov 2023 00:40:54 +0200
Subject: [PATCH 9/9] arm64: defconfig: Enable Rockchip Samsung HDMI/DP Combo
PHY
Enable support for the Rockchip HDMI/DP Combo PHY based on a Samsung IP
block. This is used by the RK3588 SoC family.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index b1109c48fe27..906f2f21ac05 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1451,6 +1451,7 @@ CONFIG_PHY_ROCKCHIP_INNO_USB2=y
CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=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
--
2.42.1