armbian-build/patch/kernel/rockchip-rk3588-edge/0028-Add-HDMI-QP-Support-for-RK3588.patch
2024-01-24 17:43:27 +01:00

13702 lines
417 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Algea Cao <algea.cao@rock-chips.com>
Date: Tue, 12 Jul 2022 09:18:25 +0200
Subject: phy/rockchip: Add the hdptx-hdmi driver
This is a phy driver for rk3588 hdmi.
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
---
drivers/phy/rockchip/Kconfig | 7 +
drivers/phy/rockchip/Makefile | 1 +
drivers/phy/rockchip/phy-rockchip-samsung-hdptx-hdmi.c | 2347 ++++++++++
3 files changed, 2355 insertions(+)
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");
--
Armbian
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezillon@collabora.com>
Date: Tue, 12 Jul 2022 09:11:57 +0200
Subject: drm/rockchip: dw hdmi qp support for rk3588
Cherry picked from panthor-v3-rk3588-evb1 tree and made some
adjust to fit the upstream drm driver.
Now we can get a 4KP60 output on rk3588 evb1
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
---
drivers/gpu/drm/bridge/synopsys/Kconfig | 13 +-
drivers/gpu/drm/bridge/synopsys/Makefile | 7 +-
drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 10 +-
drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 4 +-
drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 9 +-
drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h | 54 +
drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c | 11 +-
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h | 29 +
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c | 336 +
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h | 25 +
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c | 251 +
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 2985 +++++++++
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 831 +++
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 2009 ++++--
drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 71 +-
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 3108 +++++++++-
include/drm/bridge/dw_hdmi.h | 129 +-
17 files changed, 9149 insertions(+), 733 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig
index 15fc182d05ef..18ac9677dc93 100644
--- a/drivers/gpu/drm/bridge/synopsys/Kconfig
+++ b/drivers/gpu/drm/bridge/synopsys/Kconfig
@@ -1,9 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_DW_HDMI
tristate
- select DRM_DISPLAY_HDMI_HELPER
- select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
+ select DRM_DISPLAY_HDMI_HELPER
select REGMAP_MMIO
select CEC_CORE if CEC_NOTIFIER
@@ -27,16 +26,6 @@ config DRM_DW_HDMI_I2S_AUDIO
Support the I2S Audio interface which is part of the Synopsys
Designware HDMI block.
-config DRM_DW_HDMI_GP_AUDIO
- tristate "Synopsys Designware GP Audio interface"
- depends on DRM_DW_HDMI && SND
- select SND_PCM
- select SND_PCM_ELD
- select SND_PCM_IEC958
- help
- Support the GP Audio interface which is part of the Synopsys
- Designware HDMI block.
-
config DRM_DW_HDMI_CEC
tristate "Synopsis Designware CEC interface"
depends on DRM_DW_HDMI
diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile
index ce715562e9e5..da937282da87 100644
--- a/drivers/gpu/drm/bridge/synopsys/Makefile
+++ b/drivers/gpu/drm/bridge/synopsys/Makefile
@@ -1,8 +1,7 @@
# 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
-obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
+obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o dw-hdmi-qp-i2s-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o dw-hdmi-qp-cec.o
obj-$(CONFIG_DRM_DW_MIPI_DSI) += dw-mipi-dsi.o
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
index 67b8d17a722a..bc54b4282cb6 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
@@ -320,17 +320,13 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dw_hdmi *dw = substream->private_data;
void __iomem *base = dw->data.base;
- u8 *eld;
int ret;
runtime->hw = dw_hdmi_hw;
- eld = dw->data.get_eld(dw->data.hdmi);
- if (eld) {
- ret = snd_pcm_hw_constraint_eld(runtime, eld);
- if (ret < 0)
- return ret;
- }
+ ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
+ if (ret < 0)
+ return ret;
ret = snd_pcm_limit_hw_rates(runtime);
if (ret < 0)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
index f72d27208ebe..cb07dc0da5a7 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
@@ -9,15 +9,15 @@ struct dw_hdmi_audio_data {
void __iomem *base;
int irq;
struct dw_hdmi *hdmi;
- u8 *(*get_eld)(struct dw_hdmi *hdmi);
+ u8 *eld;
};
struct dw_hdmi_i2s_audio_data {
struct dw_hdmi *hdmi;
+ u8 *eld;
void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
u8 (*read)(struct dw_hdmi *hdmi, int offset);
- u8 *(*get_eld)(struct dw_hdmi *hdmi);
};
#endif
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
index 673661160e54..b8675ba16d61 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <drm/drm_edid.h>
+#include <drm/bridge/dw_hdmi.h>
#include <media/cec.h>
#include <media/cec-notifier.h>
@@ -270,12 +271,16 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev)
if (IS_ERR(cec->adap))
return PTR_ERR(cec->adap);
+ dw_hdmi_set_cec_adap(cec->hdmi, cec->adap);
+
/* override the module pointer */
cec->adap->owner = THIS_MODULE;
- ret = devm_add_action_or_reset(&pdev->dev, dw_hdmi_cec_del, cec);
- if (ret)
+ ret = devm_add_action(&pdev->dev, dw_hdmi_cec_del, cec);
+ if (ret) {
+ cec_delete_adapter(cec->adap);
return ret;
+ }
ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
dw_hdmi_cec_hardirq,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h
new file mode 100644
index 000000000000..d138f91f3422
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-hdcp.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author Huicong Xu <xhc@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DW_HDMI_HDCP_H
+#define DW_HDMI_HDCP_H
+
+#include <linux/miscdevice.h>
+
+#define DW_HDCP_DRIVER_NAME "dw-hdmi-hdcp"
+#define HDCP_PRIVATE_KEY_SIZE 280
+#define HDCP_KEY_SHA_SIZE 20
+
+struct hdcp_keys {
+ u8 KSV[8];
+ u8 devicekey[HDCP_PRIVATE_KEY_SIZE];
+ u8 sha1[HDCP_KEY_SHA_SIZE];
+};
+
+struct dw_hdcp {
+ bool enable;
+ int retry_times;
+ int remaining_times;
+ char *seeds;
+ int invalidkey;
+ char *invalidkeys;
+ int hdcp2_enable;
+ int status;
+ u32 reg_io_width;
+
+ struct miscdevice mdev;
+ struct hdcp_keys *keys;
+ struct device *dev;
+ struct dw_hdmi *hdmi;
+ void __iomem *regs;
+
+ void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
+ u8 (*read)(struct dw_hdmi *hdmi, int offset);
+ int (*hdcp_start)(struct dw_hdcp *hdcp);
+ int (*hdcp_stop)(struct dw_hdcp *hdcp);
+ void (*hdcp_isr)(struct dw_hdcp *hdcp, int hdcp_int);
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
index 26c187d20d97..e92dce88c2c3 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
@@ -135,15 +135,8 @@ static int dw_hdmi_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
size_t len)
{
struct dw_hdmi_i2s_audio_data *audio = data;
- u8 *eld;
-
- eld = audio->get_eld(audio->hdmi);
- if (eld)
- memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
- else
- /* Pass en empty ELD if connector not available */
- memset(buf, 0, len);
+ memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len));
return 0;
}
@@ -177,7 +170,7 @@ static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data,
return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev);
}
-static const struct hdmi_codec_ops dw_hdmi_i2s_ops = {
+static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
.hw_params = dw_hdmi_i2s_hw_params,
.audio_startup = dw_hdmi_i2s_audio_startup,
.audio_shutdown = dw_hdmi_i2s_audio_shutdown,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h
new file mode 100644
index 000000000000..93f1a42954e7
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-audio.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ * Author: Sugar Zhang <sugar.zhang@rock-chips.com>
+ */
+
+#ifndef DW_HDMI_QP_AUDIO_H
+#define DW_HDMI_QP_AUDIO_H
+
+struct dw_hdmi_qp;
+
+struct dw_hdmi_qp_audio_data {
+ phys_addr_t phys;
+ void __iomem *base;
+ int irq;
+ struct dw_hdmi_qp *hdmi;
+ u8 *eld;
+};
+
+struct dw_hdmi_qp_i2s_audio_data {
+ struct dw_hdmi_qp *hdmi;
+ u8 *eld;
+
+ void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset);
+ u32 (*read)(struct dw_hdmi_qp *hdmi, int offset);
+ void (*mod)(struct dw_hdmi_qp *hdmi, u32 val, u32 mask, u32 reg);
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c
new file mode 100644
index 000000000000..05bcfcaec665
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ * Algea Cao <algea.cao@rock-chips.com>
+ */
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <drm/drm_edid.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+#include "dw-hdmi-qp-cec.h"
+
+enum {
+ CEC_TX_CONTROL = 0x1000,
+ CEC_CTRL_CLEAR = BIT(0),
+ CEC_CTRL_START = BIT(0),
+
+ CEC_STAT_DONE = BIT(0),
+ CEC_STAT_NACK = BIT(1),
+ CEC_STAT_ARBLOST = BIT(2),
+ CEC_STAT_LINE_ERR = BIT(3),
+ CEC_STAT_RETRANS_FAIL = BIT(4),
+ CEC_STAT_DISCARD = BIT(5),
+ CEC_STAT_TX_BUSY = BIT(8),
+ CEC_STAT_RX_BUSY = BIT(9),
+ CEC_STAT_DRIVE_ERR = BIT(10),
+ CEC_STAT_EOM = BIT(11),
+ CEC_STAT_NOTIFY_ERR = BIT(12),
+
+ CEC_CONFIG = 0x1008,
+ CEC_ADDR = 0x100c,
+ CEC_TX_CNT = 0x1020,
+ CEC_RX_CNT = 0x1040,
+ CEC_TX_DATA3_0 = 0x1024,
+ CEC_RX_DATA3_0 = 0x1044,
+ CEC_LOCK_CONTROL = 0x1054,
+
+ CEC_INT_STATUS = 0x4000,
+ CEC_INT_MASK_N = 0x4004,
+ CEC_INT_CLEAR = 0x4008,
+};
+
+struct dw_hdmi_qp_cec {
+ struct dw_hdmi_qp *hdmi;
+ const struct dw_hdmi_qp_cec_ops *ops;
+ u32 addresses;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+ unsigned int tx_status;
+ bool tx_done;
+ bool rx_done;
+ struct cec_notifier *notify;
+ int irq;
+};
+
+static void dw_hdmi_qp_write(struct dw_hdmi_qp_cec *cec, u32 val, int offset)
+{
+ cec->ops->write(cec->hdmi, val, offset);
+}
+
+static u32 dw_hdmi_qp_read(struct dw_hdmi_qp_cec *cec, int offset)
+{
+ return cec->ops->read(cec->hdmi, offset);
+}
+
+static int dw_hdmi_qp_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ cec->addresses = 0;
+ else
+ cec->addresses |= BIT(logical_addr) | BIT(15);
+
+ dw_hdmi_qp_write(cec, cec->addresses, CEC_ADDR);
+
+ return 0;
+}
+
+static int dw_hdmi_qp_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap);
+ unsigned int i;
+ u32 val;
+
+ for (i = 0; i < msg->len; i++) {
+ if (!(i % 4))
+ val = msg->msg[i];
+ if ((i % 4) == 1)
+ val |= msg->msg[i] << 8;
+ if ((i % 4) == 2)
+ val |= msg->msg[i] << 16;
+ if ((i % 4) == 3)
+ val |= msg->msg[i] << 24;
+
+ if (i == (msg->len - 1) || (i % 4) == 3)
+ dw_hdmi_qp_write(cec, val, CEC_TX_DATA3_0 + (i / 4) * 4);
+ }
+
+ dw_hdmi_qp_write(cec, msg->len - 1, CEC_TX_CNT);
+ dw_hdmi_qp_write(cec, CEC_CTRL_START, CEC_TX_CONTROL);
+
+ return 0;
+}
+
+static irqreturn_t dw_hdmi_qp_cec_hardirq(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap);
+ u32 stat = dw_hdmi_qp_read(cec, CEC_INT_STATUS);
+ irqreturn_t ret = IRQ_HANDLED;
+
+ if (stat == 0)
+ return IRQ_NONE;
+
+ dw_hdmi_qp_write(cec, stat, CEC_INT_CLEAR);
+
+ if (stat & CEC_STAT_LINE_ERR) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_DONE) {
+ cec->tx_status = CEC_TX_STATUS_OK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_NACK) {
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (stat & CEC_STAT_EOM) {
+ unsigned int len, i, val;
+
+ val = dw_hdmi_qp_read(cec, CEC_RX_CNT);
+ len = (val & 0xf) + 1;
+
+ if (len > sizeof(cec->rx_msg.msg))
+ len = sizeof(cec->rx_msg.msg);
+
+ for (i = 0; i < 4; i++) {
+ val = dw_hdmi_qp_read(cec, CEC_RX_DATA3_0 + i);
+ cec->rx_msg.msg[i * 4] = val & 0xff;
+ cec->rx_msg.msg[i * 4 + 1] = (val >> 8) & 0xff;
+ cec->rx_msg.msg[i * 4 + 2] = (val >> 16) & 0xff;
+ cec->rx_msg.msg[i * 4 + 3] = (val >> 24) & 0xff;
+ }
+
+ dw_hdmi_qp_write(cec, 1, CEC_LOCK_CONTROL);
+
+ cec->rx_msg.len = len;
+ cec->rx_done = true;
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t dw_hdmi_qp_cec_thread(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap);
+
+ if (cec->tx_done) {
+ cec->tx_done = false;
+ cec_transmit_attempt_done(adap, cec->tx_status);
+ }
+ if (cec->rx_done) {
+ cec->rx_done = false;
+ cec_received_msg(adap, &cec->rx_msg);
+ }
+ return IRQ_HANDLED;
+}
+
+static int dw_hdmi_qp_cec_enable(struct cec_adapter *adap, bool enable)
+{
+ struct dw_hdmi_qp_cec *cec = cec_get_drvdata(adap);
+
+ if (!enable) {
+ dw_hdmi_qp_write(cec, 0, CEC_INT_MASK_N);
+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR);
+ cec->ops->disable(cec->hdmi);
+ } else {
+ unsigned int irqs;
+
+ cec->ops->enable(cec->hdmi);
+
+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR);
+ dw_hdmi_qp_write(cec, 1, CEC_LOCK_CONTROL);
+
+ dw_hdmi_qp_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+
+ irqs = CEC_STAT_LINE_ERR | CEC_STAT_NACK | CEC_STAT_EOM |
+ CEC_STAT_DONE;
+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR);
+ dw_hdmi_qp_write(cec, irqs, CEC_INT_MASK_N);
+ }
+ return 0;
+}
+
+static const struct cec_adap_ops dw_hdmi_qp_cec_ops = {
+ .adap_enable = dw_hdmi_qp_cec_enable,
+ .adap_log_addr = dw_hdmi_qp_cec_log_addr,
+ .adap_transmit = dw_hdmi_qp_cec_transmit,
+};
+
+static void dw_hdmi_qp_cec_del(void *data)
+{
+ struct dw_hdmi_qp_cec *cec = data;
+
+ cec_delete_adapter(cec->adap);
+}
+
+static int dw_hdmi_qp_cec_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi_qp_cec_data *data = dev_get_platdata(&pdev->dev);
+ struct dw_hdmi_qp_cec *cec;
+ int ret;
+
+ if (!data) {
+ dev_err(&pdev->dev, "can't get data\n");
+ return -ENXIO;
+ }
+
+ /*
+ * Our device is just a convenience - we want to link to the real
+ * hardware device here, so that userspace can see the association
+ * between the HDMI hardware and its associated CEC chardev.
+ */
+ cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return -ENOMEM;
+
+ cec->ops = data->ops;
+ cec->hdmi = data->hdmi;
+ cec->irq = data->irq;
+
+ platform_set_drvdata(pdev, cec);
+
+ dw_hdmi_qp_write(cec, 0, CEC_TX_CNT);
+ dw_hdmi_qp_write(cec, ~0, CEC_INT_CLEAR);
+ dw_hdmi_qp_write(cec, 0, CEC_INT_MASK_N);
+
+ cec->adap = cec_allocate_adapter(&dw_hdmi_qp_cec_ops, cec, "dw_hdmi_qp",
+ CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+ CEC_CAP_RC | CEC_CAP_PASSTHROUGH,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(cec->adap)) {
+ dev_err(&pdev->dev, "cec allocate adapter failed\n");
+ return PTR_ERR(cec->adap);
+ }
+
+ dw_hdmi_qp_set_cec_adap(cec->hdmi, cec->adap);
+
+ /* override the module pointer */
+ cec->adap->owner = THIS_MODULE;
+
+ ret = devm_add_action(&pdev->dev, dw_hdmi_qp_cec_del, cec);
+ if (ret) {
+ dev_err(&pdev->dev, "cec add action failed\n");
+ cec_delete_adapter(cec->adap);
+ return ret;
+ }
+
+ if (cec->irq < 0) {
+ ret = cec->irq;
+ dev_err(&pdev->dev, "cec get irq failed\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, cec->irq,
+ dw_hdmi_qp_cec_hardirq,
+ dw_hdmi_qp_cec_thread, IRQF_SHARED,
+ "dw-hdmi-qp-cec", cec->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cec request irq thread failed\n");
+ return ret;
+ }
+
+ cec->notify = cec_notifier_cec_adap_register(pdev->dev.parent,
+ NULL, cec->adap);
+ if (!cec->notify) {
+ dev_err(&pdev->dev, "cec notifier adap register failed\n");
+ return -ENOMEM;
+ }
+
+ ret = cec_register_adapter(cec->adap, pdev->dev.parent);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cec adap register failed\n");
+ cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
+ return ret;
+ }
+
+ /*
+ * CEC documentation says we must not call cec_delete_adapter
+ * after a successful call to cec_register_adapter().
+ */
+ devm_remove_action(&pdev->dev, dw_hdmi_qp_cec_del, cec);
+
+ return 0;
+}
+
+static int dw_hdmi_qp_cec_remove(struct platform_device *pdev)
+{
+ struct dw_hdmi_qp_cec *cec = platform_get_drvdata(pdev);
+
+ cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
+ cec_unregister_adapter(cec->adap);
+
+ return 0;
+}
+
+static struct platform_driver dw_hdmi_qp_cec_driver = {
+ .probe = dw_hdmi_qp_cec_probe,
+ .remove = dw_hdmi_qp_cec_remove,
+ .driver = {
+ .name = "dw-hdmi-qp-cec",
+ },
+};
+module_platform_driver(dw_hdmi_qp_cec_driver);
+
+MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
+MODULE_DESCRIPTION("Synopsys Designware HDMI QP CEC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-qp-cec");
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h
new file mode 100644
index 000000000000..c0977c612e34
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-cec.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ * Algea Cao <algea.cao@rock-chips.com>
+ */
+#ifndef DW_HDMI_QP_CEC_H
+#define DW_HDMI_QP_CEC_H
+
+struct dw_hdmi_qp;
+
+struct dw_hdmi_qp_cec_ops {
+ void (*enable)(struct dw_hdmi_qp *hdmi);
+ void (*disable)(struct dw_hdmi_qp *hdmi);
+ void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset);
+ u32 (*read)(struct dw_hdmi_qp *hdmi, int offset);
+};
+
+struct dw_hdmi_qp_cec_data {
+ struct dw_hdmi_qp *hdmi;
+ const struct dw_hdmi_qp_cec_ops *ops;
+ int irq;
+};
+
+#endif
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c
new file mode 100644
index 000000000000..40725f237cd6
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dw-hdmi-qp-i2s-audio.c
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ * Author: Sugar Zhang <sugar.zhang@rock-chips.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_crtc.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "dw-hdmi-qp.h"
+#include "dw-hdmi-qp-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-qp-i2s-audio"
+
+static inline void hdmi_write(struct dw_hdmi_qp_i2s_audio_data *audio,
+ u32 val, int offset)
+{
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+
+ audio->write(hdmi, val, offset);
+}
+
+static inline u32 hdmi_read(struct dw_hdmi_qp_i2s_audio_data *audio, int offset)
+{
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+
+ return audio->read(hdmi, offset);
+}
+
+static inline void hdmi_mod(struct dw_hdmi_qp_i2s_audio_data *audio,
+ u32 data, u32 mask, u32 reg)
+{
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+
+ return audio->mod(hdmi, data, mask, reg);
+}
+
+static inline bool is_dw_hdmi_qp_clk_off(struct dw_hdmi_qp_i2s_audio_data *audio)
+{
+ u32 sta = hdmi_read(audio, CMU_STATUS);
+
+ return (sta & (AUDCLK_OFF | LINKQPCLK_OFF | VIDQPCLK_OFF));
+}
+
+static int dw_hdmi_qp_i2s_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ struct dw_hdmi_qp_i2s_audio_data *audio = data;
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+ u32 conf0 = 0;
+ bool ref2stream = false;
+
+ if (is_dw_hdmi_qp_clk_off(audio))
+ return 0;
+
+ if (fmt->bit_clk_provider | fmt->frame_clk_provider) {
+ dev_err(dev, "unsupported clock settings\n");
+ return -EINVAL;
+ }
+
+ /* Reset the audio data path of the AVP */
+ hdmi_write(audio, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST);
+
+ /* Clear the audio FIFO */
+ hdmi_write(audio, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0);
+
+ /* Select I2S interface as the audio source */
+ hdmi_mod(audio, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0);
+
+ /* Enable the active i2s lanes */
+ switch (hparms->channels) {
+ case 7 ... 8:
+ conf0 |= I2S_LINES_EN(3);
+ fallthrough;
+ case 5 ... 6:
+ conf0 |= I2S_LINES_EN(2);
+ fallthrough;
+ case 3 ... 4:
+ conf0 |= I2S_LINES_EN(1);
+ fallthrough;
+ default:
+ conf0 |= I2S_LINES_EN(0);
+ break;
+ }
+
+ hdmi_mod(audio, conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0);
+
+ /*
+ * Enable bpcuv generated internally for L-PCM, or received
+ * from stream for NLPCM/HBR.
+ */
+ switch (fmt->bit_fmt) {
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP;
+ conf0 |= I2S_BPCUV_RCV_EN;
+ ref2stream = true;
+ break;
+ default:
+ conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS;
+ ref2stream = false;
+ break;
+ }
+
+ hdmi_mod(audio, conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK,
+ AUDIO_INTERFACE_CONFIG0);
+
+ /* Enable audio FIFO auto clear when overflow */
+ hdmi_mod(audio, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK,
+ AUDIO_INTERFACE_CONFIG0);
+
+ dw_hdmi_qp_set_sample_rate(hdmi, hparms->sample_rate);
+ dw_hdmi_qp_set_channel_status(hdmi, hparms->iec.status, ref2stream);
+ dw_hdmi_qp_set_channel_count(hdmi, hparms->channels);
+ dw_hdmi_qp_set_channel_allocation(hdmi, hparms->cea.channel_allocation);
+ dw_hdmi_qp_set_audio_infoframe(hdmi, hparms);
+
+ return 0;
+}
+
+static int dw_hdmi_qp_i2s_audio_startup(struct device *dev, void *data)
+{
+ struct dw_hdmi_qp_i2s_audio_data *audio = data;
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+
+ if (is_dw_hdmi_qp_clk_off(audio))
+ return 0;
+
+ dw_hdmi_qp_audio_enable(hdmi);
+
+ return 0;
+}
+
+static void dw_hdmi_qp_i2s_audio_shutdown(struct device *dev, void *data)
+{
+ struct dw_hdmi_qp_i2s_audio_data *audio = data;
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+
+ if (is_dw_hdmi_qp_clk_off(audio))
+ return;
+
+ dw_hdmi_qp_audio_disable(hdmi);
+}
+
+static int dw_hdmi_qp_i2s_get_eld(struct device *dev, void *data, uint8_t *buf,
+ size_t len)
+{
+ struct dw_hdmi_qp_i2s_audio_data *audio = data;
+
+ memcpy(buf, audio->eld, min_t(size_t, MAX_ELD_BYTES, len));
+
+ return 0;
+}
+
+static int dw_hdmi_qp_i2s_get_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ struct of_endpoint of_ep;
+ int ret;
+
+ ret = of_graph_parse_endpoint(endpoint, &of_ep);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * HDMI sound should be located as reg = <2>
+ * Then, it is sound port 0
+ */
+ if (of_ep.port == 2)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int dw_hdmi_qp_i2s_hook_plugged_cb(struct device *dev, void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct dw_hdmi_qp_i2s_audio_data *audio = data;
+ struct dw_hdmi_qp *hdmi = audio->hdmi;
+
+ return dw_hdmi_qp_set_plugged_cb(hdmi, fn, codec_dev);
+}
+
+static struct hdmi_codec_ops dw_hdmi_qp_i2s_ops = {
+ .hw_params = dw_hdmi_qp_i2s_hw_params,
+ .audio_startup = dw_hdmi_qp_i2s_audio_startup,
+ .audio_shutdown = dw_hdmi_qp_i2s_audio_shutdown,
+ .get_eld = dw_hdmi_qp_i2s_get_eld,
+ .get_dai_id = dw_hdmi_qp_i2s_get_dai_id,
+ .hook_plugged_cb = dw_hdmi_qp_i2s_hook_plugged_cb,
+};
+
+static int snd_dw_hdmi_qp_probe(struct platform_device *pdev)
+{
+ struct dw_hdmi_qp_i2s_audio_data *audio = pdev->dev.platform_data;
+ struct platform_device_info pdevinfo;
+ struct hdmi_codec_pdata pdata;
+ struct platform_device *platform;
+
+ pdata.ops = &dw_hdmi_qp_i2s_ops;
+ pdata.i2s = 1;
+ pdata.max_i2s_channels = 8;
+ pdata.data = audio;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo.parent = pdev->dev.parent;
+ pdevinfo.id = PLATFORM_DEVID_AUTO;
+ pdevinfo.name = HDMI_CODEC_DRV_NAME;
+ pdevinfo.data = &pdata;
+ pdevinfo.size_data = sizeof(pdata);
+ pdevinfo.dma_mask = DMA_BIT_MASK(32);
+
+ platform = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(platform))
+ return PTR_ERR(platform);
+
+ dev_set_drvdata(&pdev->dev, platform);
+
+ return 0;
+}
+
+static int snd_dw_hdmi_qp_remove(struct platform_device *pdev)
+{
+ struct platform_device *platform = dev_get_drvdata(&pdev->dev);
+
+ platform_device_unregister(platform);
+
+ return 0;
+}
+
+static struct platform_driver snd_dw_hdmi_qp_driver = {
+ .probe = snd_dw_hdmi_qp_probe,
+ .remove = snd_dw_hdmi_qp_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+module_platform_driver(snd_dw_hdmi_qp_driver);
+
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI QP I2S ALSA SoC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
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..4857ba099878
--- /dev/null
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -0,0 +1,2984 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ * Algea Cao <algea.cao@rock-chips.com>
+ */
+#define DEBUG
+#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/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/display/drm_hdmi_helper.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include <uapi/linux/media-bus-format.h>
+#include <uapi/linux/videodev2.h>
+
+#include "dw-hdmi-qp-audio.h"
+#include "dw-hdmi-qp.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,
+};
+
+/*
+ * Unless otherwise noted, entries in this table are 100% optimization.
+ * Values can be obtained from hdmi_compute_n() but that function is
+ * slow so we pre-compute values we expect to see.
+ *
+ * All 32k and 48k values are expected to be the same (due to the way
+ * the math works) for any rate that's an exact kHz.
+ */
+static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
+ { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
+ { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
+ { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
+ { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
+ { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
+ { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
+ { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+ { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
+ { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
+ { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
+ { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
+ { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+ { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+ { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
+ { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
+ { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
+ { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
+ { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+ { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+ { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
+ { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
+ { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
+ { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+ { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
+ { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+
+ /* For 297 MHz+ HDMI spec have some other rule for setting N */
+ { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
+ { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
+
+ /* End of table */
+ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
+};
+
+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 */
+ 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);
+ 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->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;
+}
+
+static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi,
+ const 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;
+ 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));
+ drm_scdc_set_high_tmds_clock_ratio((struct drm_connector *)connector, 1);
+ drm_scdc_set_scrambling((struct drm_connector *)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((struct drm_connector *)connector, 0);
+ drm_scdc_set_scrambling((struct drm_connector *)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_qp_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;
+ 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);
+ ret = drm_add_edid_modes(connector, 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->edid_hdmi_ycbcr444_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_qp_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);
+
+ 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 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_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 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 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 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_qp_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_qp_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);
+
+ 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..fa7763e68e86 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -9,6 +9,8 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/irq.h>
@@ -19,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
+#include <linux/pinctrl/consumer.h>
#include <media/cec-notifier.h>
@@ -26,17 +29,19 @@
#include <uapi/linux/videodev2.h>
#include <drm/bridge/dw_hdmi.h>
-#include <drm/display/drm_hdmi_helper.h>
-#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_scdc_helper.h>
+#include <drm/display/drm_hdmi_helper.h>
#include "dw-hdmi-audio.h"
#include "dw-hdmi-cec.h"
+#include "dw-hdmi-hdcp.h"
#include "dw-hdmi.h"
#define DDC_CI_ADDR 0x37
@@ -49,6 +54,80 @@
#define HDMI14_MAX_TMDSCLK 340000000
+static const unsigned int dw_hdmi_cable[] = {
+ EXTCON_DISP_HDMI,
+ EXTCON_NONE,
+};
+
+enum hdmi_datamap {
+ RGB444_8B = 0x01,
+ RGB444_10B = 0x03,
+ RGB444_12B = 0x05,
+ RGB444_16B = 0x07,
+ YCbCr444_8B = 0x09,
+ YCbCr444_10B = 0x0B,
+ YCbCr444_12B = 0x0D,
+ YCbCr444_16B = 0x0F,
+ YCbCr422_8B = 0x16,
+ YCbCr422_10B = 0x14,
+ YCbCr422_12B = 0x12,
+};
+
+/*
+ * Unless otherwise noted, entries in this table are 100% optimization.
+ * Values can be obtained from hdmi_compute_n() but that function is
+ * slow so we pre-compute values we expect to see.
+ *
+ * All 32k and 48k values are expected to be the same (due to the way
+ * the math works) for any rate that's an exact kHz.
+ */
+static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
+ { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
+ { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
+ { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
+ { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
+ { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
+ { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
+ { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+ { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
+ { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
+ { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
+ { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
+ { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+ { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+ { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
+ { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
+ { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
+ { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
+ { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+ { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+ { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+ { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
+ { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
+ { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
+ { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+ { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+ { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
+ { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+
+ /* For 297 MHz+ HDMI spec have some other rule for setting N */
+ { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
+ { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
+
+ /* End of table */
+ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
+};
+
static const u16 csc_coeff_default[3][4] = {
{ 0x2000, 0x0000, 0x0000, 0x0000 },
{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -85,12 +164,47 @@ static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {
{ 0x0000, 0x0000, 0x1b7c, 0x0020 }
};
+static const struct drm_display_mode dw_hdmi_default_modes[] = {
+ /* 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, },
+ /* 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, },
+ /* 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, },
+};
+
struct hdmi_vmode {
bool mdataenablepolarity;
+ unsigned int previous_pixelclock;
unsigned int mpixelclock;
unsigned int mpixelrepetitioninput;
unsigned int mpixelrepetitionoutput;
+ unsigned int previous_tmdsclock;
unsigned int mtmdsclock;
};
@@ -99,8 +213,8 @@ struct hdmi_data_info {
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;
- unsigned int hdcp_enable;
struct hdmi_vmode video_mode;
bool rgb_limited_range;
};
@@ -115,6 +229,9 @@ struct dw_hdmi_i2c {
u8 slave_reg;
bool is_regaddr;
bool is_segment;
+
+ unsigned int scl_high_ns;
+ unsigned int scl_low_ns;
};
struct dw_hdmi_phy_data {
@@ -131,6 +248,7 @@ struct dw_hdmi {
struct drm_connector connector;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
+ struct platform_device *hdcp_dev;
unsigned int version;
@@ -144,8 +262,10 @@ struct dw_hdmi {
struct hdmi_data_info hdmi_data;
const struct dw_hdmi_plat_data *plat_data;
+ struct dw_hdcp *hdcp;
int vic;
+ int irq;
u8 edid[HDMI_EDID_LEN];
@@ -162,6 +282,13 @@ struct dw_hdmi {
void __iomem *regs;
bool sink_is_hdmi;
bool sink_has_audio;
+ bool hpd_state;
+ bool support_hdmi;
+ bool force_logo;
+ int force_output;
+
+ struct delayed_work work;
+ struct workqueue_struct *workqueue;
struct pinctrl *pinctrl;
struct pinctrl_state *default_state;
@@ -178,13 +305,14 @@ struct dw_hdmi {
spinlock_t audio_lock;
struct mutex audio_mutex;
- unsigned int sample_non_pcm;
- unsigned int sample_width;
+ struct dentry *debugfs_dir;
unsigned int sample_rate;
- unsigned int channels;
unsigned int audio_cts;
unsigned int audio_n;
bool audio_enable;
+ bool scramble_low_rates;
+
+ struct extcon_dev *extcon;
unsigned int reg_shift;
struct regmap *regm;
@@ -193,10 +321,12 @@ struct dw_hdmi {
struct mutex cec_notifier_mutex;
struct cec_notifier *cec_notifier;
+ struct cec_adapter *cec_adap;
hdmi_codec_plugged_cb plugged_cb;
struct device *codec_dev;
enum drm_connector_status last_connector_result;
+ bool initialized; /* hdmi is enabled before bind */
};
#define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -254,6 +384,124 @@ 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 repo_hpd_event(struct work_struct *p_work)
+{
+ struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work);
+ enum drm_connector_status status = hdmi->hpd_state ?
+ connector_status_connected : connector_status_disconnected;
+ u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+ mutex_lock(&hdmi->mutex);
+ if (!(phy_stat & HDMI_PHY_RX_SENSE))
+ hdmi->rxsense = false;
+ if (phy_stat & HDMI_PHY_HPD)
+ hdmi->rxsense = true;
+ mutex_unlock(&hdmi->mutex);
+
+ if (hdmi->bridge.dev) {
+ bool change;
+
+ change = drm_helper_hpd_irq_event(hdmi->bridge.dev);
+ if (change && hdmi->cec_adap &&
+ hdmi->cec_adap->devnode.registered)
+ cec_queue_pin_hpd_event(hdmi->cec_adap,
+ hdmi->hpd_state,
+ ktime_get());
+ drm_bridge_hpd_notify(&hdmi->bridge, status);
+ }
+}
+
+static bool check_hdmi_irq(struct dw_hdmi *hdmi, int intr_stat,
+ int phy_int_pol)
+{
+ int msecs;
+
+ /* To determine whether interrupt type is HPD */
+ if (!(intr_stat & HDMI_IH_PHY_STAT0_HPD))
+ return false;
+
+ if (phy_int_pol & HDMI_PHY_HPD) {
+ dev_dbg(hdmi->dev, "dw hdmi plug in\n");
+ msecs = 150;
+ hdmi->hpd_state = true;
+ } else {
+ dev_dbg(hdmi->dev, "dw hdmi plug out\n");
+ msecs = 20;
+ hdmi->hpd_state = false;
+ }
+ mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
+
+ return true;
+}
+
+static void init_hpd_work(struct dw_hdmi *hdmi)
+{
+ hdmi->workqueue = create_workqueue("hpd_queue");
+ INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
+}
+
+static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi)
+{
+ unsigned long clk_rate_khz;
+ unsigned long low_ns, high_ns;
+ unsigned long div_low, div_high;
+
+ /* Standard-mode */
+ if (hdmi->i2c->scl_high_ns < 4000)
+ high_ns = 4708;
+ else
+ high_ns = hdmi->i2c->scl_high_ns;
+
+ if (hdmi->i2c->scl_low_ns < 4700)
+ low_ns = 4916;
+ else
+ low_ns = hdmi->i2c->scl_low_ns;
+
+ /* Adjust to avoid overflow */
+ clk_rate_khz = DIV_ROUND_UP(clk_get_rate(hdmi->isfr_clk), 1000);
+
+ div_low = (clk_rate_khz * low_ns) / 1000000;
+ if ((clk_rate_khz * low_ns) % 1000000)
+ div_low++;
+
+ div_high = (clk_rate_khz * high_ns) / 1000000;
+ if ((clk_rate_khz * high_ns) % 1000000)
+ div_high++;
+
+ /* Maximum divider supported by hw is 0xffff */
+ if (div_low > 0xffff)
+ div_low = 0xffff;
+
+ if (div_high > 0xffff)
+ div_high = 0xffff;
+
+ hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
+ hdmi_writeb(hdmi, (div_high >> 8) & 0xff,
+ HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
+ hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
+ hdmi_writeb(hdmi, (div_low >> 8) & 0xff,
+ HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
+}
+
static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
{
hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
@@ -267,7 +515,8 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
/* Set Standard Mode speed (determined to be 100KHz on iMX6) */
- hdmi_writeb(hdmi, 0x00, HDMI_I2CM_DIV);
+ hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE,
+ HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV);
/* Set done, not acknowledged and arbitration interrupt polarities */
hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
@@ -281,6 +530,11 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
/* Mute DONE and ERROR interrupts */
hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
HDMI_IH_MUTE_I2CM_STAT0);
+
+ /* set SDA high level holding time */
+ hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
+
+ dw_hdmi_i2c_set_divs(hdmi);
}
static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi)
@@ -452,6 +706,8 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
/* Set slave device address taken from the first I2C message */
+ if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
+ addr = DDC_ADDR;
hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
/* Set slave device register address on transfer */
@@ -561,66 +817,117 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
}
-static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
+static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi,
+ unsigned long pixel_clk,
+ unsigned long freq)
{
- unsigned int n = (128 * freq) / 1000;
- unsigned int mult = 1;
+ 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;
+ }
+ }
+ }
- while (freq > 48000) {
- mult *= 2;
- freq /= 2;
+ 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:
- if (pixel_clk == 25175000)
- n = 4576;
- else if (pixel_clk == 27027000)
- n = 4096;
- else if (pixel_clk == 74176000 || pixel_clk == 148352000)
- n = 11648;
- else if (pixel_clk == 297000000)
- n = 3072;
- else
- n = 4096;
- n *= mult;
- break;
-
+ return tmds_n->n_32k;
case 44100:
- if (pixel_clk == 25175000)
- n = 7007;
- else if (pixel_clk == 74176000)
- n = 17836;
- else if (pixel_clk == 148352000)
- n = 8918;
- else if (pixel_clk == 297000000)
- n = 4704;
- else
- n = 6272;
- n *= mult;
- break;
-
+ case 88200:
+ case 176400:
+ return (freq / 44100) * tmds_n->n_44k1;
case 48000:
- if (pixel_clk == 25175000)
- n = 6864;
- else if (pixel_clk == 27027000)
- n = 6144;
- else if (pixel_clk == 74176000)
- n = 11648;
- else if (pixel_clk == 148352000)
- n = 5824;
- else if (pixel_clk == 297000000)
- n = 5120;
- else
- n = 6144;
- n *= mult;
- break;
-
+ case 96000:
+ case 192000:
+ return (freq / 48000) * tmds_n->n_48k;
default:
- break;
+ 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 *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 n;
+ return best_n;
+}
+
+static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk,
+ unsigned long sample_rate)
+{
+ int n;
+
+ n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
+ 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);
}
/*
@@ -651,12 +958,12 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
u8 config3;
u64 tmp;
- n = hdmi_compute_n(sample_rate, pixel_clk);
+ n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
- /* Compute CTS when using internal AHB audio or General Parallel audio*/
- if ((config3 & HDMI_CONFIG3_AHBAUDDMA) || (config3 & HDMI_CONFIG3_GPAUD)) {
+ /* Only compute CTS when using internal AHB audio */
+ if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
/*
* Compute the CTS value from the N value. Note that CTS and N
* can be up to 20 bits in total, so we need 64-bit math. Also
@@ -698,22 +1005,6 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
mutex_unlock(&hdmi->audio_mutex);
}
-void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width)
-{
- mutex_lock(&hdmi->audio_mutex);
- hdmi->sample_width = width;
- mutex_unlock(&hdmi->audio_mutex);
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_width);
-
-void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm)
-{
- mutex_lock(&hdmi->audio_mutex);
- hdmi->sample_non_pcm = non_pcm;
- mutex_unlock(&hdmi->audio_mutex);
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm);
-
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
{
mutex_lock(&hdmi->audio_mutex);
@@ -729,7 +1020,6 @@ void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt)
u8 layout;
mutex_lock(&hdmi->audio_mutex);
- hdmi->channels = cnt;
/*
* For >2 channel PCM audio, we need to select layout 1
@@ -770,97 +1060,6 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
}
-static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
-{
- if (!hdmi->curr_conn)
- return NULL;
-
- return hdmi->curr_conn->eld;
-}
-
-static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi)
-{
- const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
- int sample_freq = 0x2, org_sample_freq = 0xD;
- int ch_mask = BIT(hdmi->channels) - 1;
-
- switch (hdmi->sample_rate) {
- case 32000:
- sample_freq = 0x03;
- org_sample_freq = 0x0C;
- break;
- case 44100:
- sample_freq = 0x00;
- org_sample_freq = 0x0F;
- break;
- case 48000:
- sample_freq = 0x02;
- org_sample_freq = 0x0D;
- break;
- case 88200:
- sample_freq = 0x08;
- org_sample_freq = 0x07;
- break;
- case 96000:
- sample_freq = 0x0A;
- org_sample_freq = 0x05;
- break;
- case 176400:
- sample_freq = 0x0C;
- org_sample_freq = 0x03;
- break;
- case 192000:
- sample_freq = 0x0E;
- org_sample_freq = 0x01;
- break;
- default:
- break;
- }
-
- hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
- hdmi_enable_audio_clk(hdmi, true);
-
- hdmi_writeb(hdmi, 0x1, HDMI_FC_AUDSCHNLS0);
- hdmi_writeb(hdmi, hdmi->channels, HDMI_FC_AUDSCHNLS2);
- hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS3);
- hdmi_writeb(hdmi, 0x22, HDMI_FC_AUDSCHNLS4);
- hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS5);
- hdmi_writeb(hdmi, 0x11, HDMI_FC_AUDSCHNLS6);
- hdmi_writeb(hdmi, (0x3 << 4) | sample_freq, HDMI_FC_AUDSCHNLS7);
- hdmi_writeb(hdmi, (org_sample_freq << 4) | 0xb, HDMI_FC_AUDSCHNLS8);
-
- hdmi_writeb(hdmi, ch_mask, HDMI_GP_CONF1);
- hdmi_writeb(hdmi, 0x02, HDMI_GP_CONF2);
- hdmi_writeb(hdmi, 0x01, HDMI_GP_CONF0);
-
- hdmi_modb(hdmi, 0x3, 0x3, HDMI_FC_DATAUTO3);
-
- /* hbr */
- if (hdmi->sample_rate == 192000 && hdmi->channels == 8 &&
- hdmi->sample_width == 32 && hdmi->sample_non_pcm)
- hdmi_modb(hdmi, 0x01, 0x01, HDMI_GP_CONF2);
-
- if (pdata->enable_audio)
- pdata->enable_audio(hdmi,
- hdmi->channels,
- hdmi->sample_width,
- hdmi->sample_rate,
- hdmi->sample_non_pcm);
-}
-
-static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi)
-{
- const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
-
- hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
-
- hdmi_modb(hdmi, 0, 0x3, HDMI_FC_DATAUTO3);
- if (pdata->disable_audio)
- pdata->disable_audio(hdmi);
-
- hdmi_enable_audio_clk(hdmi, false);
-}
-
static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
{
hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
@@ -1110,6 +1309,15 @@ static bool is_csc_needed(struct dw_hdmi *hdmi)
is_color_space_interpolation(hdmi);
}
+static bool is_rgb_full_to_limited_needed(struct dw_hdmi *hdmi)
+{
+ if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
+ (!hdmi->hdmi_data.quant_range && hdmi->hdmi_data.rgb_limited_range))
+ return true;
+
+ return false;
+}
+
static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
{
const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
@@ -1132,7 +1340,7 @@ static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
csc_coeff = &csc_coeff_rgb_in_eitu709;
csc_scale = 0;
} else if (is_input_rgb && is_output_rgb &&
- hdmi->hdmi_data.rgb_limited_range) {
+ is_rgb_full_to_limited_needed(hdmi)) {
csc_coeff = &csc_coeff_rgb_full_to_rgb_limited;
}
@@ -1164,7 +1372,7 @@ static void hdmi_video_csc(struct dw_hdmi *hdmi)
if (is_color_space_interpolation(hdmi))
interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
else if (is_color_space_decimation(hdmi))
- decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+ decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1;
switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
case 8:
@@ -1204,8 +1412,6 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP;
struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
u8 val, vp_conf;
- u8 clear_gcp_auto = 0;
-
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
@@ -1213,9 +1419,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
switch (hdmi_bus_fmt_color_depth(
hdmi->hdmi_data.enc_out_bus_format)) {
case 8:
- color_depth = 4;
+ color_depth = 0;
output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
- clear_gcp_auto = 1;
break;
case 10:
color_depth = 5;
@@ -1235,7 +1440,6 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
case 0:
case 8:
remap_size = HDMI_VP_REMAP_YCC422_16bit;
- clear_gcp_auto = 1;
break;
case 10:
remap_size = HDMI_VP_REMAP_YCC422_20bit;
@@ -1253,31 +1457,15 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
}
/* set the packetizer registers */
- val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
- HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
- ((hdmi_data->pix_repet_factor <<
- HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
- HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
+ val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
+ HDMI_VP_PR_CD_COLOR_DEPTH_MASK;
hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
- /* HDMI1.4b specification section 6.5.3:
- * Source shall only send GCPs with non-zero CD to sinks
- * that indicate support for Deep Color.
- * GCP only transmit CD and do not handle AVMUTE, PP norDefault_Phase (yet).
- * Disable Auto GCP when 24-bit color for sinks that not support Deep Color.
- */
- val = hdmi_readb(hdmi, HDMI_FC_DATAUTO3);
- if (clear_gcp_auto == 1)
- val &= ~HDMI_FC_DATAUTO3_GCP_AUTO;
- else
- val |= HDMI_FC_DATAUTO3_GCP_AUTO;
- hdmi_writeb(hdmi, val, HDMI_FC_DATAUTO3);
-
hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
/* Data from pixel repeater block */
- if (hdmi_data->pix_repet_factor > 1) {
+ if (hdmi_data->pix_repet_factor > 0) {
vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
} else { /* data from packetizer block */
@@ -1289,8 +1477,13 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
HDMI_VP_CONF_PR_EN_MASK |
HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
- hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
- HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
+ if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) ||
+ (color_depth == 6 && hdmi->previous_mode.htotal % 2))
+ hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK,
+ HDMI_VP_STUFF);
+ else
+ hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+ HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
@@ -1391,6 +1584,23 @@ static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
return true;
}
+static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr)
+{
+ int val;
+
+ hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+ hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR);
+ hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR);
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ,
+ HDMI_PHY_I2CM_OPERATION_ADDR);
+ hdmi_phy_wait_i2c_done(hdmi, 1000);
+ val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR);
+ val = (val & 0xff) << 8;
+ val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff;
+ return val;
+}
+
/*
* HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
* - The Source shall suspend transmission of the TMDS clock and data
@@ -1576,6 +1786,13 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+ unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
+ unsigned int depth =
+ hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
+ pdata->mpll_cfg_420)
+ mpll_config = pdata->mpll_cfg_420;
/* TOFIX Will need 420 specific PHY configuration tables */
@@ -1585,11 +1802,11 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
break;
for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
- if (mpixelclock <= curr_ctrl->mpixelclock)
+ if (tmdsclock <= curr_ctrl->mpixelclock)
break;
for (; phy_config->mpixelclock != ~0UL; phy_config++)
- if (mpixelclock <= phy_config->mpixelclock)
+ if (tmdsclock <= phy_config->mpixelclock)
break;
if (mpll_config->mpixelclock == ~0UL ||
@@ -1597,11 +1814,18 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
phy_config->mpixelclock == ~0UL)
return -EINVAL;
- dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
+ if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+ depth = fls(depth - 8);
+ else
+ depth = 0;
+ if (depth)
+ depth--;
+
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
HDMI_3D_TX_PHY_CPCE_CTRL);
- dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
HDMI_3D_TX_PHY_GMPCTRL);
- dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
+ dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
HDMI_3D_TX_PHY_CURRCTRL);
dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
@@ -1614,10 +1838,6 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
HDMI_3D_TX_PHY_VLEVCTRL);
- /* Override and disable clock termination. */
- dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
- HDMI_3D_TX_PHY_CKCALCTRL);
-
return 0;
}
@@ -1719,14 +1939,16 @@ void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
HDMI_IH_PHY_STAT0);
- /* Enable cable hot plug irq. */
- hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+ if (!hdmi->next_bridge) {
+ /* Enable cable hot plug irq. */
+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
- /* Clear and unmute interrupts. */
- hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
- HDMI_IH_PHY_STAT0);
- hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
- HDMI_IH_MUTE_PHY_STAT0);
+ /* Clear and unmute interrupts. */
+ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+ HDMI_IH_PHY_STAT0);
+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+ HDMI_IH_MUTE_PHY_STAT0);
+ }
}
EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
@@ -1742,23 +1964,36 @@ static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
* HDMI TX Setup
*/
-static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
+static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
+ const struct drm_display_mode *mode)
{
- u8 de;
-
- if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
- de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
- else
- de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
-
- /* disable rx detect */
- hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
- HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
-
- hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
-
- hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
- HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+ u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi;
+
+ /* Configure the video polarity */
+ vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
+ HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
+ hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
+ HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
+ data_pol = vmode->mdataenablepolarity ?
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH :
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
+ hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol,
+ HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
+ HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
+ HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
+ HDMI_A_VIDPOLCFG);
+
+ /* Config the display mode */
+ hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
+ HDMI_A_HDCPCFG0_HDMIDVI_DVI;
+ hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
+ HDMI_A_HDCPCFG0);
+
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
+ hdmi->hdcp->hdcp_start(hdmi->hdcp);
}
static void hdmi_config_AVI(struct dw_hdmi *hdmi,
@@ -1772,10 +2007,15 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
- drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
- hdmi->hdmi_data.rgb_limited_range ?
- HDMI_QUANTIZATION_RANGE_LIMITED :
- HDMI_QUANTIZATION_RANGE_FULL);
+ /* default range */
+ if (!hdmi->hdmi_data.quant_range)
+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
+ hdmi->hdmi_data.rgb_limited_range ?
+ HDMI_QUANTIZATION_RANGE_LIMITED :
+ HDMI_QUANTIZATION_RANGE_FULL);
+ else
+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
+ hdmi->hdmi_data.quant_range);
} else {
frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
frame.ycc_quantization_range =
@@ -1810,6 +2050,14 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
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 =
@@ -1946,17 +2194,44 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
const struct drm_connector *connector)
{
const struct drm_connector_state *conn_state = connector->state;
+ struct hdr_output_metadata *hdr_metadata;
struct hdmi_drm_infoframe frame;
u8 buffer[30];
ssize_t err;
int i;
+ /* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */
+ if (hdmi->version < 0x211a) {
+ DRM_ERROR("Not support DRM Infoframe\n");
+ return;
+ }
+
if (!hdmi->plat_data->use_drm_infoframe)
return;
hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE,
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_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;
@@ -1976,51 +2251,66 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP);
hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE,
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
+
+ DRM_DEBUG("%s eotf %d end\n", __func__,
+ hdr_metadata->hdmi_metadata_type1.eotf);
}
-static void hdmi_av_composer(struct dw_hdmi *hdmi,
- const struct drm_display_info *display,
- const struct drm_display_mode *mode)
+static unsigned int
+hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock)
{
- u8 inv_val, bytes;
- const struct drm_hdmi_info *hdmi_info = &display->hdmi;
- struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
- int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
- unsigned int vdisplay, hdisplay;
-
- vmode->mpixelclock = mode->clock * 1000;
-
- dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
-
- vmode->mtmdsclock = vmode->mpixelclock;
+ unsigned int 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 (hdmi_bus_fmt_color_depth(
- hdmi->hdmi_data.enc_out_bus_format)) {
+ switch (depth) {
case 16:
- vmode->mtmdsclock = vmode->mpixelclock * 2;
+ tmdsclock = mpixelclock * 2;
break;
case 12:
- vmode->mtmdsclock = vmode->mpixelclock * 3 / 2;
+ tmdsclock = mpixelclock * 3 / 2;
break;
case 10:
- vmode->mtmdsclock = vmode->mpixelclock * 5 / 4;
+ tmdsclock = mpixelclock * 5 / 4;
+ break;
+ default:
break;
}
}
+ return tmdsclock;
+}
+
+static void hdmi_av_composer(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
+{
+ u8 inv_val, bytes;
+ const struct drm_hdmi_info *hdmi_info = &display->hdmi;
+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+ int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+ unsigned int vdisplay, hdisplay;
+
+ vmode->previous_pixelclock = vmode->mpixelclock;
+ 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 = %d\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_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
- /* Set up HDMI_FC_INVIDCONF */
- inv_val = (hdmi->hdmi_data.hdcp_enable ||
- (dw_hdmi_support_scdc(hdmi, display) &&
- (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
- hdmi_info->scdc.scrambling.low_rates)) ?
- HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
- HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
+ /* Set up HDMI_FC_INVIDCONF
+ * Some display equipments require that the interval
+ * between Video Data and Data island must be at least 58 pixels,
+ * and fc_invidconf.HDCP_keepout set (1'b1) can meet the requirement.
+ */
+ inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE;
inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
@@ -2086,7 +2376,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
/* Scrambling Control */
if (dw_hdmi_support_scdc(hdmi, display)) {
if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
- hdmi_info->scdc.scrambling.low_rates) {
+ (hdmi_info->scdc.scrambling.low_rates &&
+ hdmi->scramble_low_rates)) {
/*
* HDMI2.0 Specifies the following procedure:
* After the Source Device has determined that
@@ -2120,6 +2411,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
HDMI_MC_SWRSTZ);
drm_scdc_set_scrambling(hdmi->curr_conn, 0);
}
+ } else {
+ hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
}
/* Set up horizontal active pixel width */
@@ -2177,6 +2470,12 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+ /* Enable pixel repetition path */
+ if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) {
+ hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
+ hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+ }
+
/* Enable csc path */
if (is_csc_needed(hdmi)) {
hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
@@ -2207,22 +2506,31 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
* then write one of the FC registers several times.
*
* The number of iterations matters and depends on the HDMI TX revision
- * (and possibly on the platform).
- * 4 iterations for i.MX6Q(v1.30a) and 1 iteration for others.
- * i.MX6DL (v1.31a), Allwinner SoCs (v1.32a), Rockchip RK3288 SoC (v2.00a),
- * Amlogic Meson GX SoCs (v2.01a), RK3328/RK3399 SoCs (v2.11a)
- * and i.MX8MPlus (v2.13a) have been identified as needing the workaround
- * with a single iteration.
+ * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
+ * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
+ * as needing the workaround, with 4 iterations for v1.30a and 1
+ * iteration for others.
+ * The Amlogic Meson GX SoCs (v2.01a) have been identified as needing
+ * the workaround with a single iteration.
+ * The Rockchip RK3288 SoC (v2.00a) and RK3328/RK3399 SoCs (v2.11a) have
+ * been identified as needing the workaround with a single iteration.
*/
switch (hdmi->version) {
case 0x130a:
count = 4;
break;
- default:
+ case 0x131a:
+ case 0x132a:
+ case 0x200a:
+ case 0x201a:
+ case 0x211a:
+ case 0x212a:
count = 1;
break;
- }
+ default:
+ return;
+ }
/* TMDS software reset */
hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
@@ -2243,6 +2551,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
const struct drm_display_mode *mode)
{
int ret;
+ void *data = hdmi->plat_data->phy_data;
hdmi_disable_overflow_interrupts(hdmi);
@@ -2254,48 +2563,91 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
}
- 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))
+ 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;
- hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
- hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
+ 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;
+ }
+ /* TOFIX: 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;
- if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
- hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ /* TOFIX: 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;
/* TOFIX: Get input encoding from plat data or fallback to none */
- if (hdmi->plat_data->input_bus_encoding)
+ 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->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
- hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+ if (hdmi->plat_data->get_quant_range)
+ hdmi->hdmi_data.quant_range =
+ hdmi->plat_data->get_quant_range(data);
hdmi->hdmi_data.rgb_limited_range = hdmi->sink_is_hdmi &&
drm_default_rgb_quant_range(mode) ==
HDMI_QUANTIZATION_RANGE_LIMITED;
- hdmi->hdmi_data.pix_repet_factor = 0;
- hdmi->hdmi_data.hdcp_enable = 0;
+ if (!hdmi->sink_is_hdmi)
+ hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_FULL;
+
+ /*
+ * 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;
/* HDMI Initialization Step B.1 */
hdmi_av_composer(hdmi, &connector->display_info, mode);
/* HDMI Initializateion Step B.2 */
- ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
- &connector->display_info,
- &hdmi->previous_mode);
- if (ret)
- return ret;
- hdmi->phy.enabled = true;
+ if (!hdmi->phy.enabled ||
+ hdmi->hdmi_data.video_mode.previous_pixelclock !=
+ hdmi->hdmi_data.video_mode.mpixelclock ||
+ hdmi->hdmi_data.video_mode.previous_tmdsclock !=
+ hdmi->hdmi_data.video_mode.mtmdsclock) {
+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
+ &connector->display_info,
+ &hdmi->previous_mode);
+ if (ret)
+ return ret;
+ hdmi->phy.enabled = true;
+ }
/* HDMI Initialization Step B.3 */
dw_hdmi_enable_video_path(hdmi);
@@ -2323,7 +2675,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
hdmi_video_packetize(hdmi);
hdmi_video_csc(hdmi);
hdmi_video_sample(hdmi);
- hdmi_tx_hdcp_config(hdmi);
+ hdmi_tx_hdcp_config(hdmi, mode);
dw_hdmi_clear_overflow(hdmi);
@@ -2399,6 +2751,8 @@ static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
hdmi->phy.enabled = false;
}
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
+ hdmi->hdcp->hdcp_stop(hdmi->hdcp);
hdmi->bridge_is_on = false;
}
@@ -2416,6 +2770,10 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
}
if (force == DRM_FORCE_OFF) {
+ if (hdmi->initialized) {
+ hdmi->initialized = false;
+ hdmi->disabled = true;
+ }
if (hdmi->bridge_is_on)
dw_hdmi_poweroff(hdmi);
} else {
@@ -2448,8 +2806,28 @@ static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
{
enum drm_connector_status result;
+ if (!hdmi->force_logo) {
+ mutex_lock(&hdmi->mutex);
+ hdmi->force = DRM_FORCE_UNSPECIFIED;
+ dw_hdmi_update_power(hdmi);
+ dw_hdmi_update_phy_mask(hdmi);
+ mutex_unlock(&hdmi->mutex);
+ }
+
result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
- hdmi->last_connector_result = result;
+ 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);
+
+ if (result == connector_status_connected)
+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);
+ else
+ extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
return result;
}
@@ -2471,7 +2849,7 @@ static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
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->support_hdmi = drm_detect_hdmi_monitor(edid);
hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
return edid;
@@ -2489,25 +2867,123 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
return dw_hdmi_detect(hdmi);
}
+static int
+dw_hdmi_update_hdr_property(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ 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 *hdmi = container_of(connector, struct dw_hdmi,
connector);
+ struct hdr_static_metadata *metedata =
+ &connector->hdr_sink_metadata.hdmi_type1;
struct edid *edid;
- int ret;
+ struct drm_display_mode *mode;
+ struct drm_display_info *info = &connector->display_info;
+ int i, ret = 0;
+ memset(metedata, 0, sizeof(*metedata));
edid = dw_hdmi_get_edid(hdmi, connector);
- if (!edid)
- return 0;
+ if (edid) {
+ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+ edid->width_cm, edid->height_cm);
+ drm_connector_update_edid_property(connector, edid);
+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ dw_hdmi_update_hdr_property(connector);
+ kfree(edid);
+ } else {
+ hdmi->support_hdmi = true;
+ hdmi->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;
+ mode->picture_aspect_ratio =
+ HDMI_PICTURE_ASPECT_NONE;
+ }
+ drm_mode_probed_add(connector, mode);
+ ret++;
+ }
+ }
+ info->edid_hdmi_rgb444_dc_modes = 0;
+ info->edid_hdmi_ycbcr444_dc_modes = 0;
+ info->hdmi.y420_dc_modes = 0;
+ info->color_formats = 0;
- drm_connector_update_edid_property(connector, edid);
- cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
- ret = drm_add_edid_modes(connector, edid);
- kfree(edid);
+ dev_info(hdmi->dev, "failed to get edid\n");
+ }
+ dw_hdmi_check_output_type_changed(hdmi);
+
+ return ret;
+}
+
+static struct drm_encoder *
+dw_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+
+ return hdmi->bridge.encoder;
+}
+
+static bool dw_hdmi_color_changed(struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ void *data = hdmi->plat_data->phy_data;
+ bool ret = false;
+
+ if (hdmi->plat_data->get_color_changed)
+ ret = hdmi->plat_data->get_color_changed(data);
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)
{
@@ -2517,11 +2993,54 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
drm_atomic_get_new_connector_state(state, connector);
struct drm_crtc *crtc = new_state->crtc;
struct drm_crtc_state *crtc_state;
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ struct drm_display_mode *mode = NULL;
+ void *data = hdmi->plat_data->phy_data;
+ struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+ unsigned int in_bus_format = hdmi->hdmi_data.enc_in_bus_format;
+ unsigned int out_bus_format = hdmi->hdmi_data.enc_out_bus_format;
+ bool color_changed = false;
if (!crtc)
return 0;
- if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_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;
+ 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 (in_bus_format != hdmi->hdmi_data.enc_in_bus_format ||
+ out_bus_format != hdmi->hdmi_data.enc_out_bus_format)
+ color_changed = true;
+ }
+
+ if (!hdr_metadata_equal(old_state, new_state) ||
+ dw_hdmi_color_changed(connector) || color_changed) {
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
@@ -2532,12 +3051,105 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
return 0;
}
+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 *hdmi = container_of(connector, struct dw_hdmi,
+ 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 *hdmi = container_of(connector, struct dw_hdmi,
+ 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);
+}
+
+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,
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;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
@@ -2550,15 +3162,98 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
.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 void dw_hdmi_attach_properties(struct dw_hdmi *hdmi)
+{
+ unsigned int color = MEDIA_BUS_FMT_RGB888_1X24;
+ int video_mapping, colorspace;
+ enum drm_connector_status connect_status =
+ hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+ const struct dw_hdmi_property_ops *ops =
+ hdmi->plat_data->property_ops;
+
+ if (connect_status == connector_status_connected) {
+ video_mapping = (hdmi_readb(hdmi, HDMI_TX_INVID0) &
+ HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+ colorspace = (hdmi_readb(hdmi, HDMI_FC_AVICONF0) &
+ HDMI_FC_AVICONF0_PIX_FMT_MASK);
+ switch (video_mapping) {
+ case 0x01:
+ color = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case 0x03:
+ color = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+ case 0x09:
+ if (colorspace == HDMI_COLORSPACE_YUV420)
+ color = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+ else if (colorspace == HDMI_COLORSPACE_YUV422)
+ color = MEDIA_BUS_FMT_UYVY8_1X16;
+ else
+ color = MEDIA_BUS_FMT_YUV8_1X24;
+ break;
+ case 0x0b:
+ if (colorspace == HDMI_COLORSPACE_YUV420)
+ color = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+ else if (colorspace == HDMI_COLORSPACE_YUV422)
+ color = MEDIA_BUS_FMT_UYVY10_1X20;
+ else
+ color = MEDIA_BUS_FMT_YUV10_1X30;
+ break;
+ case 0x14:
+ color = MEDIA_BUS_FMT_UYVY10_1X20;
+ break;
+ case 0x16:
+ color = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ default:
+ color = MEDIA_BUS_FMT_RGB888_1X24;
+ dev_err(hdmi->dev, "unexpected mapping: 0x%x\n",
+ video_mapping);
+ }
+
+ hdmi->hdmi_data.enc_in_bus_format = color;
+ hdmi->hdmi_data.enc_out_bus_format = color;
+ /*
+ * input format will be set as yuv444 when output
+ * format is yuv420
+ */
+ if (color == MEDIA_BUS_FMT_UYVY10_1X20)
+ hdmi->hdmi_data.enc_in_bus_format =
+ MEDIA_BUS_FMT_YUV10_1X30;
+ else if (color == MEDIA_BUS_FMT_UYVY8_1X16)
+ hdmi->hdmi_data.enc_in_bus_format =
+ MEDIA_BUS_FMT_YUV8_1X24;
+ }
+
+ if (ops && ops->attach_properties)
+ return ops->attach_properties(&hdmi->connector,
+ color, hdmi->version,
+ hdmi->plat_data->phy_data);
+}
+
+static void dw_hdmi_destroy_properties(struct dw_hdmi *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 int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
{
struct drm_connector *connector = &hdmi->connector;
@@ -2590,10 +3285,13 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
drm_connector_attach_max_bpc_property(connector, 8, 16);
if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
- drm_connector_attach_hdr_output_metadata_property(connector);
+ drm_object_attach_property(&connector->base,
+ connector->dev->mode_config.hdr_output_metadata_property, 0);
drm_connector_attach_encoder(connector, hdmi->bridge.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);
@@ -2655,9 +3353,8 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
if (!output_fmts)
return NULL;
- /* If dw-hdmi is the first or only bridge, avoid negociating with ourselves */
- if (list_is_singular(&bridge->encoder->bridge_chain) ||
- list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain)) {
+ /* If dw-hdmi is the only bridge, avoid negociating with ourselves */
+ if (list_is_singular(&bridge->encoder->bridge_chain)) {
*num_output_fmts = 1;
output_fmts[0] = MEDIA_BUS_FMT_FIXED;
@@ -2870,16 +3567,36 @@ static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_connector_state *conn_state)
{
struct dw_hdmi *hdmi = bridge->driver_private;
+ void *data = hdmi->plat_data->phy_data;
- hdmi->hdmi_data.enc_out_bus_format =
- bridge_state->output_bus_cfg.format;
+ if (bridge_state->output_bus_cfg.format == MEDIA_BUS_FMT_FIXED) {
+ 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;
+
+ 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;
+ } else {
+ hdmi->hdmi_data.enc_out_bus_format =
+ bridge_state->output_bus_cfg.format;
- hdmi->hdmi_data.enc_in_bus_format =
- bridge_state->input_bus_cfg.format;
+ hdmi->hdmi_data.enc_in_bus_format =
+ bridge_state->input_bus_cfg.format;
- dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
- bridge_state->input_bus_cfg.format,
- bridge_state->output_bus_cfg.format);
+ dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
+ bridge_state->input_bus_cfg.format,
+ bridge_state->output_bus_cfg.format);
+ }
return 0;
}
@@ -2888,10 +3605,21 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct dw_hdmi *hdmi = bridge->driver_private;
+ int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
- return drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
- bridge, flags);
+ return 0;
+
+ if (hdmi->next_bridge) {
+ hdmi->next_bridge->encoder = bridge->encoder;
+ ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags);
+ if (ret) {
+ DRM_ERROR("Failed to attach bridge with dw-hdmi\n");
+ return ret;
+ }
+
+ return 0;
+ }
return dw_hdmi_connector_create(hdmi);
}
@@ -2915,14 +3643,12 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
enum drm_mode_status mode_status = MODE_OK;
- /* We don't support double-clocked modes */
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- return MODE_BAD;
+ if (hdmi->next_bridge)
+ return MODE_OK;
if (pdata->mode_valid)
- mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
- mode);
-
+ mode_status = pdata->mode_valid(hdmi, pdata->priv_data,
+ info, mode);
return mode_status;
}
@@ -2935,7 +3661,7 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
mutex_lock(&hdmi->mutex);
/* Store the display mode for plugin/DKMS poweron events */
- drm_mode_copy(&hdmi->previous_mode, mode);
+ memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
mutex_unlock(&hdmi->mutex);
}
@@ -3005,6 +3731,12 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.get_edid = dw_hdmi_bridge_get_edid,
};
+void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap)
+{
+ hdmi->cec_adap = adap;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_cec_adap);
+
/* -----------------------------------------------------------------------------
* IRQ Handling
*/
@@ -3030,7 +3762,7 @@ static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
{
struct dw_hdmi *hdmi = dev_id;
- u8 intr_stat;
+ u8 intr_stat, hdcp_stat;
irqreturn_t ret = IRQ_NONE;
if (hdmi->i2c)
@@ -3042,6 +3774,13 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
+ hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
+ if (hdcp_stat) {
+ dev_dbg(hdmi->dev, "HDCP irq %#x\n", hdcp_stat);
+ hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
+ return IRQ_WAKE_THREAD;
+ }
+
return ret;
}
@@ -3049,7 +3788,7 @@ void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense)
{
mutex_lock(&hdmi->mutex);
- if (!hdmi->force) {
+ if (!hdmi->force && !hdmi->force_logo) {
/*
* If the RX sense status indicates we're disconnected,
* clear the software rxsense status.
@@ -3076,7 +3815,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense);
static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
{
struct dw_hdmi *hdmi = dev_id;
- u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
+ u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat;
enum drm_connector_status status = connector_status_unknown;
intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
@@ -3135,10 +3874,21 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
}
}
- hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
- hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
- HDMI_IH_MUTE_PHY_STAT0);
+ check_hdmi_irq(hdmi, intr_stat, phy_int_pol);
+ hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+ if (!hdmi->next_bridge)
+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+ HDMI_IH_PHY_STAT0_RX_SENSE),
+ HDMI_IH_MUTE_PHY_STAT0);
+
+ hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
+ if (hdcp_stat) {
+ if (hdmi->hdcp)
+ hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
+ hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
+ hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
+ }
return IRQ_HANDLED;
}
@@ -3272,72 +4022,372 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
* Even if we are using a separate i2c adapter doing this doesn't
* hurt.
*/
- dw_hdmi_i2c_init(hdmi);
+ if (hdmi->i2c)
+ dw_hdmi_i2c_init(hdmi);
if (hdmi->phy.ops->setup_hpd)
hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
}
-/* -----------------------------------------------------------------------------
- * Probe/remove API, used from platforms based on the DRM bridge API.
- */
-
-static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi)
+static int dw_hdmi_status_show(struct seq_file *s, void *v)
{
- struct device_node *endpoint;
- struct device_node *remote;
+ struct dw_hdmi *hdmi = s->private;
+ u32 val;
+
+ seq_puts(s, "PHY: ");
+ if (!hdmi->phy.enabled) {
+ 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.mtmdsclock > 340000000)
+ val = hdmi->hdmi_data.video_mode.mtmdsclock / 4;
+ else
+ val = hdmi->hdmi_data.video_mode.mtmdsclock;
+ seq_printf(s, "Pixel Clk: %uHz\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: ");
- if (!hdmi->plat_data->output_port)
+ if (hdmi->version < 0x211a) {
+ seq_puts(s, "Unsupported\n");
return 0;
+ }
- endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node,
- hdmi->plat_data->output_port,
- -1);
- if (!endpoint) {
- /*
- * On platforms whose bindings don't make the output port
- * mandatory (such as Rockchip) the plat_data->output_port
- * field isn't set, so it's safe to make this a fatal error.
- */
- dev_err(hdmi->dev, "Missing endpoint in port@%u\n",
- hdmi->plat_data->output_port);
- return -ENODEV;
+ val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN);
+ if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) {
+ seq_puts(s, "Off\n");
+ return 0;
}
- remote = of_graph_get_remote_port_parent(endpoint);
- of_node_put(endpoint);
- if (!remote) {
- dev_err(hdmi->dev, "Endpoint in port@%u unconnected\n",
- hdmi->plat_data->output_port);
- return -ENODEV;
+ switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) {
+ 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;
}
- if (!of_device_is_available(remote)) {
- dev_err(hdmi->dev, "port@%u remote device is disabled\n",
- hdmi->plat_data->output_port);
- of_node_put(remote);
- return -ENODEV;
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB3) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB2);
+ seq_printf(s, "\nx0: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB5) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB4);
+ seq_printf(s, "\t\t\t\ty0: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB7) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB6);
+ seq_printf(s, "x1: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB9) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB8);
+ seq_printf(s, "\t\t\t\ty1: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB11) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB10);
+ seq_printf(s, "x2: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB13) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB12);
+ seq_printf(s, "\t\t\t\ty2: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB15) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB14);
+ seq_printf(s, "white x: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB17) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB16);
+ seq_printf(s, "\t\t\twhite y: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB19) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB18);
+ seq_printf(s, "max lum: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB21) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB20);
+ seq_printf(s, "\t\t\tmin lum: %d\n", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB23) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB22);
+ seq_printf(s, "max cll: %d", val);
+ val = hdmi_readb(hdmi, HDMI_FC_DRM_PB25) << 8;
+ val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB24);
+ 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,
+};
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+struct dw_hdmi_reg_table {
+ int reg_base;
+ int reg_end;
+};
+
+static const struct dw_hdmi_reg_table hdmi_reg_table[] = {
+ {HDMI_DESIGN_ID, HDMI_CONFIG3_ID},
+ {HDMI_IH_FC_STAT0, HDMI_IH_MUTE},
+ {HDMI_TX_INVID0, HDMI_TX_BCBDATA1},
+ {HDMI_VP_STATUS, HDMI_VP_POL},
+ {HDMI_FC_INVIDCONF, HDMI_FC_DBGTMDS2},
+ {HDMI_PHY_CONF0, HDMI_PHY_POL0},
+ {HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR},
+ {HDMI_AUD_CONF0, 0x3624},
+ {HDMI_MC_SFRDIV, HDMI_MC_HEACPHY_RST},
+ {HDMI_CSC_CFG, HDMI_CSC_COEF_C4_LSB},
+ {HDMI_A_HDCPCFG0, 0x52bb},
+ {0x7800, 0x7818},
+ {0x7900, 0x790e},
+ {HDMI_CEC_CTRL, HDMI_CEC_WKUPCTRL},
+ {HDMI_I2CM_SLAVE, 0x7e31},
+};
+
+static int dw_hdmi_ctrl_show(struct seq_file *s, void *v)
+{
+ struct dw_hdmi *hdmi = s->private;
+ u32 i = 0, j = 0, val = 0;
+
+ seq_puts(s, "\n>>>hdmi_ctl reg ");
+ for (i = 0; i < 16; i++)
+ seq_printf(s, " %2x", i);
+ 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++) {
+ val = hdmi_readb(hdmi, j);
+ if ((j - hdmi_reg_table[i].reg_base) % 16 == 0)
+ seq_printf(s, "\n>>>hdmi_ctl %04x:", j);
+ seq_printf(s, " %02x", val);
+ }
}
+ seq_puts(s, "\n---------------------------------------------------\n");
- hdmi->next_bridge = of_drm_find_bridge(remote);
- of_node_put(remote);
- if (!hdmi->next_bridge)
- return -EPROBE_DEFER;
+ 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 *hdmi =
+ ((struct seq_file *)file->private_data)->private;
+ u32 reg, val;
+ char kbuf[25];
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+ if (sscanf(kbuf, "%x%x", &reg, &val) == -1)
+ return -EFAULT;
+ if (reg > HDMI_I2CM_FS_SCL_LCNT_0_ADDR) {
+ 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_writeb(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_phy_show(struct seq_file *s, void *v)
+{
+ struct dw_hdmi *hdmi = s->private;
+ u32 i;
+
+ seq_puts(s, "\n>>>hdmi_phy reg ");
+ for (i = 0; i < 0x28; i++)
+ seq_printf(s, "regs %02x val %04x\n",
+ i, hdmi_phy_i2c_read(hdmi, i));
return 0;
}
-bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi)
+static int dw_hdmi_phy_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dw_hdmi_phy_show, inode->i_private);
+}
+
+static ssize_t
+dw_hdmi_phy_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dw_hdmi *hdmi =
+ ((struct seq_file *)file->private_data)->private;
+ u32 reg, val;
+ char kbuf[25];
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EFAULT;
+ if (sscanf(kbuf, "%x%x", &reg, &val) == -1)
+ return -EFAULT;
+ if (reg > 0x28) {
+ dev_err(hdmi->dev, "it is not a hdmi phy register\n");
+ return count;
+ }
+ dev_info(hdmi->dev, "/*******hdmi phy register config******/");
+ dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
+ dw_hdmi_phy_i2c_write(hdmi, val, reg);
+ return count;
+}
+
+static const struct file_operations dw_hdmi_phy_fops = {
+ .owner = THIS_MODULE,
+ .open = dw_hdmi_phy_open,
+ .read = seq_read,
+ .write = dw_hdmi_phy_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi *hdmi)
+{
+ hdmi->debugfs_dir = debugfs_create_dir("dw-hdmi", 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", 0400, hdmi->debugfs_dir,
+ hdmi, &dw_hdmi_ctrl_fops);
+ debugfs_create_file("phy", 0400, hdmi->debugfs_dir,
+ hdmi, &dw_hdmi_phy_fops);
+}
+
+static void dw_hdmi_register_hdcp(struct device *dev, struct dw_hdmi *hdmi,
+ u32 val, bool hdcp1x_enable)
+{
+ struct dw_hdcp hdmi_hdcp = {
+ .hdmi = hdmi,
+ .write = hdmi_writeb,
+ .read = hdmi_readb,
+ .regs = hdmi->regs,
+ .reg_io_width = val,
+ .enable = hdcp1x_enable,
+ };
+ struct platform_device_info hdcp_device_info = {
+ .parent = dev,
+ .id = PLATFORM_DEVID_AUTO,
+ .res = NULL,
+ .num_res = 0,
+ .name = DW_HDCP_DRIVER_NAME,
+ .data = &hdmi_hdcp,
+ .size_data = sizeof(hdmi_hdcp),
+ .dma_mask = DMA_BIT_MASK(32),
+ };
+
+ hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
+ if (IS_ERR(hdmi->hdcp_dev))
+ dev_err(dev, "failed to register hdcp!\n");
+ else
+ hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
+}
+
+static int get_force_logo_property(struct dw_hdmi *hdmi)
{
- return hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format);
+ struct device_node *dss;
+ struct device_node *route;
+ struct device_node *route_hdmi;
+
+ dss = of_find_node_by_name(NULL, "display-subsystem");
+ if (!dss) {
+ dev_err(hdmi->dev, "can't find display-subsystem\n");
+ return -ENODEV;
+ }
+
+ route = of_find_node_by_name(dss, "route");
+ if (!route) {
+ dev_err(hdmi->dev, "can't find route\n");
+ of_node_put(dss);
+ return -ENODEV;
+ }
+ of_node_put(dss);
+
+ route_hdmi = of_find_node_by_name(route, "route-hdmi");
+ if (!route_hdmi) {
+ dev_err(hdmi->dev, "can't find route-hdmi\n");
+ of_node_put(route);
+ return -ENODEV;
+ }
+ of_node_put(route);
+
+ hdmi->force_logo =
+ of_property_read_bool(route_hdmi, "force-output");
+
+ of_node_put(route_hdmi);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(dw_hdmi_bus_fmt_is_420);
+/* -----------------------------------------------------------------------------
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
struct dw_hdmi *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 *endpoint;
struct platform_device_info pdevinfo;
struct device_node *ddc_node;
struct dw_hdmi_cec_data cec;
@@ -3350,15 +4400,16 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
u8 prod_id1;
u8 config0;
u8 config3;
+ bool hdcp1x_enable = 0;
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->channels = 2;
hdmi->disabled = true;
hdmi->rxsense = true;
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
@@ -3370,10 +4421,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
mutex_init(&hdmi->cec_notifier_mutex);
spin_lock_init(&hdmi->audio_lock);
- ret = dw_hdmi_parse_dt(hdmi);
- if (ret < 0)
- return ERR_PTR(ret);
-
ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
if (ddc_node) {
hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
@@ -3490,7 +4537,24 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without",
hdmi->phy.name);
- dw_hdmi_init_hw(hdmi);
+ ret = get_force_logo_property(hdmi);
+ if (ret)
+ goto err_iahb;
+
+ hdmi->initialized = false;
+ ret = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+ if (((ret & HDMI_PHY_TX_PHY_LOCK) && (ret & HDMI_PHY_HPD) &&
+ hdmi_readb(hdmi, HDMI_FC_EXCTRLDUR)) || hdmi->force_logo) {
+ hdmi->mc_clkdis = hdmi_readb(hdmi, HDMI_MC_CLKDIS);
+ hdmi->disabled = false;
+ hdmi->bridge_is_on = true;
+ hdmi->phy.enabled = true;
+ hdmi->initialized = true;
+ } else if (ret & HDMI_PHY_TX_PHY_LOCK) {
+ hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
+ }
+
+ init_hpd_work(hdmi);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -3498,6 +4562,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
goto err_iahb;
}
+ hdmi->irq = irq;
ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
dw_hdmi_irq, IRQF_SHARED,
dev_name(dev), hdmi);
@@ -3533,8 +4598,20 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
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;
}
+ dw_hdmi_init_hw(hdmi);
+
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
@@ -3543,6 +4620,30 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->bridge.ddc = hdmi->ddc;
hdmi->bridge.of_node = pdev->dev.of_node;
+ endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node, 1, -1);
+ if (endpoint && of_device_is_available(endpoint)) {
+ struct device_node *remote;
+
+ remote = of_graph_get_remote_port_parent(endpoint);
+ of_node_put(endpoint);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ ret = -ENODEV;
+ goto err_iahb;
+ }
+
+ hdmi->next_bridge = of_drm_find_bridge(remote);
+ of_node_put(remote);
+ if (!hdmi->next_bridge) {
+ dev_err(hdmi->dev, "can't find next bridge\n");
+ ret = -EPROBE_DEFER;
+ goto err_iahb;
+ }
+
+ hdmi->sink_is_hdmi = true;
+ hdmi->sink_has_audio = true;
+ }
+
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;
pdevinfo.id = PLATFORM_DEVID_AUTO;
@@ -3557,7 +4658,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
audio.base = hdmi->regs;
audio.irq = irq;
audio.hdmi = hdmi;
- audio.get_eld = hdmi_audio_get_eld;
+ audio.eld = hdmi->connector.eld;
hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
@@ -3570,7 +4671,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
struct dw_hdmi_i2s_audio_data audio;
audio.hdmi = hdmi;
- audio.get_eld = hdmi_audio_get_eld;
+ audio.eld = hdmi->connector.eld;
audio.write = hdmi_writeb;
audio.read = hdmi_readb;
hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
@@ -3581,27 +4682,9 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
pdevinfo.size_data = sizeof(audio);
pdevinfo.dma_mask = DMA_BIT_MASK(32);
hdmi->audio = platform_device_register_full(&pdevinfo);
- } else if (iores && config3 & HDMI_CONFIG3_GPAUD) {
- struct dw_hdmi_audio_data audio;
-
- audio.phys = iores->start;
- audio.base = hdmi->regs;
- audio.irq = irq;
- audio.hdmi = hdmi;
- audio.get_eld = hdmi_audio_get_eld;
-
- hdmi->enable_audio = dw_hdmi_gp_audio_enable;
- hdmi->disable_audio = dw_hdmi_gp_audio_disable;
-
- pdevinfo.name = "dw-hdmi-gp-audio";
- pdevinfo.id = PLATFORM_DEVID_NONE;
- pdevinfo.data = &audio;
- pdevinfo.size_data = sizeof(audio);
- pdevinfo.dma_mask = DMA_BIT_MASK(32);
- hdmi->audio = platform_device_register_full(&pdevinfo);
}
- if (!plat_data->disable_cec && (config0 & HDMI_CONFIG0_CEC)) {
+ if (config0 & HDMI_CONFIG0_CEC) {
cec.hdmi = hdmi;
cec.ops = &dw_hdmi_cec_ops;
cec.irq = irq;
@@ -3614,17 +4697,53 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->cec = platform_device_register_full(&pdevinfo);
}
+ hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable);
+ if (IS_ERR(hdmi->extcon)) {
+ ret = PTR_ERR(hdmi->extcon);
+ dev_err(hdmi->dev, "allocate extcon failed: %d\n", ret);
+ goto err_iahb;
+ }
+
+ ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon);
+ if (ret) {
+ dev_err(hdmi->dev, "failed to register extcon: %d\n",
+ ret);
+ goto err_iahb;
+ }
+
+ 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_iahb;
+ }
+
drm_bridge_add(&hdmi->bridge);
+ dw_hdmi_register_debugfs(dev, hdmi);
+
+ if (of_property_read_bool(np, "scramble-low-rates"))
+ hdmi->scramble_low_rates = true;
+
+ if (of_property_read_bool(np, "hdcp1x-enable"))
+ hdcp1x_enable = 1;
+ dw_hdmi_register_hdcp(dev, hdmi, val, hdcp1x_enable);
+
return hdmi;
err_iahb:
clk_disable_unprepare(hdmi->iahb_clk);
- clk_disable_unprepare(hdmi->cec_clk);
+ if (hdmi->cec_clk)
+ clk_disable_unprepare(hdmi->cec_clk);
err_isfr:
clk_disable_unprepare(hdmi->isfr_clk);
err_res:
- i2c_put_adapter(hdmi->ddc);
+ if (hdmi->i2c)
+ i2c_del_adapter(&hdmi->i2c->adap);
+ else
+ i2c_put_adapter(hdmi->ddc);
return ERR_PTR(ret);
}
@@ -3632,19 +4751,39 @@ EXPORT_SYMBOL_GPL(dw_hdmi_probe);
void dw_hdmi_remove(struct dw_hdmi *hdmi)
{
+ if (hdmi->irq)
+ disable_irq(hdmi->irq);
+
+ cancel_delayed_work(&hdmi->work);
+ flush_workqueue(hdmi->workqueue);
+ destroy_workqueue(hdmi->workqueue);
+
+ debugfs_remove_recursive(hdmi->debugfs_dir);
+
drm_bridge_remove(&hdmi->bridge);
if (hdmi->audio && !IS_ERR(hdmi->audio))
platform_device_unregister(hdmi->audio);
+ if (hdmi->hdcp_dev && !IS_ERR(hdmi->hdcp_dev))
+ platform_device_unregister(hdmi->hdcp_dev);
if (!IS_ERR(hdmi->cec))
platform_device_unregister(hdmi->cec);
/* Disable all interrupts */
hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+ if (!hdmi->next_bridge) {
+ dw_hdmi_destroy_properties(hdmi);
+ hdmi->connector.funcs->destroy(&hdmi->connector);
+ }
+
+ if (hdmi->bridge.encoder)
+ hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
+
clk_disable_unprepare(hdmi->iahb_clk);
clk_disable_unprepare(hdmi->isfr_clk);
- clk_disable_unprepare(hdmi->cec_clk);
+ if (hdmi->cec_clk)
+ clk_disable_unprepare(hdmi->cec_clk);
if (hdmi->i2c)
i2c_del_adapter(&hdmi->i2c->adap);
@@ -3658,7 +4797,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_remove);
*/
struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
struct drm_encoder *encoder,
- const struct dw_hdmi_plat_data *plat_data)
+ struct dw_hdmi_plat_data *plat_data)
{
struct dw_hdmi *hdmi;
int ret;
@@ -3670,9 +4809,13 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
if (ret) {
dw_hdmi_remove(hdmi);
+ DRM_ERROR("Failed to initialize bridge with drm\n");
return ERR_PTR(ret);
}
+ if (!hdmi->next_bridge)
+ plat_data->connector = &hdmi->connector;
+
return hdmi;
}
EXPORT_SYMBOL_GPL(dw_hdmi_bind);
@@ -3683,9 +4826,87 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi)
}
EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
+static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi)
+{
+ if (hdmi_readb(hdmi, HDMI_IH_MUTE)) {
+ initialize_hdmi_ih_mutes(hdmi);
+ /* unmute cec irq */
+ hdmi_writeb(hdmi, 0x68, HDMI_IH_MUTE_CEC_STAT0);
+
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+ HDMI_PHY_I2CM_INT_ADDR);
+
+ hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+ HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+ HDMI_PHY_I2CM_CTLINT_ADDR);
+
+ if (!hdmi->next_bridge) {
+ hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+ HDMI_PHY_POL0);
+ hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+ hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+ HDMI_IH_PHY_STAT0_RX_SENSE),
+ HDMI_IH_MUTE_PHY_STAT0);
+ }
+ }
+}
+
+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);
+
+ 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);
+ if (!hdmi)
+ return;
+
+ pinctrl_pm_select_default_state(hdmi->dev);
+ mutex_lock(&hdmi->mutex);
+ dw_hdmi_reg_initial(hdmi);
+ if (hdmi->i2c)
+ dw_hdmi_i2c_init(hdmi);
+ if (hdmi->irq)
+ enable_irq(hdmi->irq);
+ /*
+ * HDMI status maybe incorrect in the following condition:
+ * HDMI plug in -> system sleep -> HDMI plug out -> system wake up.
+ * At this time, cat /sys/class/drm/card 0-HDMI-A-1/status is connected.
+ * There is no hpd interrupt, because HDMI is powerdown during suspend.
+ * So we need check the current HDMI status in this case.
+ */
+ if (hdmi->connector.status == connector_status_connected) {
+ if (hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data) ==
+ connector_status_disconnected) {
+ hdmi->hpd_state = false;
+ mod_delayed_work(hdmi->workqueue, &hdmi->work,
+ msecs_to_jiffies(20));
+ }
+ }
+ mutex_unlock(&hdmi->mutex);
}
EXPORT_SYMBOL_GPL(dw_hdmi_resume);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
index af43a0414b78..50973280048c 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
@@ -158,17 +158,8 @@
#define HDMI_FC_SPDDEVICEINF 0x1062
#define HDMI_FC_AUDSCONF 0x1063
#define HDMI_FC_AUDSSTAT 0x1064
-#define HDMI_FC_AUDSV 0x1065
-#define HDMI_FC_AUDSU 0x1066
-#define HDMI_FC_AUDSCHNLS0 0x1067
-#define HDMI_FC_AUDSCHNLS1 0x1068
-#define HDMI_FC_AUDSCHNLS2 0x1069
-#define HDMI_FC_AUDSCHNLS3 0x106A
-#define HDMI_FC_AUDSCHNLS4 0x106B
-#define HDMI_FC_AUDSCHNLS5 0x106C
-#define HDMI_FC_AUDSCHNLS6 0x106D
-#define HDMI_FC_AUDSCHNLS7 0x106E
-#define HDMI_FC_AUDSCHNLS8 0x106F
+#define HDMI_FC_AUDSCHNLS7 0x106e
+#define HDMI_FC_AUDSCHNLS8 0x106f
#define HDMI_FC_DATACH0FILL 0x1070
#define HDMI_FC_DATACH1FILL 0x1071
#define HDMI_FC_DATACH2FILL 0x1072
@@ -518,6 +509,51 @@
#define HDMI_A_PRESETUP 0x501A
#define HDMI_A_SRM_BASE 0x5020
+/* CEC Engine Registers */
+#define HDMI_CEC_CTRL 0x7D00
+#define HDMI_CEC_STAT 0x7D01
+#define HDMI_CEC_MASK 0x7D02
+#define HDMI_CEC_POLARITY 0x7D03
+#define HDMI_CEC_INT 0x7D04
+#define HDMI_CEC_ADDR_L 0x7D05
+#define HDMI_CEC_ADDR_H 0x7D06
+#define HDMI_CEC_TX_CNT 0x7D07
+#define HDMI_CEC_RX_CNT 0x7D08
+#define HDMI_CEC_TX_DATA0 0x7D10
+#define HDMI_CEC_TX_DATA1 0x7D11
+#define HDMI_CEC_TX_DATA2 0x7D12
+#define HDMI_CEC_TX_DATA3 0x7D13
+#define HDMI_CEC_TX_DATA4 0x7D14
+#define HDMI_CEC_TX_DATA5 0x7D15
+#define HDMI_CEC_TX_DATA6 0x7D16
+#define HDMI_CEC_TX_DATA7 0x7D17
+#define HDMI_CEC_TX_DATA8 0x7D18
+#define HDMI_CEC_TX_DATA9 0x7D19
+#define HDMI_CEC_TX_DATA10 0x7D1a
+#define HDMI_CEC_TX_DATA11 0x7D1b
+#define HDMI_CEC_TX_DATA12 0x7D1c
+#define HDMI_CEC_TX_DATA13 0x7D1d
+#define HDMI_CEC_TX_DATA14 0x7D1e
+#define HDMI_CEC_TX_DATA15 0x7D1f
+#define HDMI_CEC_RX_DATA0 0x7D20
+#define HDMI_CEC_RX_DATA1 0x7D21
+#define HDMI_CEC_RX_DATA2 0x7D22
+#define HDMI_CEC_RX_DATA3 0x7D23
+#define HDMI_CEC_RX_DATA4 0x7D24
+#define HDMI_CEC_RX_DATA5 0x7D25
+#define HDMI_CEC_RX_DATA6 0x7D26
+#define HDMI_CEC_RX_DATA7 0x7D27
+#define HDMI_CEC_RX_DATA8 0x7D28
+#define HDMI_CEC_RX_DATA9 0x7D29
+#define HDMI_CEC_RX_DATA10 0x7D2a
+#define HDMI_CEC_RX_DATA11 0x7D2b
+#define HDMI_CEC_RX_DATA12 0x7D2c
+#define HDMI_CEC_RX_DATA13 0x7D2d
+#define HDMI_CEC_RX_DATA14 0x7D2e
+#define HDMI_CEC_RX_DATA15 0x7D2f
+#define HDMI_CEC_LOCK 0x7D30
+#define HDMI_CEC_WKUPCTRL 0x7D31
+
/* I2C Master Registers (E-DDC) */
#define HDMI_I2CM_SLAVE 0x7E00
#define HDMI_I2CM_ADDRESS 0x7E01
@@ -538,6 +574,7 @@
#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10
#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11
#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12
+#define HDMI_I2CM_SDA_HOLD 0x7E13
enum {
/* PRODUCT_ID0 field values */
@@ -851,6 +888,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,
@@ -859,9 +900,6 @@ enum {
HDMI_FC_DATAUTO0_VSD_MASK = 0x08,
HDMI_FC_DATAUTO0_VSD_OFFSET = 3,
-/* FC_DATAUTO3 field values */
- HDMI_FC_DATAUTO3_GCP_AUTO = 0x04,
-
/* PHY_CONF0 field values */
HDMI_PHY_CONF0_PDZ_MASK = 0x80,
HDMI_PHY_CONF0_PDZ_OFFSET = 7,
@@ -1097,6 +1135,11 @@ enum {
HDMI_I2CM_CTLINT_NAC_MASK = 0x40,
HDMI_I2CM_CTLINT_ARB_POL = 0x8,
HDMI_I2CM_CTLINT_ARB_MASK = 0x4,
+
+/* I2CM_DIV field values */
+ HDMI_I2CM_DIV_FAST_STD_MODE = 0x8,
+ HDMI_I2CM_DIV_FAST_MODE = 0x8,
+ HDMI_I2CM_DIV_STD_MODE = 0,
};
/*
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 341550199111..140e7be14236 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -4,21 +4,31 @@
*/
#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/regmap.h>
-#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.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"
+#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 +40,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,34 +68,174 @@
#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,
+};
+
+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_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 clk *ref_clk;
+ struct dw_hdmi_plat_data *plat_data;
+ struct clk *aud_clk;
+ struct clk *phyref_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 regulator *avdd_0v9;
- struct regulator *avdd_1v8;
+ struct dw_hdmi_qp *hdmi_qp;
+
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;
+
+ u8 max_frl_rate_per_lane;
+ u8 max_lanes;
+ 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)
{
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
@@ -90,88 +243,195 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
return container_of(rkencoder, struct rockchip_hdmi, encoder);
}
+/*
+ * There are some rates that would be ranged for better clock jitter at
+ * Chrome OS tree, like 25.175Mhz would range to 25.170732Mhz. But due
+ * to the clock is aglined to KHz in struct drm_display_mode, this would
+ * bring some inaccurate error if we still run the compute_n math, so
+ * let's just code an const table for it until we can actually get the
+ * right clock rate.
+ */
+static const struct dw_hdmi_audio_tmds_n rockchip_werid_tmds_n_table[] = {
+ /* 25176471 for 25.175 MHz = 428000000 / 17. */
+ { .tmds = 25177000, .n_32k = 4352, .n_44k1 = 14994, .n_48k = 6528, },
+ /* 57290323 for 57.284 MHz */
+ { .tmds = 57291000, .n_32k = 3968, .n_44k1 = 4557, .n_48k = 5952, },
+ /* 74437500 for 74.44 MHz = 297750000 / 4 */
+ { .tmds = 74438000, .n_32k = 8192, .n_44k1 = 18816, .n_48k = 4096, },
+ /* 118666667 for 118.68 MHz */
+ { .tmds = 118667000, .n_32k = 4224, .n_44k1 = 5292, .n_48k = 6336, },
+ /* 121714286 for 121.75 MHz */
+ { .tmds = 121715000, .n_32k = 4480, .n_44k1 = 6174, .n_48k = 6272, },
+ /* 136800000 for 136.75 MHz */
+ { .tmds = 136800000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+ /* End of table */
+ { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
+};
+
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
{
- 27000000, {
- { 0x00b3, 0x0000},
- { 0x2153, 0x0000},
- { 0x40f3, 0x0000}
- },
- }, {
- 36000000, {
- { 0x00b3, 0x0000},
- { 0x2153, 0x0000},
- { 0x40f3, 0x0000}
- },
- }, {
- 40000000, {
- { 0x00b3, 0x0000},
- { 0x2153, 0x0000},
- { 0x40f3, 0x0000}
- },
- }, {
- 54000000, {
- { 0x0072, 0x0001},
- { 0x2142, 0x0001},
- { 0x40a2, 0x0001},
- },
- }, {
- 65000000, {
- { 0x0072, 0x0001},
- { 0x2142, 0x0001},
- { 0x40a2, 0x0001},
- },
- }, {
- 66000000, {
- { 0x013e, 0x0003},
- { 0x217e, 0x0002},
- { 0x4061, 0x0002}
- },
- }, {
- 74250000, {
- { 0x0072, 0x0001},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
- },
- }, {
- 83500000, {
- { 0x0072, 0x0001},
- },
- }, {
- 108000000, {
- { 0x0051, 0x0002},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
- },
- }, {
- 106500000, {
- { 0x0051, 0x0002},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
- },
- }, {
- 146250000, {
- { 0x0051, 0x0002},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
- },
- }, {
- 148500000, {
- { 0x0051, 0x0003},
- { 0x214c, 0x0003},
- { 0x4064, 0x0003}
- },
- }, {
+ 30666000, {
+ { 0x00b3, 0x0000 },
+ { 0x2153, 0x0000 },
+ { 0x40f3, 0x0000 },
+ },
+ }, {
+ 36800000, {
+ { 0x00b3, 0x0000 },
+ { 0x2153, 0x0000 },
+ { 0x40a2, 0x0001 },
+ },
+ }, {
+ 46000000, {
+ { 0x00b3, 0x0000 },
+ { 0x2142, 0x0001 },
+ { 0x40a2, 0x0001 },
+ },
+ }, {
+ 61333000, {
+ { 0x0072, 0x0001 },
+ { 0x2142, 0x0001 },
+ { 0x40a2, 0x0001 },
+ },
+ }, {
+ 73600000, {
+ { 0x0072, 0x0001 },
+ { 0x2142, 0x0001 },
+ { 0x4061, 0x0002 },
+ },
+ }, {
+ 92000000, {
+ { 0x0072, 0x0001 },
+ { 0x2145, 0x0002 },
+ { 0x4061, 0x0002 },
+ },
+ }, {
+ 122666000, {
+ { 0x0051, 0x0002 },
+ { 0x2145, 0x0002 },
+ { 0x4061, 0x0002 },
+ },
+ }, {
+ 147200000, {
+ { 0x0051, 0x0002 },
+ { 0x2145, 0x0002 },
+ { 0x4064, 0x0003 },
+ },
+ }, {
+ 184000000, {
+ { 0x0051, 0x0002 },
+ { 0x214c, 0x0003 },
+ { 0x4064, 0x0003 },
+ },
+ }, {
+ 226666000, {
+ { 0x0040, 0x0003 },
+ { 0x214c, 0x0003 },
+ { 0x4064, 0x0003 },
+ },
+ }, {
+ 272000000, {
+ { 0x0040, 0x0003 },
+ { 0x214c, 0x0003 },
+ { 0x5a64, 0x0003 },
+ },
+ }, {
340000000, {
{ 0x0040, 0x0003 },
{ 0x3b4c, 0x0003 },
{ 0x5a64, 0x0003 },
},
- }, {
+ }, {
+ 600000000, {
+ { 0x1a40, 0x0003 },
+ { 0x3b4c, 0x0003 },
+ { 0x5a64, 0x0003 },
+ },
+ }, {
+ ~0UL, {
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ },
+ }
+};
+
+static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
+ {
+ 30666000, {
+ { 0x00b7, 0x0000 },
+ { 0x2157, 0x0000 },
+ { 0x40f7, 0x0000 },
+ },
+ }, {
+ 92000000, {
+ { 0x00b7, 0x0000 },
+ { 0x2143, 0x0001 },
+ { 0x40a3, 0x0001 },
+ },
+ }, {
+ 184000000, {
+ { 0x0073, 0x0001 },
+ { 0x2146, 0x0002 },
+ { 0x4062, 0x0002 },
+ },
+ }, {
+ 340000000, {
+ { 0x0052, 0x0003 },
+ { 0x214d, 0x0003 },
+ { 0x4065, 0x0003 },
+ },
+ }, {
+ 600000000, {
+ { 0x0041, 0x0003 },
+ { 0x3b4d, 0x0003 },
+ { 0x5a65, 0x0003 },
+ },
+ }, {
+ ~0UL, {
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ },
+ }
+};
+
+static const struct dw_hdmi_mpll_config rockchip_rk3288w_mpll_cfg_420[] = {
+ {
+ 30666000, {
+ { 0x00b7, 0x0000 },
+ { 0x2157, 0x0000 },
+ { 0x40f7, 0x0000 },
+ },
+ }, {
+ 92000000, {
+ { 0x00b7, 0x0000 },
+ { 0x2143, 0x0001 },
+ { 0x40a3, 0x0001 },
+ },
+ }, {
+ 184000000, {
+ { 0x0073, 0x0001 },
+ { 0x2146, 0x0002 },
+ { 0x4062, 0x0002 },
+ },
+ }, {
+ 340000000, {
+ { 0x0052, 0x0003 },
+ { 0x214d, 0x0003 },
+ { 0x4065, 0x0003 },
+ },
+ }, {
+ 600000000, {
+ { 0x0040, 0x0003 },
+ { 0x3b4c, 0x0003 },
+ { 0x5a65, 0x0003 },
+ },
+ }, {
~0UL, {
- { 0x00a0, 0x000a },
- { 0x2001, 0x000f },
- { 0x4002, 0x000f },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
},
}
};
@@ -179,198 +439,2088 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
/* pixelclk bpp8 bpp10 bpp12 */
{
- 40000000, { 0x0018, 0x0018, 0x0018 },
- }, {
- 65000000, { 0x0028, 0x0028, 0x0028 },
- }, {
- 66000000, { 0x0038, 0x0038, 0x0038 },
- }, {
- 74250000, { 0x0028, 0x0038, 0x0038 },
- }, {
- 83500000, { 0x0028, 0x0038, 0x0038 },
- }, {
- 146250000, { 0x0038, 0x0038, 0x0038 },
- }, {
- 148500000, { 0x0000, 0x0038, 0x0038 },
- }, {
600000000, { 0x0000, 0x0000, 0x0000 },
- }, {
+ }, {
~0UL, { 0x0000, 0x0000, 0x0000},
}
};
-static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
+static 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},
- { ~0UL, 0x0000, 0x0000, 0x0000}
+ { 594000000, 0x8039, 0x0000, 0x019d},
+ { ~0UL, 0x0000, 0x0000, 0x0000},
+ { ~0UL, 0x0000, 0x0000, 0x0000},
};
-static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
+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)
{
- struct device_node *np = hdmi->dev->of_node;
+ 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;
+ }
+}
- hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
- if (IS_ERR(hdmi->regmap)) {
- DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
- return PTR_ERR(hdmi->regmap);
+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);
+
+ 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_dbg(hdmi->dev, "use tmds mode\n");
+ hdmi->link_cfg.frl_mode = false;
+ return;
+ }
+#if 0
+ 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;
+ }
+#endif
+}
+
+#if 0
+/////////////////////////////////////////////////////////////////////////////////////
+
+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_dbg(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);
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+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)
+{
+ int ret, val, phy_table_size;
+ u32 *phy_config;
+ struct device_node *np = hdmi->dev->of_node;
+
+ hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(hdmi->regmap)) {
+ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
+ 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->phyref_clk = devm_clk_get(hdmi->dev, "vpll");
+ if (PTR_ERR(hdmi->phyref_clk) == -ENOENT)
+ hdmi->phyref_clk = devm_clk_get(hdmi->dev, "ref");
+
+ if (PTR_ERR(hdmi->phyref_clk) == -ENOENT) {
+ hdmi->phyref_clk = NULL;
+ } else if (PTR_ERR(hdmi->phyref_clk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(hdmi->phyref_clk)) {
+ DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
+ return PTR_ERR(hdmi->phyref_clk);
+ }
+
+ hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
+ if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
+ hdmi->grf_clk = NULL;
+ } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(hdmi->grf_clk)) {
+ DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
+ return PTR_ERR(hdmi->grf_clk);
+ }
+
+ 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");
+
+ if (of_get_property(np, "rockchip,phy-table", &val)) {
+ phy_config = kmalloc(val, GFP_KERNEL);
+ if (!phy_config) {
+ /* use default table when kmalloc failed. */
+ dev_err(hdmi->dev, "kmalloc phy table failed\n");
+
+ return -ENOMEM;
+ }
+ phy_table_size = val / 16;
+ of_property_read_u32_array(np, "rockchip,phy-table",
+ phy_config, val / sizeof(u32));
+ ret = rockchip_hdmi_update_phy_table(hdmi, phy_config,
+ phy_table_size);
+ if (ret) {
+ kfree(phy_config);
+ return ret;
+ }
+ kfree(phy_config);
+ } else {
+ dev_dbg(hdmi->dev, "use default hdmi phy table\n");
+ }
+
+ return 0;
+}
+
+static enum drm_mode_status
+dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdm, void *data,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct rockchip_hdmi *hdmi = data;
+ enum drm_mode_status status = MODE_OK;
+
+ /*
+ * 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 &&
+ info->max_tmds_clock < 340000 &&
+ !drm_mode_is_420(info, mode))
+ return MODE_BAD;
+
+ if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 &&
+ !drm_mode_is_420(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;
+#if 0
+ 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;
+ }
+ }
+#endif
+ /*
+ * 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 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->phyref_clk,
+ crtc->state->adjusted_mode.crtc_clock * 1000);
+
+ if (hdmi->chip_data->lcdsel_grf_reg < 0)
+ return;
+
+ 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;
+
+ ret = clk_prepare_enable(hdmi->grf_clk);
+ if (ret < 0) {
+ DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
+ return;
+ }
+
+ ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
+ if (ret != 0)
+ DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
+
+ 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 void rk3588_set_link_mode(struct rockchip_hdmi *hdmi)
+{
+ int val;
+ bool is_hdmi0;
+
+ 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;
+
+ 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_ycbcr444_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;
+ }
+ }
+
+ dev_info(hdmi->dev, "%s color_format: %d\n", __func__, *color_format);
+ 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;
+ int eotf;
+ 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 = 0;
+
+ dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
+ &colorformat,
+ &output_mode, &bus_format, &bus_width,
+ &hdmi->enc_out_encoding, &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;
+
+#if 0
+ 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;
+ }
+#endif
+
+ 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;
+}
+
+#if 0
+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;
+};
+#endif
+
+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;
+
+ printk("%s vp%d\n", __func__, hdmi->vp_id);
+ 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;
+
+ 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;
+ }
+
+ 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);
+}
- hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
- if (!hdmi->ref_clk)
- hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");
+static void
+dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector,
+ void *data)
+{
+ struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
- if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (IS_ERR(hdmi->ref_clk)) {
- DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
- return PTR_ERR(hdmi->ref_clk);
+ if (hdmi->color_depth_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->color_depth_property);
+ hdmi->color_depth_property = NULL;
}
- hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
- if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
- hdmi->grf_clk = NULL;
- } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (IS_ERR(hdmi->grf_clk)) {
- DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
- return PTR_ERR(hdmi->grf_clk);
+ if (hdmi->hdmi_output_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->hdmi_output_property);
+ hdmi->hdmi_output_property = NULL;
}
- hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
- if (IS_ERR(hdmi->avdd_0v9))
- return PTR_ERR(hdmi->avdd_0v9);
+ if (hdmi->colordepth_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->colordepth_capacity);
+ hdmi->colordepth_capacity = NULL;
+ }
- hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
- if (IS_ERR(hdmi->avdd_1v8))
- return PTR_ERR(hdmi->avdd_1v8);
+ if (hdmi->outputmode_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->outputmode_capacity);
+ hdmi->outputmode_capacity = NULL;
+ }
- return 0;
-}
+ if (hdmi->quant_range) {
+ drm_property_destroy(connector->dev,
+ hdmi->quant_range);
+ hdmi->quant_range = NULL;
+ }
-static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode)
-{
- struct rockchip_hdmi *hdmi = data;
- const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
- int pclk = mode->clock * 1000;
- bool exact_match = hdmi->plat_data->phy_force_vendor;
- int i;
+ if (hdmi->hdr_panel_metadata_property) {
+ drm_property_destroy(connector->dev,
+ hdmi->hdr_panel_metadata_property);
+ hdmi->hdr_panel_metadata_property = NULL;
+ }
- if (hdmi->ref_clk) {
- int rpclk = clk_round_rate(hdmi->ref_clk, pclk);
+ 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 (abs(rpclk - pclk) > pclk / 1000)
- return MODE_NOCLOCK;
+ if (hdmi->output_hdmi_dvi) {
+ drm_property_destroy(connector->dev,
+ hdmi->output_hdmi_dvi);
+ hdmi->output_hdmi_dvi = NULL;
}
- for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
- /*
- * For vendor specific phys force an exact match of the pixelclock
- * to preserve the original behaviour of the driver.
- */
- if (exact_match && pclk == mpll_cfg[i].mpixelclock)
- return MODE_OK;
- /*
- * The Synopsys phy can work with pixelclocks up to the value given
- * in the corresponding mpll_cfg entry.
- */
- if (!exact_match && pclk <= mpll_cfg[i].mpixelclock)
- return MODE_OK;
+ if (hdmi->output_type_capacity) {
+ drm_property_destroy(connector->dev,
+ hdmi->output_type_capacity);
+ hdmi->output_type_capacity = NULL;
}
- return MODE_BAD;
+ if (hdmi->user_split_mode_prop) {
+ drm_property_destroy(connector->dev,
+ hdmi->user_split_mode_prop);
+ hdmi->user_split_mode_prop = NULL;
+ }
}
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
+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;
+ }
-static bool
-dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
-{
- return true;
+ DRM_ERROR("Unknown property [PROP:%d:%s]\n",
+ property->base.id, property->name);
+
+ return -EINVAL;
}
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
+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 = to_rockchip_hdmi(encoder);
+ 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 | info->edid_hdmi_ycbcr444_dc_modes) & DRM_EDID_HDMI_DC_30)
+ *val |= BIT(RK_IF_DEPTH_10);
+ if ((info->edid_hdmi_rgb444_dc_modes | info->edid_hdmi_ycbcr444_dc_modes) & DRM_EDID_HDMI_DC_36)
+ *val |= BIT(RK_IF_DEPTH_12);
+ if ((info->edid_hdmi_rgb444_dc_modes | info->edid_hdmi_ycbcr444_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;
+ }
- clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
+ DRM_ERROR("Unknown property [PROP:%d:%s]\n",
+ property->base.id, property->name);
+
+ return -EINVAL;
}
-static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
+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 void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adj)
{
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
- u32 val;
- int ret;
+ struct drm_crtc *crtc;
+ struct rockchip_crtc_state *s;
- if (hdmi->chip_data->lcdsel_grf_reg < 0)
+ if (!encoder->crtc)
return;
+ crtc = encoder->crtc;
- ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
- if (ret)
- val = hdmi->chip_data->lcdsel_lit;
- else
- val = hdmi->chip_data->lcdsel_big;
-
- ret = clk_prepare_enable(hdmi->grf_clk);
- if (ret < 0) {
- DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
+ if (!crtc->state)
return;
- }
-
- ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
- if (ret != 0)
- DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
+ s = to_rockchip_crtc_state(crtc->state);
- clk_disable_unprepare(hdmi->grf_clk);
- DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
- ret ? "LIT" : "BIG");
-}
+ if (!s)
+ return;
-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);
+ if (hdmi->is_hdmi_qp) {
+ /*s->dsc_enable = 0;
+ if (hdmi->link_cfg.dsc_mode)
+ dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state); */
- s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
- s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+ phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+ }
- return 0;
+ clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000);
}
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,
.enable = dw_hdmi_rockchip_encoder_enable,
.disable = dw_hdmi_rockchip_encoder_disable,
.atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
+ .mode_set = dw_hdmi_rockchip_encoder_mode_set,
};
-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 +2587,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,
@@ -458,6 +2692,8 @@ static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
.phy_ops = &rk3228_hdmi_phy_ops,
.phy_name = "inno_dw_hdmi_phy2",
.phy_force_vendor = true,
+ .max_tmdsclk = 371250,
+ .ycbcr_420_allowed = true,
};
static struct rockchip_hdmi_chip_data rk3288_chip_data = {
@@ -469,9 +2705,13 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
+ .mpll_cfg_420 = rockchip_rk3288w_mpll_cfg_420,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
.phy_data = &rk3288_chip_data,
+ .tmds_n_table = rockchip_werid_tmds_n_table,
+ .unsupported_yuv_input = true,
+ .ycbcr_420_allowed = true,
};
static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = {
@@ -496,6 +2736,24 @@ static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
.phy_name = "inno_dw_hdmi_phy2",
.phy_force_vendor = true,
.use_drm_infoframe = true,
+ .max_tmdsclk = 371250,
+ .ycbcr_420_allowed = true,
+};
+
+static struct rockchip_hdmi_chip_data rk3368_chip_data = {
+ .lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3368_hdmi_drv_data = {
+ .mode_valid = dw_hdmi_rockchip_mode_valid,
+ .mpll_cfg = rockchip_mpll_cfg,
+ .mpll_cfg_420 = rockchip_mpll_cfg_420,
+ .cur_ctr = rockchip_cur_ctr,
+ .phy_config = rockchip_phy_config,
+ .phy_data = &rk3368_chip_data,
+ .unsupported_deep_color = true,
+ .max_tmdsclk = 340000,
+ .ycbcr_420_allowed = true,
};
static struct rockchip_hdmi_chip_data rk3399_chip_data = {
@@ -507,22 +2765,51 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
+ .mpll_cfg_420 = rockchip_mpll_cfg_420,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
.phy_data = &rk3399_chip_data,
.use_drm_infoframe = true,
+ .ycbcr_420_allowed = true,
};
static struct rockchip_hdmi_chip_data rk3568_chip_data = {
.lcdsel_grf_reg = -1,
+ .ddc_en_reg = RK3568_GRF_VO_CON1,
};
static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
+ .mpll_cfg_420 = rockchip_mpll_cfg_420,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
.phy_data = &rk3568_chip_data,
+ .ycbcr_420_allowed = true,
+ .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,
};
@@ -536,12 +2823,19 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
{ .compatible = "rockchip,rk3328-dw-hdmi",
.data = &rk3328_hdmi_drv_data
},
+ {
+ .compatible = "rockchip,rk3368-dw-hdmi",
+ .data = &rk3368_hdmi_drv_data
+ },
{ .compatible = "rockchip,rk3399-dw-hdmi",
.data = &rk3399_hdmi_drv_data
},
{ .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);
@@ -550,45 +2844,99 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
void *data)
{
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 dw_hdmi_plat_data *plat_data;
+ 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->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_link_cfg = dw_hdmi_rockchip_get_link_cfg;
+ plat_data->set_grf_cfg = rk3588_set_grf_cfg;
+ plat_data->dclk_set = dw_hdmi_dclk_set;
plat_data->priv_data = hdmi;
- 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);
+ plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
- /*
- * 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;
+ 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) {
+
+ /*
+ * 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 (!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);
+ 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 +2945,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 = clk_prepare_enable(hdmi->hpd_clk);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret);
return ret;
}
- ret = regulator_enable(hdmi->avdd_0v9);
+ ret = clk_prepare_enable(hdmi->hclk_vo1);
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 hclk_vo1: %d\n", ret);
+ return ret;
}
- ret = regulator_enable(hdmi->avdd_1v8);
+ ret = clk_prepare_enable(hdmi->earc_clk);
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 earc_clk: %d\n", ret);
+ return ret;
}
- ret = clk_prepare_enable(hdmi->ref_clk);
+ ret = clk_prepare_enable(hdmi->hdmitx_ref);
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 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,32 +2990,112 @@ 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->phyref_clk);
+ if (ret) {
+ DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
+ ret);
+ return ret;
+ }
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+ ret = clk_prepare_enable(hdmi->hclk_vio);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n",
+ ret);
+ return ret;
+ }
- /*
- * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
- * which would have called the encoder cleanup. Do it manually.
- */
- if (IS_ERR(hdmi->hdmi)) {
- ret = PTR_ERR(hdmi->hdmi);
- goto err_bind;
+ ret = clk_prepare_enable(hdmi->hclk_vop);
+ if (ret) {
+ dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n",
+ ret);
+ return ret;
}
- return 0;
+ 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) {
+ 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->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;
+ }
-err_bind:
- drm_encoder_cleanup(encoder);
- clk_disable_unprepare(hdmi->ref_clk);
-err_clk:
- regulator_disable(hdmi->avdd_1v8);
-err_avdd_1v8:
- regulator_disable(hdmi->avdd_0v9);
-err_avdd_0v9:
return ret;
}
@@ -666,12 +3104,24 @@ 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);
- drm_encoder_cleanup(&hdmi->encoder.encoder);
- clk_disable_unprepare(hdmi->ref_clk);
+ if (hdmi->is_hdmi_qp) {
+ cancel_delayed_work(&hdmi->work);
+ flush_workqueue(hdmi->workqueue);
+ destroy_workqueue(hdmi->workqueue);
+ }
- regulator_disable(hdmi->avdd_1v8);
- regulator_disable(hdmi->avdd_0v9);
+ if (hdmi->is_hdmi_qp)
+ dw_hdmi_qp_unbind(hdmi->hdmi_qp);
+ else
+ dw_hdmi_unbind(hdmi->hdmi);
+ clk_disable_unprepare(hdmi->aud_clk);
+ clk_disable_unprepare(hdmi->phyref_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);
}
static const struct component_ops dw_hdmi_rockchip_ops = {
@@ -681,30 +3131,132 @@ 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_remove(struct platform_device *pdev)
+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 int dw_hdmi_rockchip_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+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)
+static int 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,
+ .remove = 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..67eb3c113cf2 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -6,12 +6,15 @@
#ifndef __DW_HDMI__
#define __DW_HDMI__
+#include <drm/drm_property.h>
#include <sound/hdmi-codec.h>
+#include <media/cec.h>
struct drm_display_info;
struct drm_display_mode;
struct drm_encoder;
struct dw_hdmi;
+struct dw_hdmi_qp;
struct platform_device;
/**
@@ -92,6 +95,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 +122,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 +142,51 @@ 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;
unsigned int output_port;
-
+ unsigned int disable_cec;
+ 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,29 +195,55 @@ struct dw_hdmi_plat_data {
void *priv_data;
/* Platform-specific mode validation (optional). */
- enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data,
+ enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi,
+ void *data,
const struct drm_display_info *info,
const struct drm_display_mode *mode);
- /* Platform-specific audio enable/disable (optional) */
- void (*enable_audio)(struct dw_hdmi *hdmi, int channel,
- int width, int rate, int non_pcm);
- void (*disable_audio)(struct dw_hdmi *hdmi);
-
/* 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;
+ const struct dw_hdmi_audio_tmds_n *tmds_n_table;
+
+ /* 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;
+ 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,
@@ -170,16 +252,15 @@ void dw_hdmi_remove(struct dw_hdmi *hdmi);
void dw_hdmi_unbind(struct dw_hdmi *hdmi);
struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
struct drm_encoder *encoder,
- const struct dw_hdmi_plat_data *plat_data);
+ 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);
int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
struct device *codec_dev);
-void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm);
-void dw_hdmi_set_sample_width(struct dw_hdmi *hdmi, unsigned int width);
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt);
void dw_hdmi_set_channel_status(struct dw_hdmi *hdmi, u8 *channel_status);
@@ -205,6 +286,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);
--
Armbian