build/patch/kernel/rk322x-dev/01-linux-1002-rockchip-drm-wip.patch
paolo 9080d453e0 Moving rk322x-current to linux 5.9.y
Removed default-sample-phase property for base rk322x-box device tree
Enabled spdif out for rk322x-current and -dev flavours
Removed reserved node in device tree, u-boot v2020.10 and OPTEE autoconfigure reserved zones automatically
2020-11-13 19:40:52 +00:00

5389 lines
180 KiB
Diff

From dd7efcfd32481cecf647067cedd23c1d799daead Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:47 +0000
Subject: [PATCH] phy/rockchip: inno-hdmi: use correct vco_div_5 macro on
rk3328
inno_hdmi_phy_rk3328_clk_set_rate() is using the RK3228 macro
when configuring vco_div_5 on RK3328.
Fix this by using correct vco_div_5 macro for RK3328.
Fixes: 53706a116863 ("phy: add Rockchip Innosilicon hdmi phy")
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index b1a9ff0131eb..f784408926d6 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -797,8 +797,8 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
RK3328_PRE_PLL_POWER_DOWN);
/* Configure pre-pll */
- inno_update_bits(inno, 0xa0, RK3228_PCLK_VCO_DIV_5_MASK,
- RK3228_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
+ inno_update_bits(inno, 0xa0, RK3328_PCLK_VCO_DIV_5_MASK,
+ RK3328_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
inno_write(inno, 0xa1, RK3328_PRE_PLL_PRE_DIV(cfg->prediv));
val = RK3328_SPREAD_SPECTRUM_MOD_DISABLE;
From 3b35058e573c01959e413e4899f3c6a6be8058da Mon Sep 17 00:00:00 2001
From: Zheng Yang <zhengyang@rock-chips.com>
Date: Wed, 8 Jan 2020 21:07:48 +0000
Subject: [PATCH] phy/rockchip: inno-hdmi: round fractal pixclock in rk3328
recalc_rate
inno_hdmi_phy_rk3328_clk_recalc_rate() is returning a rate not found
in the pre pll config table when the fractal divider is used.
This can prevent proper power_on because a tmdsclock for the new rate
is not found in the pre pll config table.
Fix this by saving and returning a rounded pixel rate that exist
in the pre pll config table.
Fixes: 53706a116863 ("phy: add Rockchip Innosilicon hdmi phy")
Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index f784408926d6..4f000942c824 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -752,10 +752,12 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2));
}
- inno->pixclock = vco;
- dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock);
+ inno->pixclock = DIV_ROUND_CLOSEST((unsigned long)vco, 1000) * 1000;
- return vco;
+ dev_dbg(inno->dev, "%s rate %lu vco %llu\n",
+ __func__, inno->pixclock, vco);
+
+ return inno->pixclock;
}
static long inno_hdmi_phy_rk3328_clk_round_rate(struct clk_hw *hw,
From 893babbe917db2895ead295b8897c74f40396afe Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:48 +0000
Subject: [PATCH] phy/rockchip: inno-hdmi: remove unused no_c from rk3328
recalc_rate
no_c is not used in any calculation, lets remove it.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 4f000942c824..05a5362c1f73 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -721,7 +721,7 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
{
struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
unsigned long frac;
- u8 nd, no_a, no_b, no_c, no_d;
+ u8 nd, no_a, no_b, no_d;
u64 vco;
u16 nf;
@@ -744,9 +744,6 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
no_b = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_B_MASK;
no_b >>= RK3328_PRE_PLL_PCLK_DIV_B_SHIFT;
no_b += 2;
- no_c = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_C_MASK;
- no_c >>= RK3328_PRE_PLL_PCLK_DIV_C_SHIFT;
- no_c = 1 << no_c;
no_d = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_D_MASK;
do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2));
From 8674a04f14c6bb56b84dcf934f4f921c213e62e5 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:48 +0000
Subject: [PATCH] phy/rockchip: inno-hdmi: do not power on rk3328 post pll on
reg write
inno_write is used to configure 0xaa reg, that also hold the
POST_PLL_POWER_DOWN bit.
When POST_PLL_REFCLK_SEL_TMDS is configured the power down bit is not
taken into consideration.
Fix this by keeping the power down bit until configuration is complete.
Also reorder the reg write order for consistency.
Fixes: 53706a116863 ("phy: add Rockchip Innosilicon hdmi phy")
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 05a5362c1f73..5e07346af27c 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -1054,9 +1054,10 @@ inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
inno_write(inno, 0xac, RK3328_POST_PLL_FB_DIV_7_0(cfg->fbdiv));
if (cfg->postdiv == 1) {
- inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS);
inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) |
RK3328_POST_PLL_PRE_DIV(cfg->prediv));
+ inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS |
+ RK3328_POST_PLL_POWER_DOWN);
} else {
v = (cfg->postdiv / 2) - 1;
v &= RK3328_POST_PLL_POST_DIV_MASK;
@@ -1064,7 +1065,8 @@ inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) |
RK3328_POST_PLL_PRE_DIV(cfg->prediv));
inno_write(inno, 0xaa, RK3328_POST_PLL_POST_DIV_ENABLE |
- RK3328_POST_PLL_REFCLK_SEL_TMDS);
+ RK3328_POST_PLL_REFCLK_SEL_TMDS |
+ RK3328_POST_PLL_POWER_DOWN);
}
for (v = 0; v < 14; v++)
From e6e89b9198928abb1fc417985cce8000b2e55839 Mon Sep 17 00:00:00 2001
From: Huicong Xu <xhc@rock-chips.com>
Date: Wed, 8 Jan 2020 21:07:49 +0000
Subject: [PATCH] phy/rockchip: inno-hdmi: force set_rate on power_on
Regular 8-bit and Deep Color video formats mainly differ in TMDS rate and
not in pixel clock rate.
When the hdmiphy clock is configured with the same pixel clock rate using
clk_set_rate() the clock framework do not signal the hdmi phy driver
to set_rate when switching between 8-bit and Deep Color.
This result in pre/post pll not being re-configured when switching between
regular 8-bit and Deep Color video formats.
Fix this by calling set_rate in power_on to force pre pll re-configuration.
Signed-off-by: Huicong Xu <xhc@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 5e07346af27c..29e9a1c1e76b 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -248,6 +248,7 @@ struct inno_hdmi_phy {
struct clk_hw hw;
struct clk *phyclk;
unsigned long pixclock;
+ unsigned long tmdsclock;
};
struct pre_pll_config {
@@ -492,6 +493,8 @@ static int inno_hdmi_phy_power_on(struct phy *phy)
dev_dbg(inno->dev, "Inno HDMI PHY Power On\n");
+ inno->plat_data->clk_ops->set_rate(&inno->hw, inno->pixclock, 24000000);
+
ret = clk_prepare_enable(inno->phyclk);
if (ret)
return ret;
@@ -516,6 +519,8 @@ static int inno_hdmi_phy_power_off(struct phy *phy)
clk_disable_unprepare(inno->phyclk);
+ inno->tmdsclock = 0;
+
dev_dbg(inno->dev, "Inno HDMI PHY Power Off\n");
return 0;
@@ -635,6 +640,9 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n",
__func__, rate, tmdsclock);
+ if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
+ return 0;
+
cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
if (IS_ERR(cfg))
return PTR_ERR(cfg);
@@ -677,6 +685,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
}
inno->pixclock = rate;
+ inno->tmdsclock = tmdsclock;
return 0;
}
@@ -788,6 +797,9 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n",
__func__, rate, tmdsclock);
+ if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
+ return 0;
+
cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
if (IS_ERR(cfg))
return PTR_ERR(cfg);
@@ -827,6 +839,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
}
inno->pixclock = rate;
+ inno->tmdsclock = tmdsclock;
return 0;
}
From 43a9498fb30c743d2afe70b9d39f561910ddcb5b Mon Sep 17 00:00:00 2001
From: Algea Cao <algea.cao@rock-chips.com>
Date: Wed, 8 Jan 2020 21:07:53 +0000
Subject: [PATCH] phy/rockchip: inno-hdmi: Support more pre-pll configuration
Adding the following freq cfg in 8-bit and 10-bit color depth:
{
40000000, 65000000, 71000000, 83500000, 85750000,
88750000, 108000000, 119000000, 162000000
}
New freq has been validated by quantumdata 980.
For some freq which can't be got by only using integer freq div,
frac freq div is needed, Such as 88.75Mhz 10-bit. But The actual
freq is different from the target freq, We must try to narrow
the gap between them. RK322X only support integer freq div.
The VCO of pre-PLL must be more than 2Ghz, otherwise PLL may be
unlocked.
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Acked-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 74 ++++++++++++-------
1 file changed, 49 insertions(+), 25 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
index 29e9a1c1e76b..0c7a97352714 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -294,32 +294,56 @@ struct inno_hdmi_phy_drv_data {
const struct phy_config *phy_cfg_table;
};
+/*
+ * If only using integer freq div can't get frequency we want, frac
+ * freq div is needed. For example, pclk 88.75 Mhz and tmdsclk
+ * 110.9375 Mhz must use frac div 0xF00000. The actual frequency is different
+ * from the target frequency. Such as the tmds clock 110.9375 Mhz,
+ * the actual tmds clock we get is 110.93719 Mhz. It is important
+ * to note that RK322X platforms do not support frac div.
+ */
static const struct pre_pll_config pre_pll_cfg_table[] = {
- { 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
- { 27000000, 33750000, 1, 90, 1, 3, 3, 10, 3, 3, 4, 0, 0},
- { 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
- { 59341000, 59341000, 1, 98, 3, 1, 2, 1, 3, 3, 4, 0, 0xE6AE6B},
- { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0},
- { 59341000, 74176250, 1, 98, 0, 3, 3, 1, 3, 3, 4, 0, 0xE6AE6B},
- { 59400000, 74250000, 1, 99, 1, 2, 2, 1, 3, 3, 4, 0, 0},
- { 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xE6AE6B},
- { 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0},
- { 74176000, 92720000, 4, 494, 1, 2, 2, 1, 3, 3, 4, 0, 0x816817},
- { 74250000, 92812500, 4, 495, 1, 2, 2, 1, 3, 3, 4, 0, 0},
- {148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xE6AE6B},
- {148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0},
- {148352000, 185440000, 4, 494, 0, 2, 2, 1, 3, 2, 2, 0, 0x816817},
- {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0},
- {296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B},
- {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0},
- {296703000, 370878750, 4, 494, 1, 2, 0, 1, 3, 1, 1, 0, 0x816817},
- {297000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 0},
- {593407000, 296703500, 1, 98, 0, 1, 1, 1, 0, 2, 1, 0, 0xE6AE6B},
- {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 1, 0, 0},
- {593407000, 370879375, 4, 494, 1, 2, 0, 1, 3, 1, 1, 1, 0x816817},
- {594000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 0},
- {593407000, 593407000, 1, 98, 0, 2, 0, 1, 0, 1, 1, 0, 0xE6AE6B},
- {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0},
+ { 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
+ { 27000000, 33750000, 1, 90, 1, 3, 3, 10, 3, 3, 4, 0, 0},
+ { 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
+ { 40000000, 50000000, 1, 100, 2, 2, 2, 1, 0, 0, 15, 0, 0},
+ { 59341000, 59341000, 1, 98, 3, 1, 2, 1, 3, 3, 4, 0, 0xE6AE6B},
+ { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0},
+ { 59341000, 74176250, 1, 98, 0, 3, 3, 1, 3, 3, 4, 0, 0xE6AE6B},
+ { 59400000, 74250000, 1, 99, 1, 2, 2, 1, 3, 3, 4, 0, 0},
+ { 65000000, 65000000, 1, 130, 2, 2, 2, 1, 0, 0, 12, 0, 0},
+ { 65000000, 81250000, 3, 325, 0, 3, 3, 1, 0, 0, 10, 0, 0},
+ { 71000000, 71000000, 3, 284, 0, 3, 3, 1, 0, 0, 8, 0, 0},
+ { 71000000, 88750000, 3, 355, 0, 3, 3, 1, 0, 0, 10, 0, 0},
+ { 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xE6AE6B},
+ { 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0},
+ { 74176000, 92720000, 4, 494, 1, 2, 2, 1, 3, 3, 4, 0, 0x816817},
+ { 74250000, 92812500, 4, 495, 1, 2, 2, 1, 3, 3, 4, 0, 0},
+ { 83500000, 83500000, 2, 167, 2, 1, 1, 1, 0, 0, 6, 0, 0},
+ { 83500000, 104375000, 1, 104, 2, 1, 1, 1, 1, 0, 5, 0, 0x600000},
+ { 85750000, 85750000, 3, 343, 0, 3, 3, 1, 0, 0, 8, 0, 0},
+ { 88750000, 88750000, 3, 355, 0, 3, 3, 1, 0, 0, 8, 0, 0},
+ { 88750000, 110937500, 1, 110, 2, 1, 1, 1, 1, 0, 5, 0, 0xF00000},
+ {108000000, 108000000, 1, 90, 3, 0, 0, 1, 0, 0, 5, 0, 0},
+ {108000000, 135000000, 1, 90, 0, 2, 2, 1, 0, 0, 5, 0, 0},
+ {119000000, 119000000, 1, 119, 2, 1, 1, 1, 0, 0, 6, 0, 0},
+ {119000000, 148750000, 1, 99, 0, 2, 2, 1, 0, 0, 5, 0, 0x2AAAAA},
+ {148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xE6AE6B},
+ {148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0},
+ {148352000, 185440000, 4, 494, 0, 2, 2, 1, 3, 2, 2, 0, 0x816817},
+ {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0},
+ {162000000, 162000000, 1, 108, 0, 2, 2, 1, 0, 0, 4, 0, 0},
+ {162000000, 202500000, 1, 135, 0, 2, 2, 1, 0, 0, 5, 0, 0},
+ {296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B},
+ {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0},
+ {296703000, 370878750, 4, 494, 1, 2, 0, 1, 3, 1, 1, 0, 0x816817},
+ {297000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 0},
+ {593407000, 296703500, 1, 98, 0, 1, 1, 1, 0, 2, 1, 0, 0xE6AE6B},
+ {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 1, 0, 0},
+ {593407000, 370879375, 4, 494, 1, 2, 0, 1, 3, 1, 1, 1, 0x816817},
+ {594000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 0},
+ {593407000, 593407000, 1, 98, 0, 2, 0, 1, 0, 1, 1, 0, 0xE6AE6B},
+ {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0},
{ /* sentinel */ }
};
From d9dca0f6a52f6d013240398187c4460600abcdf4 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 3 May 2020 16:51:31 +0000
Subject: [PATCH] drm/rockchip: vop: filter modes outside 0.5% pixel clock
tolerance
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 33 +++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c80f7d9fd13f..6cbdb4672a4b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1142,6 +1142,38 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
}
+/*
+ * The VESA DMT standard specifies a 0.5% pixel clock frequency tolerance.
+ * The CVT spec reuses that tolerance in its examples.
+ */
+#define CLOCK_TOLERANCE_PER_MILLE 5
+
+static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ struct vop *vop = to_vop(crtc);
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+ long rounded_rate;
+ long lowest, highest;
+
+ if (s->output_type != DRM_MODE_CONNECTOR_HDMIA)
+ return MODE_OK;
+
+ rounded_rate = clk_round_rate(vop->dclk, mode->clock * 1000 + 999);
+ if (rounded_rate < 0)
+ return MODE_NOCLOCK;
+
+ lowest = mode->clock * (1000 - CLOCK_TOLERANCE_PER_MILLE);
+ if (rounded_rate < lowest)
+ return MODE_CLOCK_LOW;
+
+ highest = mode->clock * (1000 + CLOCK_TOLERANCE_PER_MILLE);
+ if (rounded_rate > highest)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -1512,6 +1544,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
}
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
+ .mode_valid = vop_crtc_mode_valid,
.mode_fixup = vop_crtc_mode_fixup,
.atomic_check = vop_crtc_atomic_check,
.atomic_begin = vop_crtc_atomic_begin,
From b6030ac943d755d31b69c658f498999875a0a46d Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 11:46:16 +0000
Subject: [PATCH] WIP: drm/rockchip: vop: max_output
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 5 +++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 6 ++++++
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 7 +++++++
3 files changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 6cbdb4672a4b..106b38ea12df 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1152,6 +1152,7 @@ static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct vop *vop = to_vop(crtc);
+ const struct vop_rect *max_output = &vop->data->max_output;
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
long rounded_rate;
long lowest, highest;
@@ -1171,6 +1172,10 @@ static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
if (rounded_rate > highest)
return MODE_CLOCK_HIGH;
+ if (max_output->width && max_output->height)
+ return drm_mode_validate_size(mode, max_output->width,
+ max_output->height);
+
return MODE_OK;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 4a2099cb582e..1516231bbf93 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -185,6 +185,11 @@ struct vop_win_data {
enum drm_plane_type type;
};
+struct vop_rect {
+ int width;
+ int height;
+};
+
struct vop_data {
uint32_t version;
const struct vop_intr *intr;
@@ -197,6 +202,7 @@ struct vop_data {
const struct vop_win_data *win;
unsigned int win_size;
unsigned int lut_size;
+ struct vop_rect max_output;
#define VOP_FEATURE_OUTPUT_RGB10 BIT(0)
#define VOP_FEATURE_INTERNAL_RGB BIT(1)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 80053d91a301..57c36e9207c1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -682,6 +682,7 @@ static const struct vop_intr rk3288_vop_intr = {
static const struct vop_data rk3288_vop = {
.version = VOP_VERSION(3, 1),
.feature = VOP_FEATURE_OUTPUT_RGB10,
+ .max_output = { 3840, 2160 },
.intr = &rk3288_vop_intr,
.common = &rk3288_common,
.modeset = &rk3288_modeset,
@@ -782,6 +783,7 @@ static const struct vop_misc rk3368_misc = {
static const struct vop_data rk3368_vop = {
.version = VOP_VERSION(3, 2),
+ .max_output = { 4096, 2160 },
.intr = &rk3368_vop_intr,
.common = &rk3288_common,
.modeset = &rk3288_modeset,
@@ -803,6 +805,7 @@ static const struct vop_intr rk3366_vop_intr = {
static const struct vop_data rk3366_vop = {
.version = VOP_VERSION(3, 4),
+ .max_output = { 4096, 2160 },
.intr = &rk3366_vop_intr,
.common = &rk3288_common,
.modeset = &rk3288_modeset,
@@ -909,6 +912,7 @@ static const struct vop_afbc rk3399_vop_afbc = {
static const struct vop_data rk3399_vop_big = {
.version = VOP_VERSION(3, 5),
.feature = VOP_FEATURE_OUTPUT_RGB10,
+ .max_output = { 4096, 2160 },
.intr = &rk3366_vop_intr,
.common = &rk3288_common,
.modeset = &rk3288_modeset,
@@ -935,6 +939,7 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_lit_win_yuv2yuv_data[] = {
static const struct vop_data rk3399_vop_lit = {
.version = VOP_VERSION(3, 6),
+ .max_output = { 2560, 1600 },
.intr = &rk3366_vop_intr,
.common = &rk3288_common,
.modeset = &rk3288_modeset,
@@ -955,6 +960,7 @@ static const struct vop_win_data rk3228_vop_win_data[] = {
static const struct vop_data rk3228_vop = {
.version = VOP_VERSION(3, 7),
.feature = VOP_FEATURE_OUTPUT_RGB10,
+ .max_output = { 4096, 2160 },
.intr = &rk3366_vop_intr,
.common = &rk3288_common,
.modeset = &rk3288_modeset,
@@ -1026,6 +1032,7 @@ static const struct vop_win_data rk3328_vop_win_data[] = {
static const struct vop_data rk3328_vop = {
.version = VOP_VERSION(3, 8),
.feature = VOP_FEATURE_OUTPUT_RGB10,
+ .max_output = { 4096, 2160 },
.intr = &rk3328_vop_intr,
.common = &rk3328_common,
.modeset = &rk3328_modeset,
From 7c8b58413c38c263e4a9103281a214cb3a2db69c Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:51 +0000
Subject: [PATCH] arm64: dts: rockchip: increase vop clock rate on rk3328
The VOP on RK3328 needs to run at higher rate in order to
produce a proper 3840x2160 signal.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
index bbdb19a3e85d..6547e2b4b617 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
@@ -802,8 +802,8 @@ cru: clock-controller@ff440000 {
<0>, <24000000>,
<24000000>, <24000000>,
<15000000>, <15000000>,
- <100000000>, <100000000>,
- <100000000>, <100000000>,
+ <300000000>, <100000000>,
+ <400000000>, <100000000>,
<50000000>, <100000000>,
<100000000>, <100000000>,
<50000000>, <50000000>,
From b6d211f4b212569724efed3c9eaaf06615268d61 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:49 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: allow high tmds bit rates
Prepare support for High TMDS Bit Rates used by HDMI2.0 display modes.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 23de359a1dec..cdf953850873 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -317,6 +317,8 @@ static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
{
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+ dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display);
+
return phy_power_on(hdmi->phy);
}
From 1efdc8bf4ab51829d5b086f6bc45e7f02da1170c Mon Sep 17 00:00:00 2001
From: Yakir Yang <ykk@rock-chips.com>
Date: Mon, 11 Jul 2016 19:05:39 +0800
Subject: [PATCH] drm/rockchip: dw_hdmi: adjust cklvl & txlvl for RF/EMI
Dut to the high HDMI signal voltage driver, Mickey have meet
a serious RF/EMI problem, so we decided to reduce HDMI signal
voltage to a proper value.
The default params for phy is cklvl = 20 & txlvl = 13 (RF/EMI failed)
ck: lvl = 13, term=100, vlo = 2.71, vhi=3.14, vswing = 0.43
tx: lvl = 20, term=100, vlo = 2.81, vhi=3.16, vswing = 0.35
1. We decided to reduce voltage value to lower, but VSwing still
keep high, RF/EMI have been improved but still failed.
ck: lvl = 6, term=100, vlo = 2.61, vhi=3.11, vswing = 0.50
tx: lvl = 6, term=100, vlo = 2.61, vhi=3.11, vswing = 0.50
2. We try to keep voltage value and vswing both lower, then RF/EMI
test all passed ;)
ck: lvl = 11, term= 66, vlo = 2.68, vhi=3.09, vswing = 0.40
tx: lvl = 11, term= 66, vlo = 2.68, vhi=3.09, vswing = 0.40
When we back to run HDMI different test and single-end test, we see
different test passed, but signle-end test failed. The oscilloscope
show that simgle-end clock's VL value is 1.78v (which remind LowLimit
should not lower then 2.6v).
3. That's to say there are some different between PHY document and
measure value. And according to experiment 2 results, we need to
higher clock voltage and lower data voltage, then we can keep RF/EMI
satisfied and single-end & differen test passed.
ck: lvl = 9, term=100, vlo = 2.65, vhi=3.12, vswing = 0.47
tx: lvl = 16, term=100, vlo = 2.75, vhi=3.15, vswing = 0.39
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cdf953850873..4652c0e0dcd6 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -181,7 +181,7 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
/*pixelclk symbol term vlev*/
{ 74250000, 0x8009, 0x0004, 0x0272},
- { 148500000, 0x802b, 0x0004, 0x028d},
+ { 165000000, 0x802b, 0x0004, 0x0209},
{ 297000000, 0x8039, 0x0005, 0x028d},
{ ~0UL, 0x0000, 0x0000, 0x0000}
};
From d010c041afc48d4592625e23082aa31871042847 Mon Sep 17 00:00:00 2001
From: Nickey Yang <nickey.yang@rock-chips.com>
Date: Mon, 13 Feb 2017 15:40:29 +0800
Subject: [PATCH] drm/rockchip: dw_hdmi: add phy_config for 594Mhz pixel clock
Add phy_config for 594Mhz pixel clock used for 4K@60hz
Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 4652c0e0dcd6..10c3dc521cbd 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -183,6 +183,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
{ 74250000, 0x8009, 0x0004, 0x0272},
{ 165000000, 0x802b, 0x0004, 0x0209},
{ 297000000, 0x8039, 0x0005, 0x028d},
+ { 594000000, 0x8039, 0x0000, 0x019d},
{ ~0UL, 0x0000, 0x0000, 0x0000}
};
From c4e637b2fc48c3bdb6f1ff8d5a5883e21587f051 Mon Sep 17 00:00:00 2001
From: Douglas Anderson <dianders@chromium.org>
Date: Mon, 11 Jul 2016 19:05:36 +0800
Subject: [PATCH] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
Jitter was improved by lowering the MPLL bandwidth to account for high
frequency noise in the rk3288 PLL. In each case MPLL bandwidth was
lowered only enough to get us a comfortable margin. We believe that
lowering the bandwidth like this is safe given sufficient testing.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 10c3dc521cbd..cc7675638e4f 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -160,20 +160,8 @@ 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},
}
};
From 2b9a23ae36142d8d4d9fcce63a90b9c8329ad187 Mon Sep 17 00:00:00 2001
From: Douglas Anderson <dianders@chromium.org>
Date: Mon, 11 Jul 2016 19:05:42 +0800
Subject: [PATCH] drm/rockchip: dw_hdmi: Use auto-generated tables
The previous tables for mpll_cfg and curr_ctrl were created using the
20-pages of example settings provided by the PHY vendor. Those
example settings weren't particularly dense, so there were places
where we were guessing what the settings would be for 10-bit and
12-bit (not that we use those anyway). It was also always a lot of
extra work every time we wanted to add a new clock rate since we had
to cross-reference several tables.
In <http://crosreview.com/285855> I've gone through the work to figure
out how to generate this table automatically. Let's now use the
automatically generated table and then we'll never need to look at it
again.
We only support 8-bit mode right now and only support a small number
of clock rates and and I've verified that the only 8-bit rate that was
affected was 148.5. That mode appears to have been wrong in the old
table.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Yakir Yang <ykk@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 130 +++++++++++---------
1 file changed, 69 insertions(+), 61 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cc7675638e4f..c4c158106ca4 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -79,80 +79,88 @@ struct rockchip_hdmi {
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
{
- 27000000, {
- { 0x00b3, 0x0000},
- { 0x2153, 0x0000},
- { 0x40f3, 0x0000}
+ 30666000, {
+ { 0x00b3, 0x0000 },
+ { 0x2153, 0x0000 },
+ { 0x40f3, 0x0000 },
},
- }, {
- 36000000, {
- { 0x00b3, 0x0000},
- { 0x2153, 0x0000},
- { 0x40f3, 0x0000}
+ }, {
+ 36800000, {
+ { 0x00b3, 0x0000 },
+ { 0x2153, 0x0000 },
+ { 0x40a2, 0x0001 },
},
- }, {
- 40000000, {
- { 0x00b3, 0x0000},
- { 0x2153, 0x0000},
- { 0x40f3, 0x0000}
+ }, {
+ 46000000, {
+ { 0x00b3, 0x0000 },
+ { 0x2142, 0x0001 },
+ { 0x40a2, 0x0001 },
},
- }, {
- 54000000, {
- { 0x0072, 0x0001},
- { 0x2142, 0x0001},
- { 0x40a2, 0x0001},
+ }, {
+ 61333000, {
+ { 0x0072, 0x0001 },
+ { 0x2142, 0x0001 },
+ { 0x40a2, 0x0001 },
},
- }, {
- 65000000, {
- { 0x0072, 0x0001},
- { 0x2142, 0x0001},
- { 0x40a2, 0x0001},
+ }, {
+ 73600000, {
+ { 0x0072, 0x0001 },
+ { 0x2142, 0x0001 },
+ { 0x4061, 0x0002 },
},
- }, {
- 66000000, {
- { 0x013e, 0x0003},
- { 0x217e, 0x0002},
- { 0x4061, 0x0002}
+ }, {
+ 92000000, {
+ { 0x0072, 0x0001 },
+ { 0x2145, 0x0002 },
+ { 0x4061, 0x0002 },
+ },
+ }, {
+ 122666000, {
+ { 0x0051, 0x0002 },
+ { 0x2145, 0x0002 },
+ { 0x4061, 0x0002 },
},
- }, {
- 74250000, {
- { 0x0072, 0x0001},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
+ }, {
+ 147200000, {
+ { 0x0051, 0x0002 },
+ { 0x2145, 0x0002 },
+ { 0x4064, 0x0003 },
},
- }, {
- 83500000, {
- { 0x0072, 0x0001},
+ }, {
+ 184000000, {
+ { 0x0051, 0x0002 },
+ { 0x214c, 0x0003 },
+ { 0x4064, 0x0003 },
},
- }, {
- 108000000, {
- { 0x0051, 0x0002},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
+ }, {
+ 226666000, {
+ { 0x0040, 0x0003 },
+ { 0x214c, 0x0003 },
+ { 0x4064, 0x0003 },
},
- }, {
- 106500000, {
- { 0x0051, 0x0002},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
+ }, {
+ 272000000, {
+ { 0x0040, 0x0003 },
+ { 0x214c, 0x0003 },
+ { 0x5a64, 0x0003 },
},
- }, {
- 146250000, {
- { 0x0051, 0x0002},
- { 0x2145, 0x0002},
- { 0x4061, 0x0002}
+ }, {
+ 340000000, {
+ { 0x0040, 0x0003 },
+ { 0x3b4c, 0x0003 },
+ { 0x5a64, 0x0003 },
},
- }, {
- 148500000, {
- { 0x0051, 0x0003},
- { 0x214c, 0x0003},
- { 0x4064, 0x0003}
+ }, {
+ 600000000, {
+ { 0x1a40, 0x0003 },
+ { 0x3b4c, 0x0003 },
+ { 0x5a64, 0x0003 },
},
- }, {
+ }, {
~0UL, {
- { 0x00a0, 0x000a },
- { 0x2001, 0x000f },
- { 0x4002, 0x000f },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
+ { 0x0000, 0x0000 },
},
}
};
From fd940089282dffc01cc7b45a5aef11acd59a4f9f Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:52 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: limit tmds to 340mhz
RK3228/RK3328 does not provide a stable hdmi signal at TMDS rates
above 371.25MHz (340MHz pixel clock).
Limit the pixel clock rate to 340MHz to provide a stable signal.
Also limit the pixel clock to the display reported max tmds clock.
This also enables use of pixel clocks up to 340MHz on RK3288/RK3399.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index c4c158106ca4..b62d8f4fc9a8 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -221,19 +221,11 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
- const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
- int pclk = mode->clock * 1000;
- bool valid = false;
- int i;
-
- for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
- if (pclk == mpll_cfg[i].mpixelclock) {
- valid = true;
- break;
- }
- }
+ if (mode->clock > 340000 ||
+ (info->max_tmds_clock && mode->clock > info->max_tmds_clock))
+ return MODE_CLOCK_HIGH;
- return (valid) ? MODE_OK : MODE_BAD;
+ return MODE_OK;
}
static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
From c2ac79d4d979bc7cdee94bc4ecad5946b31d2caf Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 3 May 2020 22:36:23 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: limit resolution to 3840x2160
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index b62d8f4fc9a8..6f7641fbe6cc 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -225,7 +225,7 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
(info->max_tmds_clock && mode->clock > info->max_tmds_clock))
return MODE_CLOCK_HIGH;
- return MODE_OK;
+ return drm_mode_validate_size(mode, 3840, 2160);
}
static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
From f7e97888e1b955bee4e7fc778b9434c3c3aeaa1c Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:52 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: remove unused plat_data on
rk3228/rk3328
mpll_cfg/cur_ctr/phy_config is not used when phy_force_vendor is true,
lets remove them.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 6f7641fbe6cc..cc20a83fa9b8 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -396,9 +396,6 @@ static struct rockchip_hdmi_chip_data rk3228_chip_data = {
static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
- .mpll_cfg = rockchip_mpll_cfg,
- .cur_ctr = rockchip_cur_ctr,
- .phy_config = rockchip_phy_config,
.phy_data = &rk3228_chip_data,
.phy_ops = &rk3228_hdmi_phy_ops,
.phy_name = "inno_dw_hdmi_phy2",
@@ -433,9 +430,6 @@ static struct rockchip_hdmi_chip_data rk3328_chip_data = {
static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
- .mpll_cfg = rockchip_mpll_cfg,
- .cur_ctr = rockchip_cur_ctr,
- .phy_config = rockchip_phy_config,
.phy_data = &rk3328_chip_data,
.phy_ops = &rk3328_hdmi_phy_ops,
.phy_name = "inno_dw_hdmi_phy2",
From db3a0bad2ab53b12c7dbeec032cb8f3d7ef0563d Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 8 Jan 2020 21:07:50 +0000
Subject: [PATCH] clk: rockchip: set parent rate for DCLK_VOP clock on rk3228
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/clk/rockchip/clk-rk3228.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c
index 6c39ecd3db7e..0c422e4bb213 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -393,7 +393,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
RK2928_CLKSEL_CON(29), 0, 3, DFLAGS),
DIV(0, "sclk_vop_pre", "sclk_vop_src", 0,
RK2928_CLKSEL_CON(27), 8, 8, DFLAGS),
- MUX(DCLK_VOP, "dclk_vop", mux_dclk_vop_p, 0,
+ MUX(DCLK_VOP, "dclk_vop", mux_dclk_vop_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
RK2928_CLKSEL_CON(27), 1, 1, MFLAGS),
FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
From d25ae20e096caca3783c95d14586a4357fa83449 Mon Sep 17 00:00:00 2001
From: Nickey Yang <nickey.yang@rock-chips.com>
Date: Mon, 17 Jul 2017 16:35:34 +0800
Subject: [PATCH] HACK: clk: rockchip: rk3288: dedicate npll for vopb and hdmi
use
MINIARM: set npll be used for hdmi only
Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
arch/arm/boot/dts/rk3288.dtsi | 2 ++
drivers/clk/rockchip/clk-rk3288.c | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 68d5a58cfe88..a376dea3bb1b 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -1046,6 +1046,8 @@ vopb: vop@ff930000 {
resets = <&cru SRST_LCDC0_AXI>, <&cru SRST_LCDC0_AHB>, <&cru SRST_LCDC0_DCLK>;
reset-names = "axi", "ahb", "dclk";
iommus = <&vopb_mmu>;
+ assigned-clocks = <&cru DCLK_VOP0>;
+ assigned-clock-parents = <&cru PLL_NPLL>;
status = "disabled";
vopb_out: port {
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 15c8f1dcba9a..460b19d65ef3 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -234,7 +234,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
RK3288_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
[npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16),
- RK3288_MODE_CON, 14, 9, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
+ RK3288_MODE_CON, 14, 9, 0, rk3288_pll_rates),
};
static struct clk_div_table div_hclk_cpu_t[] = {
@@ -444,7 +444,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS,
RK3288_CLKGATE_CON(3), 4, GFLAGS),
- COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, 0,
+ COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
RK3288_CLKSEL_CON(27), 0, 2, MFLAGS, 8, 8, DFLAGS,
RK3288_CLKGATE_CON(3), 1, GFLAGS),
COMPOSITE(DCLK_VOP1, "dclk_vop1", mux_pll_src_cpll_gpll_npll_p, 0,
From f971cfab78a201e0e4c3413a69b3ab1467ca6e3e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 4 Aug 2018 14:51:14 +0200
Subject: [PATCH] HACK: clk: rockchip: rk3288: use npll table to to improve
HDMI compatibility
Based on https://github.com/TinkerBoard/debian_kernel/commit/3d90870530b8a2901681f7b7fa598ee7381e49f3
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/clk/rockchip/clk-rk3288.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 460b19d65ef3..b973c6b0315b 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -124,6 +124,27 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = {
{ /* sentinel */ },
};
+static struct rockchip_pll_rate_table rk3288_npll_rates[] = {
+ RK3066_PLL_RATE_NB(594000000, 1, 99, 4, 32),
+ RK3066_PLL_RATE_NB(585000000, 6, 585, 4, 32),
+ RK3066_PLL_RATE_NB(432000000, 3, 216, 4, 32),
+ RK3066_PLL_RATE_NB(426000000, 3, 213, 4, 32),
+ RK3066_PLL_RATE_NB(400000000, 1, 100, 6, 32),
+ RK3066_PLL_RATE_NB(342000000, 3, 171, 4, 32),
+ RK3066_PLL_RATE_NB(297000000, 2, 198, 8, 16),
+ RK3066_PLL_RATE_NB(270000000, 1, 135, 12, 32),
+ RK3066_PLL_RATE_NB(260000000, 1, 130, 12, 32),
+ RK3066_PLL_RATE_NB(148500000, 1, 99, 16, 32),
+ RK3066_PLL_RATE(148352000, 13, 1125, 14),
+ RK3066_PLL_RATE_NB(146250000, 6, 585, 16, 32),
+ RK3066_PLL_RATE_NB(108000000, 1, 54, 12, 32),
+ RK3066_PLL_RATE_NB(106500000, 4, 213, 12, 32),
+ RK3066_PLL_RATE_NB(85500000, 4, 171, 12, 32),
+ RK3066_PLL_RATE_NB(74250000, 4, 198, 16, 32),
+ RK3066_PLL_RATE(74176000, 26, 1125, 14),
+ { /* sentinel */ },
+};
+
#define RK3288_DIV_ACLK_CORE_M0_MASK 0xf
#define RK3288_DIV_ACLK_CORE_M0_SHIFT 0
#define RK3288_DIV_ACLK_CORE_MP_MASK 0xf
@@ -234,7 +255,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = {
[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12),
RK3288_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3288_pll_rates),
[npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16),
- RK3288_MODE_CON, 14, 9, 0, rk3288_pll_rates),
+ RK3288_MODE_CON, 14, 9, 0, rk3288_npll_rates),
};
static struct clk_div_table div_hclk_cpu_t[] = {
From 611c4656dadc68be76767b04c27266c183748090 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 28 Oct 2018 21:43:01 +0100
Subject: [PATCH] HACK: clk: rockchip: rk3288: add more npll clocks
Fixes 2560x1440@60Hz, 1600x1200@60Hz, 1920x1200@60Hz, 1680x1050@60Hz and 1440x900@60Hz modes on my monitor
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/clk/rockchip/clk-rk3288.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index b973c6b0315b..9192b89c2550 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -130,18 +130,34 @@ static struct rockchip_pll_rate_table rk3288_npll_rates[] = {
RK3066_PLL_RATE_NB(432000000, 3, 216, 4, 32),
RK3066_PLL_RATE_NB(426000000, 3, 213, 4, 32),
RK3066_PLL_RATE_NB(400000000, 1, 100, 6, 32),
+ RK3066_PLL_RATE(348500000, 8, 697, 6),
RK3066_PLL_RATE_NB(342000000, 3, 171, 4, 32),
RK3066_PLL_RATE_NB(297000000, 2, 198, 8, 16),
RK3066_PLL_RATE_NB(270000000, 1, 135, 12, 32),
RK3066_PLL_RATE_NB(260000000, 1, 130, 12, 32),
+ RK3066_PLL_RATE(241500000, 2, 161, 8),
+ RK3066_PLL_RATE(162000000, 1, 81, 12),
+ RK3066_PLL_RATE(154000000, 6, 539, 14),
RK3066_PLL_RATE_NB(148500000, 1, 99, 16, 32),
RK3066_PLL_RATE(148352000, 13, 1125, 14),
RK3066_PLL_RATE_NB(146250000, 6, 585, 16, 32),
+ RK3066_PLL_RATE(121750000, 6, 487, 16),
+ RK3066_PLL_RATE(119000000, 3, 238, 16),
RK3066_PLL_RATE_NB(108000000, 1, 54, 12, 32),
RK3066_PLL_RATE_NB(106500000, 4, 213, 12, 32),
+ RK3066_PLL_RATE(101000000, 3, 202, 16),
+ RK3066_PLL_RATE(88750000, 6, 355, 16),
RK3066_PLL_RATE_NB(85500000, 4, 171, 12, 32),
+ RK3066_PLL_RATE(83500000, 3, 167, 16),
+ RK3066_PLL_RATE(79500000, 1, 53, 16),
RK3066_PLL_RATE_NB(74250000, 4, 198, 16, 32),
RK3066_PLL_RATE(74176000, 26, 1125, 14),
+ RK3066_PLL_RATE(72000000, 1, 48, 16),
+ RK3066_PLL_RATE(71000000, 3, 142, 16),
+ RK3066_PLL_RATE(68250000, 2, 91, 16),
+ RK3066_PLL_RATE(65000000, 3, 130, 16),
+ RK3066_PLL_RATE(40000000, 3, 80, 16),
+ RK3066_PLL_RATE(33750000, 2, 45, 16),
{ /* sentinel */ },
};
From 1e71e8f100f877a3fb04a63c437869732bab5974 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 25 May 2020 20:36:45 +0000
Subject: [PATCH] HACK: clk: rockchip: rk3399: dedicate vpll for vopb and hdmi
use
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/clk/rockchip/clk-rk3399.c | 32 +++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 3682d5675cf7..0934145c09ad 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -111,6 +111,25 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
{ /* sentinel */ },
};
+static struct rockchip_pll_rate_table rk3399_vpll_rates[] = {
+ /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+ RK3036_PLL_RATE( 594000000, 1, 123, 5, 1, 0, 12582912), /* vco = 2970000000 */
+ RK3036_PLL_RATE( 593406592, 1, 123, 5, 1, 0, 10508804), /* vco = 2967032965 */
+ RK3036_PLL_RATE( 297000000, 1, 123, 5, 2, 0, 12582912), /* vco = 2970000000 */
+ RK3036_PLL_RATE( 296703296, 1, 123, 5, 2, 0, 10508807), /* vco = 2967032970 */
+ RK3036_PLL_RATE( 148500000, 1, 129, 7, 3, 0, 15728640), /* vco = 3118500000 */
+ RK3036_PLL_RATE( 148351648, 1, 123, 5, 4, 0, 10508800), /* vco = 2967032960 */
+ RK3036_PLL_RATE( 106500000, 1, 124, 7, 4, 0, 4194304), /* vco = 2982000000 */
+ RK3036_PLL_RATE( 74250000, 1, 129, 7, 6, 0, 15728640), /* vco = 3118500000 */
+ RK3036_PLL_RATE( 74175824, 1, 129, 7, 6, 0, 13550823), /* vco = 3115384608 */
+ RK3036_PLL_RATE( 65000000, 1, 113, 7, 6, 0, 12582912), /* vco = 2730000000 */
+ RK3036_PLL_RATE( 59340659, 1, 121, 7, 7, 0, 2581098), /* vco = 2907692291 */
+ RK3036_PLL_RATE( 54000000, 1, 110, 7, 7, 0, 4194304), /* vco = 2646000000 */
+ RK3036_PLL_RATE( 27000000, 1, 55, 7, 7, 0, 2097152), /* vco = 1323000000 */
+ RK3036_PLL_RATE( 26973026, 1, 55, 7, 7, 0, 1173232), /* vco = 1321678323 */
+ { /* sentinel */ },
+};
+
/* CRU parents */
PNAME(mux_pll_p) = { "xin24m", "xin32k" };
@@ -129,7 +148,7 @@ PNAME(mux_ddrclk_p) = { "clk_ddrc_lpll_src",
PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src",
"gpll_aclk_cci_src",
"npll_aclk_cci_src",
- "vpll_aclk_cci_src" };
+ "prevent:vpll" };
PNAME(mux_cci_trace_p) = { "cpll_cci_trace",
"gpll_cci_trace" };
PNAME(mux_cs_p) = { "cpll_cs", "gpll_cs",
@@ -156,9 +175,10 @@ PNAME(mux_pll_src_cpll_gpll_npll_ppll_upll_24m_p) = { "cpll", "gpll", "npll",
"ppll", "upll", "xin24m" };
PNAME(mux_pll_src_vpll_cpll_gpll_p) = { "vpll", "cpll", "gpll" };
-PNAME(mux_pll_src_vpll_cpll_gpll_npll_p) = { "vpll", "cpll", "gpll",
+
+PNAME(mux_pll_src_vpll_cpll_gpll_npll_p) = { "prevent:vpll", "cpll", "gpll",
"npll" };
-PNAME(mux_pll_src_vpll_cpll_gpll_24m_p) = { "vpll", "cpll", "gpll",
+PNAME(mux_pll_src_vpll_cpll_gpll_24m_p) = { "prevent:vpll", "cpll", "gpll",
"xin24m" };
PNAME(mux_dclk_vop0_p) = { "dclk_vop0_div",
@@ -235,7 +255,7 @@ static struct rockchip_pll_clock rk3399_pll_clks[] __initdata = {
[npll] = PLL(pll_rk3399, PLL_NPLL, "npll", mux_pll_p, 0, RK3399_PLL_CON(40),
RK3399_PLL_CON(43), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
[vpll] = PLL(pll_rk3399, PLL_VPLL, "vpll", mux_pll_p, 0, RK3399_PLL_CON(48),
- RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
+ RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_vpll_rates),
};
static struct rockchip_pll_clock rk3399_pmu_pll_clks[] __initdata = {
@@ -285,7 +305,7 @@ static struct rockchip_clk_branch rk3399_uart4_pmu_fracmux __initdata =
RK3399_PMU_CLKSEL_CON(5), 8, 2, MFLAGS);
static struct rockchip_clk_branch rk3399_dclk_vop0_fracmux __initdata =
- MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT,
+ MUX(DCLK_VOP0, "dclk_vop0", mux_dclk_vop0_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
RK3399_CLKSEL_CON(49), 11, 1, MFLAGS);
static struct rockchip_clk_branch rk3399_dclk_vop1_fracmux __initdata =
@@ -1166,7 +1186,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
GATE(HCLK_VOP0_NOC, "hclk_vop0_noc", "hclk_vop0_pre", CLK_IGNORE_UNUSED,
RK3399_CLKGATE_CON(28), 0, GFLAGS),
- COMPOSITE(DCLK_VOP0_DIV, "dclk_vop0_div", mux_pll_src_vpll_cpll_gpll_p, 0,
+ COMPOSITE(DCLK_VOP0_DIV, "dclk_vop0_div", mux_pll_src_vpll_cpll_gpll_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
RK3399_CLKSEL_CON(49), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK3399_CLKGATE_CON(10), 12, GFLAGS),
From aafa27ca194b5601de77c56773aa69c2e2db0051 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 19 Jul 2020 16:35:11 +0000
Subject: [PATCH] HACK: dts: rockchip: do not use vopl for hdmi
---
arch/arm/boot/dts/rk3288.dtsi | 9 ---------
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 9 ---------
2 files changed, 18 deletions(-)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index a376dea3bb1b..9757976d6e8a 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -1104,11 +1104,6 @@ vopl_out: port {
#address-cells = <1>;
#size-cells = <0>;
- vopl_out_hdmi: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&hdmi_in_vopl>;
- };
-
vopl_out_edp: endpoint@1 {
reg = <1>;
remote-endpoint = <&edp_in_vopl>;
@@ -1249,10 +1244,6 @@ hdmi_in_vopb: endpoint@0 {
reg = <0>;
remote-endpoint = <&vopb_out_hdmi>;
};
- hdmi_in_vopl: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&vopl_out_hdmi>;
- };
};
};
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index ada724b12f01..8973bf68d652 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1637,11 +1637,6 @@ vopl_out_edp: endpoint@1 {
remote-endpoint = <&edp_in_vopl>;
};
- vopl_out_hdmi: endpoint@2 {
- reg = <2>;
- remote-endpoint = <&hdmi_in_vopl>;
- };
-
vopl_out_mipi1: endpoint@3 {
reg = <3>;
remote-endpoint = <&mipi1_in_vopl>;
@@ -1787,10 +1782,6 @@ hdmi_in_vopb: endpoint@0 {
reg = <0>;
remote-endpoint = <&vopb_out_hdmi>;
};
- hdmi_in_vopl: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&vopl_out_hdmi>;
- };
};
};
};
From 667178b6aeeeca36baf3f739902e5888efce6705 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Tue, 26 Feb 2019 20:45:14 +0000
Subject: [PATCH] WIP: dw-hdmi-cec: sleep 100ms on error
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
index 70ab4fbdc23e..f6a85f73b90d 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2015-2017 Russell King.
*/
+#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -129,8 +130,16 @@ static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
dw_hdmi_write(cec, stat, HDMI_IH_CEC_STAT0);
- if (stat & CEC_STAT_ERROR_INIT) {
- cec->tx_status = CEC_TX_STATUS_ERROR;
+ /*
+ * Status with both done and error_initiator bits have been seen
+ * on Rockchip RK3328 devices, transmit attempt seems to have failed
+ * when this happens, report as low drive and block cec-framework
+ * 100ms before core retransmits the failed message, this seems to
+ * mitigate the issue with failed transmit attempts.
+ */
+ if ((stat & (CEC_STAT_DONE|CEC_STAT_ERROR_INIT)) == (CEC_STAT_DONE|CEC_STAT_ERROR_INIT)) {
+ pr_info("dw_hdmi_cec_hardirq: stat=%02x LOW_DRIVE\n", stat);
+ cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
cec->tx_done = true;
ret = IRQ_WAKE_THREAD;
} else if (stat & CEC_STAT_DONE) {
@@ -141,6 +150,10 @@ static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
cec->tx_status = CEC_TX_STATUS_NACK;
cec->tx_done = true;
ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_ERROR_INIT) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
}
if (stat & CEC_STAT_EOM) {
@@ -173,6 +186,8 @@ static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
if (cec->tx_done) {
cec->tx_done = false;
+ if (cec->tx_status == CEC_TX_STATUS_LOW_DRIVE)
+ msleep(100);
cec_transmit_attempt_done(adap, cec->tx_status);
}
if (cec->rx_done) {
From 2642ca152420726d73313a469423e6447df393a6 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 18 Jul 2020 20:54:38 +0000
Subject: [PATCH] asdf
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 36 +++++++++++++++----
drivers/media/cec/core/cec-adap.c | 2 +-
2 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
index f6a85f73b90d..e6953219beee 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
@@ -58,6 +58,7 @@ struct dw_hdmi_cec {
u32 addresses;
struct cec_adapter *adap;
struct cec_msg rx_msg;
+ unsigned int tx_attempts;
unsigned int tx_status;
bool tx_done;
bool rx_done;
@@ -96,6 +97,8 @@ static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts,
struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
unsigned int i, ctrl;
+ pr_info("%s: attempts=%u signal_free_time=%u msg=%*ph (sequence: %u)\n", __func__, attempts, signal_free_time, msg->len, msg->msg, msg->sequence);
+
switch (signal_free_time) {
case CEC_SIGNAL_FREE_TIME_RETRY:
ctrl = CEC_CTRL_RETRY;
@@ -131,26 +134,35 @@ static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
dw_hdmi_write(cec, stat, HDMI_IH_CEC_STAT0);
/*
- * Status with both done and error_initiator bits have been seen
- * on Rockchip RK3328 devices, transmit attempt seems to have failed
- * when this happens, report as low drive and block cec-framework
+ * Status with both done and error_initiator bits have been observed
+ * on Rockchip RK3328/RK3399 devices, transmit attempt seems to have
+ * failed when this happens, report as low drive and block cec-framework
* 100ms before core retransmits the failed message, this seems to
* mitigate the issue with failed transmit attempts.
*/
if ((stat & (CEC_STAT_DONE|CEC_STAT_ERROR_INIT)) == (CEC_STAT_DONE|CEC_STAT_ERROR_INIT)) {
- pr_info("dw_hdmi_cec_hardirq: stat=%02x LOW_DRIVE\n", stat);
+ if (!cec->tx_attempts)
+ cec->tx_attempts = 2;
cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
cec->tx_done = true;
ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_ARBLOST) {
+ cec->tx_attempts = 0;
+ cec->tx_status = CEC_TX_STATUS_ARB_LOST;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
} else if (stat & CEC_STAT_DONE) {
+ cec->tx_attempts = 0;
cec->tx_status = CEC_TX_STATUS_OK;
cec->tx_done = true;
ret = IRQ_WAKE_THREAD;
} else if (stat & CEC_STAT_NACK) {
+ cec->tx_attempts = 0;
cec->tx_status = CEC_TX_STATUS_NACK;
cec->tx_done = true;
ret = IRQ_WAKE_THREAD;
} else if (stat & CEC_STAT_ERROR_INIT) {
+ cec->tx_attempts = 0;
cec->tx_status = CEC_TX_STATUS_ERROR;
cec->tx_done = true;
ret = IRQ_WAKE_THREAD;
@@ -176,6 +188,8 @@ static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
ret = IRQ_WAKE_THREAD;
}
+ pr_info("%s: stat=%x ret=%x tx_done=%d rx_done=%d tx_status=%u tx_attempts=%u\n", __func__, stat, ret, cec->tx_done, cec->rx_done, cec->tx_status, cec->tx_attempts);
+
return ret;
}
@@ -184,11 +198,19 @@ static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
struct cec_adapter *adap = data;
struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
+ //pr_info("%s: tx_done=%d rx_done=%d tx_status=%u tx_attempts=%u\n", __func__, cec->tx_done, cec->rx_done, cec->tx_status, cec->tx_attempts);
+
if (cec->tx_done) {
cec->tx_done = false;
if (cec->tx_status == CEC_TX_STATUS_LOW_DRIVE)
msleep(100);
- cec_transmit_attempt_done(adap, cec->tx_status);
+ if (cec->tx_attempts > 1) {
+ cec->tx_attempts--;
+ dw_hdmi_write(cec, CEC_CTRL_RETRY | CEC_CTRL_START, HDMI_CEC_CTRL);
+ } else {
+ cec->tx_attempts = 0;
+ cec_transmit_attempt_done(adap, cec->tx_status);
+ }
}
if (cec->rx_done) {
cec->rx_done = false;
@@ -219,8 +241,8 @@ static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable)
cec->ops->enable(cec->hdmi);
- irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
- CEC_STAT_DONE;
+ irqs = CEC_STAT_ERROR_INIT | CEC_STAT_ARBLOST | CEC_STAT_NACK |
+ CEC_STAT_EOM | CEC_STAT_DONE;
dw_hdmi_write(cec, irqs, HDMI_CEC_POLARITY);
dw_hdmi_write(cec, ~irqs, HDMI_CEC_MASK);
dw_hdmi_write(cec, ~irqs, HDMI_IH_MUTE_CEC_STAT0);
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 926d65db6d3e..7b17539f656c 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -599,7 +599,6 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
unsigned int attempts_made = arb_lost_cnt + nack_cnt +
low_drive_cnt + error_cnt;
- dprintk(2, "%s: status 0x%02x\n", __func__, status);
if (attempts_made < 1)
attempts_made = 1;
@@ -620,6 +619,7 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
adap->transmit_in_progress = false;
msg = &data->msg;
+ dprintk(2, "%s: %*ph (sequence: %u, attempts: %d, status: %02x)\n", __func__, msg->len, msg->msg, msg->sequence, attempts_made, status);
/* Drivers must fill in the status! */
WARN_ON(status == 0);
From 2acbc9339a2fe44e1c7d5b2b97d2a09ba16c96fb Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 11:44:01 +0000
Subject: [PATCH] cec dprintk revert
---
drivers/media/cec/core/cec-adap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 7b17539f656c..926d65db6d3e 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -599,6 +599,7 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
unsigned int attempts_made = arb_lost_cnt + nack_cnt +
low_drive_cnt + error_cnt;
+ dprintk(2, "%s: status 0x%02x\n", __func__, status);
if (attempts_made < 1)
attempts_made = 1;
@@ -619,7 +620,6 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status,
adap->transmit_in_progress = false;
msg = &data->msg;
- dprintk(2, "%s: %*ph (sequence: %u, attempts: %d, status: %02x)\n", __func__, msg->len, msg->msg, msg->sequence, attempts_made, status);
/* Drivers must fill in the status! */
WARN_ON(status == 0);
From 0e1dc6a91a725e1d3417f693f620a77e29a222ad Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 12:33:01 +0000
Subject: [PATCH] Revert "fixup! WIP: drm/rockchip: vop: max_output"
This reverts commit c69612ca6820500cd1a0a3e4f8eb8c6f7b971cda.
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 106b38ea12df..138f449924f8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1184,8 +1184,19 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *adjusted_mode)
{
struct vop *vop = to_vop(crtc);
+ const struct vop_rect *max_output = &vop->data->max_output;
unsigned long rate;
+ if (max_output->width && max_output->height) {
+ enum drm_mode_status status;
+
+ status = drm_mode_validate_size(adjusted_mode,
+ max_output->width,
+ max_output->height);
+ if (status != MODE_OK)
+ return false;
+ }
+
/*
* Clock craziness.
*
From 0b9759fa2630678c7e12f1b922ff866f53df90d1 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 15 Jul 2020 15:24:47 +0000
Subject: [PATCH] drm/rockchip: vop: fix crtc duplicate state
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 138f449924f8..0a25de483515 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1578,7 +1578,11 @@ static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct rockchip_crtc_state *rockchip_state;
- rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL);
+ if (WARN_ON(!crtc->state))
+ return NULL;
+
+ rockchip_state = kmemdup(to_rockchip_crtc_state(crtc->state),
+ sizeof(*rockchip_state), GFP_KERNEL);
if (!rockchip_state)
return NULL;
From dbf463be2403be5cd1e538fce49d85f7e26d4b3e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 15:15:50 +0000
Subject: [PATCH] WIP: drm/rockchip: vop: filter interlaced modes
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 0a25de483515..5ab1412173a7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1160,6 +1160,9 @@ static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
if (s->output_type != DRM_MODE_CONNECTOR_HDMIA)
return MODE_OK;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_NO_INTERLACE;
+
rounded_rate = clk_round_rate(vop->dclk, mode->clock * 1000 + 999);
if (rounded_rate < 0)
return MODE_NOCLOCK;
From c846700ec7fdd8b7982f523bbfe867db3d08711b Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: add bridge and switch to
drm_bridge_funcs
Switch the dw-hdmi driver to drm_bridge_funcs by implementing
a new local bridge, connecting it to the dw-hdmi bridge.
Also enable bridge format negotiation by implementing
atomic_get_input_bus_fmts and support for 8-bit RGB 4:4:4.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 138 ++++++++++++++------
1 file changed, 95 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cc20a83fa9b8..745fd1c13cef 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -68,6 +68,7 @@ struct rockchip_hdmi {
struct device *dev;
struct regmap *regmap;
struct drm_encoder encoder;
+ struct drm_bridge bridge;
const struct rockchip_hdmi_chip_data *chip_data;
struct clk *vpll_clk;
struct clk *grf_clk;
@@ -228,30 +229,20 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
return drm_mode_validate_size(mode, 3840, 2160);
}
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
+static void
+dw_hdmi_rockchip_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
{
-}
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
-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;
+ clk_set_rate(hdmi->vpll_clk, adjusted_mode->clock * 1000);
}
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
+static void dw_hdmi_rockchip_bridge_enable(struct drm_bridge *bridge)
{
- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
-
- clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
-}
-
-static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
-{
- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
+ struct drm_encoder *encoder = bridge->encoder;
u32 val;
int ret;
@@ -279,10 +270,21 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
ret ? "LIT" : "BIG");
}
+static bool is_rgb(u32 format)
+{
+ switch (format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ return true;
+ default:
+ 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)
+dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
@@ -292,12 +294,38 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
-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,
+static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmt;
+
+ *num_input_fmts = 0;
+
+ if (!is_rgb(output_fmt))
+ return NULL;
+
+ input_fmt = kzalloc(sizeof(*input_fmt), GFP_KERNEL);
+ if (!input_fmt)
+ return NULL;
+
+ *num_input_fmts = 1;
+ *input_fmt = output_fmt;
+
+ return input_fmt;
+}
+
+static const struct drm_bridge_funcs dw_hdmi_rockchip_bridge_funcs = {
+ .mode_set = dw_hdmi_rockchip_bridge_mode_set,
+ .enable = dw_hdmi_rockchip_bridge_enable,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_input_bus_fmts = dw_hdmi_rockchip_get_input_bus_fmts,
+ .atomic_check = dw_hdmi_rockchip_bridge_atomic_check,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
};
static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
@@ -476,6 +504,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
struct dw_hdmi_plat_data *plat_data;
const struct of_device_id *match;
struct drm_device *drm = data;
+ struct drm_bridge *next_bridge;
struct drm_encoder *encoder;
struct rockchip_hdmi *hdmi;
int ret;
@@ -516,8 +545,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
ret = clk_prepare_enable(hdmi->vpll_clk);
if (ret) {
- DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
- ret);
+ DRM_DEV_ERROR(hdmi->dev, "Failed to enable vpll: %d\n", ret);
return ret;
}
@@ -525,27 +553,51 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
if (IS_ERR(hdmi->phy)) {
ret = PTR_ERR(hdmi->phy);
if (ret != -EPROBE_DEFER)
- DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
- return ret;
+ DRM_DEV_ERROR(hdmi->dev, "Failed to get phy: %d\n", ret);
+ goto err_disable_clk;
}
- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+ ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+ if (ret) {
+ DRM_DEV_ERROR(hdmi->dev, "Failed to init encoder: %d\n", ret);
+ goto err_disable_clk;
+ }
- platform_set_drvdata(pdev, hdmi);
+ hdmi->bridge.funcs = &dw_hdmi_rockchip_bridge_funcs;
+ drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0);
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+ platform_set_drvdata(pdev, hdmi);
- /*
- * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
- * which would have called the encoder cleanup. Do it manually.
- */
+ hdmi->hdmi = dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi->hdmi)) {
ret = PTR_ERR(hdmi->hdmi);
- drm_encoder_cleanup(encoder);
- clk_disable_unprepare(hdmi->vpll_clk);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(hdmi->dev, "Failed to init dw-hdmi bridge: %d\n", ret);
+ goto err_encoder_cleanup;
+ }
+
+ next_bridge = of_drm_find_bridge(pdev->dev.of_node);
+ if (!next_bridge) {
+ ret = -EPROBE_DEFER;
+ goto err_dw_hdmi_remove;
+ }
+
+ ret = drm_bridge_attach(encoder, next_bridge, &hdmi->bridge, 0);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(hdmi->dev, "Failed to attach dw-hdmi bridge: %d\n", ret);
+ goto err_dw_hdmi_remove;
}
+ return 0;
+
+err_dw_hdmi_remove:
+ dw_hdmi_remove(hdmi->hdmi);
+err_encoder_cleanup:
+ drm_encoder_cleanup(encoder);
+err_disable_clk:
+ clk_disable_unprepare(hdmi->vpll_clk);
+
return ret;
}
@@ -554,7 +606,7 @@ 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);
+ dw_hdmi_remove(hdmi->hdmi);
clk_disable_unprepare(hdmi->vpll_clk);
}
From a1b98d86294a9320fe154aa11e104dea6becee4a Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 18:00:44 +0000
Subject: [PATCH] drm/bridge: dw-hdmi: add mtmdsclock parameter to phy
configure ops
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 10 ++++++----
drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c | 3 ++-
include/drm/bridge/dw_hdmi.h | 3 ++-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 748df1cacd2b..c25d5ac7bb07 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -137,7 +137,8 @@ struct dw_hdmi_phy_data {
bool has_svsret;
int (*configure)(struct dw_hdmi *hdmi,
const struct dw_hdmi_plat_data *pdata,
- unsigned long mpixelclock);
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock);
};
struct dw_hdmi {
@@ -1441,7 +1442,8 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
*/
static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
const struct dw_hdmi_plat_data *pdata,
- unsigned long mpixelclock)
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock)
{
const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
@@ -1516,9 +1518,9 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,
/* Write to the PHY as configured by the platform */
if (pdata->configure_phy)
- ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
+ ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock, mtmdsclock);
else
- ret = phy->configure(hdmi, pdata, mpixelclock);
+ ret = phy->configure(hdmi, pdata, mpixelclock, mtmdsclock);
if (ret) {
dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
mpixelclock);
diff --git a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
index 7b8ec8310699..539d86131fd4 100644
--- a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
+++ b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
@@ -53,7 +53,8 @@ rcar_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
}
static int rcar_hdmi_phy_configure(struct dw_hdmi *hdmi, void *data,
- unsigned long mpixelclock)
+ unsigned long mpixelclock,
+ unsigned long mtmdsclock)
{
const struct rcar_hdmi_phy_params *params = rcar_hdmi_phy_params;
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index ea34ca146b82..4f61ede6486d 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -152,7 +152,8 @@ struct dw_hdmi_plat_data {
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 long mpixelclock,
+ unsigned long mtmdsclock);
};
struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
From 1f152929c7bb18b6349fffbc06a003007e5e3783 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 21:34:48 +0000
Subject: [PATCH] drm/bridge: dw-hdmi: support configuring phy for deep color
Q: Should we rename dw_hdmi_curr_ctrl and dw_hdmi_phy_config mpixelclock to mtmdsclock ?
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index c25d5ac7bb07..bcdd823907c2 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1448,6 +1448,7 @@ 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;
+ int depth;
/* TOFIX Will need 420 specific PHY configuration tables */
@@ -1457,11 +1458,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 (mtmdsclock <= curr_ctrl->mpixelclock)
break;
for (; phy_config->mpixelclock != ~0UL; phy_config++)
- if (mpixelclock <= phy_config->mpixelclock)
+ if (mtmdsclock <= phy_config->mpixelclock)
break;
if (mpll_config->mpixelclock == ~0UL ||
@@ -1469,11 +1470,17 @@ 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,
+ depth = hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+ if (depth > 8 && mpixelclock != mtmdsclock)
+ depth = fls(depth - 8) - 1;
+ else
+ depth = 0;
+
+ 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);
From 9c5117c0b3a6d5a128c6ce2102f8e81676752978 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 22:25:15 +0000
Subject: [PATCH] drm/bridge: dw-hdmi: add mpll_cfg_420 for ycbcr420 mode
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 +++-
include/drm/bridge/dw_hdmi.h | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index bcdd823907c2..f5d048adf649 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1450,7 +1450,9 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
int depth;
- /* TOFIX Will need 420 specific PHY configuration tables */
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
+ pdata->mpll_cfg_420)
+ mpll_config = pdata->mpll_cfg_420;
/* PLL/MPLL Cfg - always match on final entry */
for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 4f61ede6486d..0ebe01835d2a 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -149,6 +149,7 @@ struct dw_hdmi_plat_data {
/* 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,
From dadb0b7e99a25a71a8b9e41d6cb154cb2ddf856e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 15 Jul 2020 09:49:21 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: mode_valid: allow 420 clock rate
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 745fd1c13cef..9784111ea746 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -222,8 +222,15 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
- if (mode->clock > 340000 ||
- (info->max_tmds_clock && mode->clock > info->max_tmds_clock))
+ struct dw_hdmi_plat_data *pdata = (struct dw_hdmi_plat_data *)data;
+ int clock = mode->clock;
+
+ if (pdata->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420))
+ clock /= 2;
+
+ if (clock > 340000 ||
+ (info->max_tmds_clock && clock > info->max_tmds_clock))
return MODE_CLOCK_HIGH;
return drm_mode_validate_size(mode, 3840, 2160);
@@ -524,6 +531,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
hdmi->dev = &pdev->dev;
hdmi->chip_data = plat_data->phy_data;
+ plat_data->priv_data = plat_data;
plat_data->phy_data = hdmi;
encoder = &hdmi->encoder;
From 0f936ce17398a9cee1be5027cf86b44430fcd23e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Mon, 20 Jul 2020 22:26:19 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: rk3399: add mpll_cfg_420
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 9784111ea746..e7fbeb9132fb 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -166,6 +166,46 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
}
};
+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_curr_ctrl rockchip_cur_ctr[] = {
/* pixelclk bpp8 bpp10 bpp12 */
{
@@ -481,6 +521,7 @@ 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,
From 240069c903db02016afcbacbde7a292d651eeb36 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH] WIP: drm/bridge: dw-hdmi: limit mode and bus format to
max_tmds_clock
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 120 ++++++++++++++--------
1 file changed, 76 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index f5d048adf649..26c64cf2d00a 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1859,6 +1859,21 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
}
+static unsigned int
+hdmi_get_tmdsclock(unsigned int bus_format, unsigned int pixelclock)
+{
+ int color_depth = hdmi_bus_fmt_color_depth(bus_format);
+ unsigned int tmdsclock = pixelclock;
+
+ if (!hdmi_bus_fmt_is_yuv422(bus_format) && color_depth > 8)
+ tmdsclock = (u64)pixelclock * color_depth / 8;
+
+ if (hdmi_bus_fmt_is_yuv420(bus_format))
+ tmdsclock /= 2;
+
+ return tmdsclock;
+}
+
static void hdmi_av_composer(struct dw_hdmi *hdmi,
const struct drm_display_info *display,
const struct drm_display_mode *mode)
@@ -1870,29 +1885,11 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
unsigned int vdisplay, hdisplay;
vmode->mpixelclock = mode->clock * 1000;
+ vmode->mtmdsclock =
+ hdmi_get_tmdsclock(hdmi->hdmi_data.enc_out_bus_format,
+ vmode->mpixelclock);
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
-
- vmode->mtmdsclock = vmode->mpixelclock;
-
- 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)) {
- case 16:
- vmode->mtmdsclock = vmode->mpixelclock * 2;
- break;
- case 12:
- vmode->mtmdsclock = vmode->mpixelclock * 3 / 2;
- break;
- case 10:
- vmode->mtmdsclock = vmode->mpixelclock * 5 / 4;
- break;
- }
- }
-
- 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 */
@@ -2550,8 +2547,21 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
* - MEDIA_BUS_FMT_RGB888_1X24,
*/
-/* Can return a maximum of 11 possible output formats for a mode/connector */
-#define MAX_OUTPUT_SEL_FORMATS 11
+/* Can return a maximum of 15 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS 15
+
+static bool is_tmds_allowed(struct drm_display_info *info,
+ struct drm_display_mode *mode,
+ u32 bus_format)
+{
+ unsigned long tmdsclock = hdmi_get_tmdsclock(bus_format, mode->clock);
+ int max_tmds_clock = info->max_tmds_clock ? info->max_tmds_clock : 340000;
+
+ if (max_tmds_clock >= tmdsclock)
+ return true;
+
+ return false;
+}
static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
@@ -2563,8 +2573,6 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
struct drm_display_info *info = &conn->display_info;
struct drm_display_mode *mode = &crtc_state->mode;
u8 max_bpc = conn_state->max_requested_bpc;
- bool is_hdmi2_sink = info->hdmi.scdc.supported ||
- (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
u32 *output_fmts;
unsigned int i = 0;
@@ -2587,29 +2595,33 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
* If the current mode enforces 4:2:0, force the output but format
* to 4:2:0 and do not add the YUV422/444/RGB formats
*/
- if (conn->ycbcr_420_allowed &&
- (drm_mode_is_420_only(info, mode) ||
- (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
+ if (conn->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420)) {
/* Order bus formats from 16bit to 8bit if supported */
if (max_bpc >= 16 && info->bpc == 16 &&
- (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY16_0_5X48))
output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
if (max_bpc >= 12 && info->bpc >= 12 &&
- (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY12_0_5X36))
output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
if (max_bpc >= 10 && info->bpc >= 10 &&
- (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY10_0_5X30))
output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
/* Default 8bit fallback */
- output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYYVYY8_0_5X24))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
*num_output_fmts = i;
- return output_fmts;
+ if (drm_mode_is_420_only(info, mode))
+ return output_fmts;
}
/*
@@ -2618,40 +2630,51 @@ static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
*/
if (max_bpc >= 16 && info->bpc == 16) {
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV16_1X48))
output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
- output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB161616_1X48))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
}
if (max_bpc >= 12 && info->bpc >= 12) {
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB422) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY12_1X24))
output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV12_1X36))
output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
- output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB121212_1X36))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
}
if (max_bpc >= 10 && info->bpc >= 10) {
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB422) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY10_1X20))
output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV10_1X30))
output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
- output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB101010_1X30))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
}
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB422) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_UYVY8_1X16))
output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
- if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ if ((info->color_formats & DRM_COLOR_FORMAT_YCRCB444) &&
+ is_tmds_allowed(info, mode, MEDIA_BUS_FMT_YUV8_1X24))
output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
/* Default 8bit RGB fallback */
- output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ if (is_tmds_allowed(info, mode, MEDIA_BUS_FMT_RGB888_1X24))
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
*num_output_fmts = i;
@@ -2831,11 +2854,20 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
struct dw_hdmi *hdmi = bridge->driver_private;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
enum drm_mode_status mode_status = MODE_OK;
+ int max_tmds_clock = info->max_tmds_clock ? info->max_tmds_clock : 340000;
+ int clock = mode->clock;
/* We don't support double-clocked modes */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
+ if (pdata->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420))
+ clock /= 2;
+
+ if (clock > max_tmds_clock)
+ return MODE_CLOCK_HIGH;
+
if (pdata->mode_valid)
mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
mode);
From 636c91cfca72fb0c6b8c5e8084867d74491b5dfa Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +0000
Subject: [PATCH] WIP: drm/rockchip: dw_hdmi: add 10-bit rgb bus format
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 2 +
2 files changed, 43 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index e7fbeb9132fb..cd87ee8a65c3 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -77,6 +77,7 @@ struct rockchip_hdmi {
};
#define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x)
+#define to_crtc_state(x) container_of(x, struct drm_crtc_state, x)
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
{
@@ -282,6 +283,11 @@ dw_hdmi_rockchip_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *adjusted_mode)
{
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
+ struct drm_crtc_state *crtc_state = to_crtc_state(adjusted_mode);
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+ if (hdmi->phy)
+ phy_set_bus_width(hdmi->phy, s->bus_width);
clk_set_rate(hdmi->vpll_clk, adjusted_mode->clock * 1000);
}
@@ -320,6 +326,7 @@ static void dw_hdmi_rockchip_bridge_enable(struct drm_bridge *bridge)
static bool is_rgb(u32 format)
{
switch (format) {
+ case MEDIA_BUS_FMT_RGB101010_1X30:
case MEDIA_BUS_FMT_RGB888_1X24:
return true;
default:
@@ -327,6 +334,16 @@ static bool is_rgb(u32 format)
}
}
+static bool is_10bit(u32 format)
+{
+ switch (format) {
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
@@ -334,9 +351,24 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct drm_atomic_state *state = bridge_state->base.state;
+ struct drm_crtc_state *old_crtc_state;
+ struct rockchip_crtc_state *old_state;
+ u32 format = bridge_state->output_bus_cfg.format;
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+ s->output_bpc = 10;
+ s->bus_format = format;
+ s->bus_width = is_10bit(format) ? 10 : 8;
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(state, conn_state->crtc);
+ if (old_crtc_state && !crtc_state->mode_changed) {
+ old_state = to_rockchip_crtc_state(old_crtc_state);
+ if (s->bus_format != old_state->bus_format ||
+ s->bus_width != old_state->bus_width)
+ crtc_state->mode_changed = true;
+ }
return 0;
}
@@ -348,10 +380,19 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
u32 output_fmt,
unsigned int *num_input_fmts)
{
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
+ struct drm_encoder *encoder = bridge->encoder;
u32 *input_fmt;
+ bool has_10bit = true;
*num_input_fmts = 0;
+ if (drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder))
+ has_10bit = false;
+
+ if (!has_10bit && is_10bit(output_fmt))
+ return NULL;
+
if (!is_rgb(output_fmt))
return NULL;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index e33c2dcd0d4b..03944e08b6c7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -31,6 +31,8 @@ struct rockchip_crtc_state {
int output_bpc;
int output_flags;
bool enable_afbc;
+ u32 bus_format;
+ int bus_width;
};
#define to_rockchip_crtc_state(s) \
container_of(s, struct rockchip_crtc_state, base)
From ff80104f4a32e591e12358432a85aabc5b436b63 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 8 Dec 2019 23:42:44 +0000
Subject: [PATCH] WIP: drm: dw-hdmi: add content type connector property
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 26c64cf2d00a..ffb72e6874c8 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1646,6 +1646,7 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
const struct drm_connector *connector,
const struct drm_display_mode *mode)
{
+ const struct drm_connector_state *conn_state = connector->state;
struct hdmi_avi_infoframe frame;
u8 val;
@@ -1703,6 +1704,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
}
+ drm_hdmi_avi_infoframe_content_type(&frame, conn_state);
+
/*
* The Designware IP uses a different byte format from standard
* AVI info frames, though generally the bits are in the correct
@@ -2437,7 +2440,8 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
if (!crtc)
return 0;
- if (!hdr_metadata_equal(old_state, new_state)) {
+ if (!hdr_metadata_equal(old_state, new_state) ||
+ old_state->content_type != new_state->content_type) {
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
@@ -2505,6 +2509,8 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
drm_connector_attach_max_bpc_property(connector, 8, 16);
+ drm_connector_attach_content_type_property(connector);
+
if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
drm_object_attach_property(&connector->base,
connector->dev->mode_config.hdr_output_metadata_property, 0);
From 9d3f655dae4e7d01ed64783958c228ab031b723c Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH] WIP: drm/rockchip: add yuv444 support
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 30 ++++++++++++++++++++-
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 29 ++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 6 +++++
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 14 ++++++++++
4 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cd87ee8a65c3..436a9223e5e4 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -62,6 +62,7 @@ struct rockchip_hdmi_chip_data {
int lcdsel_grf_reg;
u32 lcdsel_big;
u32 lcdsel_lit;
+ bool ycbcr_444_allowed;
};
struct rockchip_hdmi {
@@ -334,10 +335,22 @@ static bool is_rgb(u32 format)
}
}
+static bool is_yuv444(u32 format)
+{
+ switch (format) {
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool is_10bit(u32 format)
{
switch (format) {
case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_YUV10_1X30:
return true;
default:
return false;
@@ -354,12 +367,22 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_atomic_state *state = bridge_state->base.state;
struct drm_crtc_state *old_crtc_state;
struct rockchip_crtc_state *old_state;
+ struct drm_bridge *next_bridge;
+ struct drm_bridge_state *next_bridge_state;
u32 format = bridge_state->output_bus_cfg.format;
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
s->output_bpc = 10;
s->bus_format = format;
+
+ next_bridge = drm_bridge_get_next_bridge(bridge);
+ if (next_bridge) {
+ next_bridge_state = drm_atomic_get_new_bridge_state(state,
+ next_bridge);
+ format = next_bridge_state->output_bus_cfg.format;
+ }
+
s->bus_width = is_10bit(format) ? 10 : 8;
old_crtc_state = drm_atomic_get_old_crtc_state(state, conn_state->crtc);
@@ -393,7 +416,10 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
if (!has_10bit && is_10bit(output_fmt))
return NULL;
- if (!is_rgb(output_fmt))
+ if (is_yuv444(output_fmt)) {
+ if (!hdmi->chip_data->ycbcr_444_allowed)
+ return NULL;
+ } else if (!is_rgb(output_fmt))
return NULL;
input_fmt = kzalloc(sizeof(*input_fmt), GFP_KERNEL);
@@ -542,6 +568,7 @@ static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = {
static struct rockchip_hdmi_chip_data rk3328_chip_data = {
.lcdsel_grf_reg = -1,
+ .ycbcr_444_allowed = true,
};
static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
@@ -557,6 +584,7 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
.lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL),
.lcdsel_lit = HIWORD_UPDATE(RK3399_HDMI_LCDC_SEL, RK3399_HDMI_LCDC_SEL),
+ .ycbcr_444_allowed = true,
};
static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 5ab1412173a7..a17bd4e90ba7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -310,6 +310,17 @@ static int vop_convert_afbc_format(uint32_t format)
return -EINVAL;
}
+static bool is_yuv_output(uint32_t bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ return true;
+ default:
+ return false;
+ }
+}
+
static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
uint32_t dst, bool is_horizontal,
int vsu_mode, int *vskiplines)
@@ -1329,6 +1340,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
u16 vact_end = vact_st + vdisplay;
uint32_t pin_pol, val;
int dither_bpc = s->output_bpc ? s->output_bpc : 10;
+ bool yuv_output = is_yuv_output(s->bus_format);
int ret;
if (old_state && old_state->self_refresh_active) {
@@ -1402,6 +1414,8 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
!(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
s->output_mode = ROCKCHIP_OUT_MODE_P888;
+ VOP_REG_SET(vop, common, dsp_data_swap, yuv_output ? 2 : 0);
+
if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
VOP_REG_SET(vop, common, pre_dither_down, 1);
else
@@ -1417,6 +1431,21 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
VOP_REG_SET(vop, common, out_mode, s->output_mode);
+ VOP_REG_SET(vop, common, overlay_mode, yuv_output);
+ VOP_REG_SET(vop, common, dsp_out_yuv, yuv_output);
+
+ /*
+ * Background color is 10bit depth if vop version >= 3.5
+ */
+ if (!yuv_output)
+ val = 0;
+ else if (VOP_MAJOR(vop_data->version) == 3 &&
+ VOP_MINOR(vop_data->version) >= 5)
+ val = 0x20010200;
+ else
+ val = 0x801080;
+ VOP_REG_SET(vop, common, dsp_background, val);
+
VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len);
val = hact_st << 16;
val |= hact_end;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 1516231bbf93..b820ad3fa091 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -92,10 +92,16 @@ struct vop_common {
struct vop_reg mmu_en;
struct vop_reg out_mode;
struct vop_reg standby;
+
+ struct vop_reg overlay_mode;
+ struct vop_reg dsp_data_swap;
+ struct vop_reg dsp_out_yuv;
+ struct vop_reg dsp_background;
};
struct vop_misc {
struct vop_reg global_regdone_en;
+ struct vop_reg win_channel[4];
};
struct vop_intr {
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 57c36e9207c1..800b9341dd42 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -644,6 +644,11 @@ static const struct vop_common rk3288_common = {
.dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18),
.out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0),
.cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0),
+
+ .overlay_mode = VOP_REG(RK3288_SYS_CTRL, 0x1, 16),
+ .dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12),
+ .dsp_out_yuv = VOP_REG(RK3288_POST_SCL_CTRL, 0x1, 2),
+ .dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0),
};
/*
@@ -996,6 +1001,10 @@ static const struct vop_output rk3328_output = {
static const struct vop_misc rk3328_misc = {
.global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11),
+
+ .win_channel[0] = VOP_REG(RK3328_WIN0_CTRL2, 0xff, 0),
+ .win_channel[1] = VOP_REG(RK3328_WIN1_CTRL2, 0xff, 0),
+ .win_channel[2] = VOP_REG(RK3328_WIN2_CTRL2, 0xff, 0),
};
static const struct vop_common rk3328_common = {
@@ -1008,6 +1017,11 @@ static const struct vop_common rk3328_common = {
.dsp_blank = VOP_REG(RK3328_DSP_CTRL0, 0x3, 18),
.out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0),
.cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0),
+
+ .overlay_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 16),
+ .dsp_data_swap = VOP_REG(RK3328_DSP_CTRL0, 0x1f, 12),
+ .dsp_out_yuv = VOP_REG(RK3328_POST_SCL_CTRL, 0x1, 2),
+ .dsp_background = VOP_REG(RK3328_DSP_BG, 0xffffffff, 0),
};
static const struct vop_intr rk3328_vop_intr = {
From 32b8598b1300eb3efd9e01b7afe0b6ca86b15123 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:43 +0000
Subject: [PATCH] WIP: drm/rockchip: add yuv420 support
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 23 +++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 18 +++++++++++++++-
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 10 +++++----
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 ++
4 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 436a9223e5e4..1abc46a023a4 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -346,9 +346,21 @@ static bool is_yuv444(u32 format)
}
}
+static bool is_yuv420(u32 format)
+{
+ switch (format) {
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool is_10bit(u32 format)
{
switch (format) {
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
case MEDIA_BUS_FMT_RGB101010_1X30:
case MEDIA_BUS_FMT_YUV10_1X30:
return true;
@@ -385,6 +397,11 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
s->bus_width = is_10bit(format) ? 10 : 8;
+ if (is_yuv420(format)) {
+ s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
+ s->bus_width /= 2;
+ }
+
old_crtc_state = drm_atomic_get_old_crtc_state(state, conn_state->crtc);
if (old_crtc_state && !crtc_state->mode_changed) {
old_state = to_rockchip_crtc_state(old_crtc_state);
@@ -405,6 +422,7 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
{
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
struct drm_encoder *encoder = bridge->encoder;
+ struct drm_connector *connector = conn_state->connector;
u32 *input_fmt;
bool has_10bit = true;
@@ -419,6 +437,9 @@ static u32 *dw_hdmi_rockchip_get_input_bus_fmts(struct drm_bridge *bridge,
if (is_yuv444(output_fmt)) {
if (!hdmi->chip_data->ycbcr_444_allowed)
return NULL;
+ } else if (is_yuv420(output_fmt)) {
+ if (!connector->ycbcr_420_allowed)
+ return NULL;
} else if (!is_rgb(output_fmt))
return NULL;
@@ -578,6 +599,7 @@ 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,
+ .ycbcr_420_allowed = true,
};
static struct rockchip_hdmi_chip_data rk3399_chip_data = {
@@ -595,6 +617,7 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
.phy_config = rockchip_phy_config,
.phy_data = &rk3399_chip_data,
.use_drm_infoframe = true,
+ .ycbcr_420_allowed = true,
};
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index a17bd4e90ba7..5ea8031eb0f7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -311,6 +311,19 @@ static int vop_convert_afbc_format(uint32_t format)
}
static bool is_yuv_output(uint32_t bus_format)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool has_uv_swapped(uint32_t bus_format)
{
switch (bus_format) {
case MEDIA_BUS_FMT_YUV8_1X24:
@@ -1414,7 +1427,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
!(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
s->output_mode = ROCKCHIP_OUT_MODE_P888;
- VOP_REG_SET(vop, common, dsp_data_swap, yuv_output ? 2 : 0);
+ VOP_REG_SET(vop, common, dsp_data_swap, has_uv_swapped(s->bus_format) ? 2 : 0);
if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
VOP_REG_SET(vop, common, pre_dither_down, 1);
@@ -1431,6 +1444,9 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
VOP_REG_SET(vop, common, out_mode, s->output_mode);
+ VOP_REG_SET(vop, common, dclk_ddr,
+ s->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
+
VOP_REG_SET(vop, common, overlay_mode, yuv_output);
VOP_REG_SET(vop, common, dsp_out_yuv, yuv_output);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index b820ad3fa091..8e6e999e5163 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -94,6 +94,7 @@ struct vop_common {
struct vop_reg standby;
struct vop_reg overlay_mode;
+ struct vop_reg dclk_ddr;
struct vop_reg dsp_data_swap;
struct vop_reg dsp_out_yuv;
struct vop_reg dsp_background;
@@ -257,11 +258,12 @@ struct vop_data {
/*
* display output interface supported by rockchip lcdc
*/
-#define ROCKCHIP_OUT_MODE_P888 0
-#define ROCKCHIP_OUT_MODE_P666 1
-#define ROCKCHIP_OUT_MODE_P565 2
+#define ROCKCHIP_OUT_MODE_P888 0
+#define ROCKCHIP_OUT_MODE_P666 1
+#define ROCKCHIP_OUT_MODE_P565 2
+#define ROCKCHIP_OUT_MODE_YUV420 14
/* for use special outface */
-#define ROCKCHIP_OUT_MODE_AAAA 15
+#define ROCKCHIP_OUT_MODE_AAAA 15
/* output flags */
#define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 800b9341dd42..dd4546f9f410 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -646,6 +646,7 @@ static const struct vop_common rk3288_common = {
.cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0),
.overlay_mode = VOP_REG(RK3288_SYS_CTRL, 0x1, 16),
+ .dclk_ddr = VOP_REG(RK3288_DSP_CTRL0, 0x1, 8),
.dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12),
.dsp_out_yuv = VOP_REG(RK3288_POST_SCL_CTRL, 0x1, 2),
.dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0),
@@ -1019,6 +1020,7 @@ static const struct vop_common rk3328_common = {
.cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0),
.overlay_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 16),
+ .dclk_ddr = VOP_REG(RK3328_DSP_CTRL0, 0x1, 8),
.dsp_data_swap = VOP_REG(RK3328_DSP_CTRL0, 0x1f, 12),
.dsp_out_yuv = VOP_REG(RK3328_POST_SCL_CTRL, 0x1, 2),
.dsp_background = VOP_REG(RK3328_DSP_BG, 0xffffffff, 0),
From bf070727c435df4b370aa6e7fdf0ecba2fd6985c Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 7 Jun 2020 20:25:25 +0000
Subject: [PATCH] drm: drm_fourcc: add NV20 and NV30 YUV formats
DRM_FORMAT_NV20 and DRM_FORMAT_NV30 formats is the 2x1 and non-subsampled
variant of NV15, a 10-bit 2-plane YUV format that has no padding between
components. Instead, luminance and chrominance samples are grouped into 4s
so that each group is packed into an integer number of bytes:
YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes
The '20' and '30' suffix refers to the optimum effective bits per pixel
which is achieved when the total number of luminance samples is a multiple
of 4.
V2: Added NV30 format
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/drm_fourcc.c | 8 ++++++++
include/uapi/drm/drm_fourcc.h | 2 ++
2 files changed, 10 insertions(+)
diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index 722c7ebe4e88..2daf8a304b53 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -278,6 +278,14 @@ const struct drm_format_info *__drm_format_info(u32 format)
.num_planes = 2, .char_per_block = { 5, 5, 0 },
.block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
.vsub = 2, .is_yuv = true },
+ { .format = DRM_FORMAT_NV20, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
+ .vsub = 1, .is_yuv = true },
+ { .format = DRM_FORMAT_NV30, .depth = 0,
+ .num_planes = 2, .char_per_block = { 5, 5, 0 },
+ .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 1,
+ .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_Q410, .depth = 0,
.num_planes = 3, .char_per_block = { 2, 2, 2 },
.block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 0,
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 82f327801267..d8e6159213dc 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -242,6 +242,8 @@ extern "C" {
* index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian
*/
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */
/*
* 2 plane YCbCr MSB aligned
From f4c0d6f87d4231a92f4337ba8d1b0913eacb963e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 7 Jun 2020 20:25:26 +0000
Subject: [PATCH] drm: rockchip: add NV15, NV20 and NV30 support
Add support for displaying 10-bit 4:2:0 and 4:2:2 formats produced by the
Rockchip Video Decoder on RK322X, RK3288, RK3328, RK3368 and RK3399.
Also add support for 10-bit 4:4:4 format while at it.
V2: Added NV30 support
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 29 +++++++++++++++++--
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 +
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 32 +++++++++++++++++----
3 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 5ea8031eb0f7..413534cf1a93 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -261,6 +261,18 @@ static bool has_rb_swapped(uint32_t format)
}
}
+static bool is_fmt_10(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_NV15:
+ case DRM_FORMAT_NV20:
+ case DRM_FORMAT_NV30:
+ return true;
+ default:
+ return false;
+ }
+}
+
static enum vop_data_format vop_convert_format(uint32_t format)
{
switch (format) {
@@ -276,10 +288,13 @@ static enum vop_data_format vop_convert_format(uint32_t format)
case DRM_FORMAT_BGR565:
return VOP_FMT_RGB565;
case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV15:
return VOP_FMT_YUV420SP;
case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV20:
return VOP_FMT_YUV422SP;
case DRM_FORMAT_NV24:
+ case DRM_FORMAT_NV30:
return VOP_FMT_YUV444SP;
default:
DRM_ERROR("unsupported format[%08x]\n", format);
@@ -946,7 +961,12 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
- offset = (src->x1 >> 16) * fb->format->cpp[0];
+ if (fb->format->block_w[0])
+ offset = (src->x1 >> 16) * fb->format->char_per_block[0] /
+ fb->format->block_w[0];
+ else
+ offset = (src->x1 >> 16) * fb->format->cpp[0];
+
offset += (src->y1 >> 16) * fb->pitches[0];
dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
@@ -972,6 +992,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
}
VOP_WIN_SET(vop, win, format, format);
+ VOP_WIN_SET(vop, win, fmt_10, is_fmt_10(fb->format->format));
VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
@@ -988,7 +1009,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
uv_obj = fb->obj[1];
rk_uv_obj = to_rockchip_obj(uv_obj);
- offset = (src->x1 >> 16) * bpp / hsub;
+ if (fb->format->block_w[1])
+ offset = (src->x1 >> 16) * bpp /
+ fb->format->block_w[1] / hsub;
+ else
+ offset = (src->x1 >> 16) * bpp / hsub;
offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 8e6e999e5163..9f50e0e00127 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -161,6 +161,7 @@ struct vop_win_phy {
struct vop_reg enable;
struct vop_reg gate;
struct vop_reg format;
+ struct vop_reg fmt_10;
struct vop_reg rb_swap;
struct vop_reg act_info;
struct vop_reg dsp_info;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index dd4546f9f410..7d5191421ddf 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -50,6 +50,23 @@ static const uint32_t formats_win_full[] = {
DRM_FORMAT_NV24,
};
+static const uint32_t formats_win_full_10[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV24,
+ DRM_FORMAT_NV15,
+ DRM_FORMAT_NV20,
+ DRM_FORMAT_NV30,
+};
+
static const uint64_t format_modifiers_win_full[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID,
@@ -579,11 +596,12 @@ static const struct vop_scl_regs rk3288_win_full_scl = {
static const struct vop_win_phy rk3288_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
+ .data_formats = formats_win_full_10,
+ .nformats = ARRAY_SIZE(formats_win_full_10),
.format_modifiers = format_modifiers_win_full,
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
@@ -720,11 +738,12 @@ static const struct vop_intr rk3368_vop_intr = {
static const struct vop_win_phy rk3368_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
+ .data_formats = formats_win_full_10,
+ .nformats = ARRAY_SIZE(formats_win_full_10),
.format_modifiers = format_modifiers_win_full,
.enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
.x_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 21),
.y_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 22),
@@ -871,11 +890,12 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = {
static const struct vop_win_phy rk3399_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full,
- .nformats = ARRAY_SIZE(formats_win_full),
+ .data_formats = formats_win_full_10,
+ .nformats = ARRAY_SIZE(formats_win_full_10),
.format_modifiers = format_modifiers_win_full_afbc,
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
.y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
From e6aab013424e8e5a033f2da27757735f2a31c24e Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sat, 15 Aug 2020 21:11:08 +0200
Subject: [PATCH] drm/rockchip: rk3368's vop does not support 10-bit formats -
neither as input nor as output
---
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 7d5191421ddf..20c3e6248ec7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -738,8 +738,8 @@ static const struct vop_intr rk3368_vop_intr = {
static const struct vop_win_phy rk3368_win01_data = {
.scl = &rk3288_win_full_scl,
- .data_formats = formats_win_full_10,
- .nformats = ARRAY_SIZE(formats_win_full_10),
+ .data_formats = formats_win_full,
+ .nformats = ARRAY_SIZE(formats_win_full),
.format_modifiers = format_modifiers_win_full,
.enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
From e3b1ffdb8a4620cb05ad1a2fb3ca6c398bf6470f Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sat, 15 Aug 2020 23:20:34 +0200
Subject: [PATCH] drm/rockchip: enable ycbcr_420_allowed and ycbcr_444_allowed
for RK3228
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 1abc46a023a4..64a79b33ff18 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -555,6 +555,7 @@ static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
static struct rockchip_hdmi_chip_data rk3228_chip_data = {
.lcdsel_grf_reg = -1,
+ .ycbcr_444_allowed = true,
};
static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
@@ -563,6 +564,7 @@ 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,
+ .ycbcr_420_allowed = true,
};
static struct rockchip_hdmi_chip_data rk3288_chip_data = {
From 8bdc71e93099e4a8e3fd8791832a83dfe485cd99 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Wed, 22 Jul 2020 20:13:28 +0200
Subject: [PATCH] drm: rockchip: add scaling for RK3036 win1
Add the registers needed to make scaling work on RK3036's win1.
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 20c3e6248ec7..93a00b6ac295 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -94,15 +94,20 @@ static const uint64_t format_modifiers_win_lite[] = {
DRM_FORMAT_MOD_INVALID,
};
-static const struct vop_scl_regs rk3036_win_scl = {
+static const struct vop_scl_regs rk3036_win0_scl = {
.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
.scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
.scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
};
+static const struct vop_scl_regs rk3036_win1_scl = {
+ .scale_yrgb_x = VOP_REG(RK3036_WIN1_SCL_FACTOR_YRGB, 0xffff, 0x0),
+ .scale_yrgb_y = VOP_REG(RK3036_WIN1_SCL_FACTOR_YRGB, 0xffff, 16),
+};
+
static const struct vop_win_phy rk3036_win0_data = {
- .scl = &rk3036_win_scl,
+ .scl = &rk3036_win0_scl,
.data_formats = formats_win_full,
.nformats = ARRAY_SIZE(formats_win_full),
.format_modifiers = format_modifiers_win_full,
@@ -119,6 +124,7 @@ static const struct vop_win_phy rk3036_win0_data = {
};
static const struct vop_win_phy rk3036_win1_data = {
+ .scl = &rk3036_win1_scl,
.data_formats = formats_win_lite,
.nformats = ARRAY_SIZE(formats_win_lite),
.format_modifiers = format_modifiers_win_lite,
From e2153eb92daad4a2e35b86096cbfe5f3ec4dc5ed Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Wed, 22 Jul 2020 20:13:29 +0200
Subject: [PATCH] drm: rockchip: add missing registers for RK3188
Add dither_up, dsp_lut_en and data_blank registers to enable their
respective functionality for RK3188's VOP.
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 93a00b6ac295..2638d084f9ce 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -529,6 +529,9 @@ static const struct vop_common rk3188_common = {
.dither_down_en = VOP_REG(RK3188_DSP_CTRL0, 0x1, 11),
.dither_down_mode = VOP_REG(RK3188_DSP_CTRL0, 0x1, 10),
.dsp_blank = VOP_REG(RK3188_DSP_CTRL1, 0x3, 24),
+ .dither_up = VOP_REG(RK3188_DSP_CTRL0, 0x1, 9),
+ .dsp_lut_en = VOP_REG(RK3188_SYS_CTRL, 0x1, 28),
+ .data_blank = VOP_REG(RK3188_DSP_CTRL1, 0x1, 25),
};
static const struct vop_win_data rk3188_vop_win_data[] = {
From c0e58f92ce8d4a6d792fb3e49ec2c1e46039ae76 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Wed, 22 Jul 2020 20:13:30 +0200
Subject: [PATCH] drm: rockchip: add alpha support for RK3036, RK3066, RK3126
and RK3188
With commit 2aae8ed1f390
("drm/rockchip: Add per-pixel alpha support for the PX30 VOP") alpha
support was introduced for PX30's VOP.
RK3036, RK3066, RK3126 and RK3188 VOPs support alpha blending in the
same manner.
With the exception of RK3066 all of them support pre-multiplied alpha.
Lets add these registers to make this work for those VOPs as well.
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 21 +++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 2638d084f9ce..e1db4e57c51a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -121,6 +121,9 @@ static const struct vop_win_phy rk3036_win0_data = {
.uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
.uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+ .alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 18),
+ .alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 0),
+ .alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29),
};
static const struct vop_win_phy rk3036_win1_data = {
@@ -136,6 +139,9 @@ static const struct vop_win_phy rk3036_win1_data = {
.dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
.yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+ .alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 19),
+ .alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 1),
+ .alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29),
};
static const struct vop_win_data rk3036_vop_win_data[] = {
@@ -202,6 +208,9 @@ static const struct vop_win_phy rk3126_win1_data = {
.dsp_st = VOP_REG(RK3126_WIN1_DSP_ST, 0x1fff1fff, 0),
.yrgb_mst = VOP_REG(RK3126_WIN1_MST, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+ .alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 19),
+ .alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 1),
+ .alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29),
};
static const struct vop_win_data rk3126_vop_win_data[] = {
@@ -381,6 +390,8 @@ static const struct vop_win_phy rk3066_win0_data = {
.uv_mst = VOP_REG(RK3066_WIN0_CBR_MST0, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3066_WIN0_VIR, 0xffff, 0),
.uv_vir = VOP_REG(RK3066_WIN0_VIR, 0x1fff, 16),
+ .alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 21),
+ .alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 0),
};
static const struct vop_win_phy rk3066_win1_data = {
@@ -398,6 +409,8 @@ static const struct vop_win_phy rk3066_win1_data = {
.uv_mst = VOP_REG(RK3066_WIN1_CBR_MST, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3066_WIN1_VIR, 0xffff, 0),
.uv_vir = VOP_REG(RK3066_WIN1_VIR, 0x1fff, 16),
+ .alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 22),
+ .alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 1),
};
static const struct vop_win_phy rk3066_win2_data = {
@@ -411,6 +424,8 @@ static const struct vop_win_phy rk3066_win2_data = {
.dsp_st = VOP_REG(RK3066_WIN2_DSP_ST, 0x1fff1fff, 0),
.yrgb_mst = VOP_REG(RK3066_WIN2_MST, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3066_WIN2_VIR, 0xffff, 0),
+ .alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 23),
+ .alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 2),
};
static const struct vop_modeset rk3066_modeset = {
@@ -493,6 +508,9 @@ static const struct vop_win_phy rk3188_win0_data = {
.yrgb_mst = VOP_REG(RK3188_WIN0_YRGB_MST0, 0xffffffff, 0),
.uv_mst = VOP_REG(RK3188_WIN0_CBR_MST0, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 0),
+ .alpha_mode = VOP_REG(RK3188_DSP_CTRL0, 0x1, 18),
+ .alpha_en = VOP_REG(RK3188_ALPHA_CTRL, 0x1, 0),
+ .alpha_pre_mul = VOP_REG(RK3188_DSP_CTRL0, 0x1, 29),
};
static const struct vop_win_phy rk3188_win1_data = {
@@ -507,6 +525,9 @@ static const struct vop_win_phy rk3188_win1_data = {
.dsp_st = VOP_REG(RK3188_WIN1_DSP_ST, 0x0fff0fff, 0),
.yrgb_mst = VOP_REG(RK3188_WIN1_MST, 0xffffffff, 0),
.yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 16),
+ .alpha_mode = VOP_REG(RK3188_DSP_CTRL0, 0x1, 19),
+ .alpha_en = VOP_REG(RK3188_ALPHA_CTRL, 0x1, 1),
+ .alpha_pre_mul = VOP_REG(RK3188_DSP_CTRL0, 0x1, 29),
};
static const struct vop_modeset rk3188_modeset = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index 6e9fa5815d4d..0b3cd65ba5c1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -955,6 +955,7 @@
#define RK3188_DSP_CTRL0 0x04
#define RK3188_DSP_CTRL1 0x08
#define RK3188_INT_STATUS 0x10
+#define RK3188_ALPHA_CTRL 0x14
#define RK3188_WIN0_YRGB_MST0 0x20
#define RK3188_WIN0_CBR_MST0 0x24
#define RK3188_WIN0_YRGB_MST1 0x28
From 03532318ac2db5df4e83226628cb80399466bcd1 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Wed, 22 Jul 2020 20:13:31 +0200
Subject: [PATCH] drm: rockchip: set alpha_en to 0 if it is not used
alpha_en should be set to 0 if it is not used, i.e. to disable alpha
blending if it was enabled before and should be disabled now.
Fixes: 2aae8ed1f390 ("drm/rockchip: Add per-pixel alpha support for the PX30 VOP")
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 413534cf1a93..9b1cc0f413fc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1062,6 +1062,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
VOP_WIN_SET(vop, win, alpha_en, 1);
} else {
VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+ VOP_WIN_SET(vop, win, alpha_en, 0);
}
VOP_WIN_SET(vop, win, enable, 1);
From 5447465aa564564d938209b4c5a2675f8d744354 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sat, 15 Aug 2020 23:38:05 +0200
Subject: [PATCH] rockchip/drm: add dsp_data_swap register for RK3188
---
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index e1db4e57c51a..e10cb2d33951 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -553,6 +553,7 @@ static const struct vop_common rk3188_common = {
.dither_up = VOP_REG(RK3188_DSP_CTRL0, 0x1, 9),
.dsp_lut_en = VOP_REG(RK3188_SYS_CTRL, 0x1, 28),
.data_blank = VOP_REG(RK3188_DSP_CTRL1, 0x1, 25),
+ .dsp_data_swap = VOP_REG(RK3188_DSP_CTRL1, 0x1f, 26),
};
static const struct vop_win_data rk3188_vop_win_data[] = {
From ee7a494712813333d714ff7360dae7e91d8ebdc3 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sun, 16 Aug 2020 00:55:19 +0200
Subject: [PATCH] drm/rockchip: inno hdmi - add audio support - add required
aclk - fix video timing - fix phy pre-emphasis
---
.../display/rockchip/inno_hdmi-rockchip.txt | 6 +-
arch/arm/boot/dts/rk3036.dtsi | 24 +-
drivers/gpu/drm/rockchip/inno_hdmi.c | 266 +++++++++++++++++-
drivers/gpu/drm/rockchip/inno_hdmi.h | 2 +
4 files changed, 279 insertions(+), 19 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
index cec21714f0e0..b022c931e186 100644
--- a/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/inno_hdmi-rockchip.txt
@@ -7,7 +7,7 @@ Required properties:
- reg:
Physical base address and length of the controller's registers.
- clocks, clock-names:
- Phandle to hdmi controller clock, name should be "pclk"
+ Phandle to hdmi controller clock, name should be "aclk" and "pclk".
- interrupts:
HDMI interrupt number
- ports:
@@ -21,8 +21,8 @@ hdmi: hdmi@20034000 {
compatible = "rockchip,rk3036-inno-hdmi";
reg = <0x20034000 0x4000>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&cru PCLK_HDMI>;
- clock-names = "pclk";
+ clocks = <&cru ACLK_VIO>, <&cru PCLK_HDMI>;
+ clock-names = "aclk", "pclk";
pinctrl-names = "default";
pinctrl-0 = <&hdmi_ctl>;
diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi
index 838040b08967..4ffe72ab5f63 100644
--- a/arch/arm/boot/dts/rk3036.dtsi
+++ b/arch/arm/boot/dts/rk3036.dtsi
@@ -344,11 +344,14 @@ hdmi: hdmi@20034000 {
compatible = "rockchip,rk3036-inno-hdmi";
reg = <0x20034000 0x4000>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&cru PCLK_HDMI>;
- clock-names = "pclk";
+ clocks = <&cru ACLK_VIO>, <&cru PCLK_HDMI>;
+ clock-names = "aclk", "pclk";
rockchip,grf = <&grf>;
pinctrl-names = "default";
pinctrl-0 = <&hdmi_ctl>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #sound-dai-cells = <0>;
status = "disabled";
hdmi_in: port {
@@ -361,6 +364,23 @@ hdmi_in_vop: endpoint@0 {
};
};
+ hdmi_sound: hdmi-sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "HDMI";
+ status = "disabled";
+
+ simple-audio-card,dai-link {
+ format = "i2s";
+ mclk-fs = <256>;
+ cpu {
+ sound-dai = <&i2s>;
+ };
+ codec {
+ sound-dai = <&hdmi>;
+ };
+ };
+ };
+
timer: timer@20044000 {
compatible = "rockchip,rk3036-timer", "rockchip,rk3288-timer";
reg = <0x20044000 0x20>;
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 7afdc54eb3ec..7e93208609a0 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
@@ -21,6 +22,8 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
+#include <sound/hdmi-codec.h>
+
#include "rockchip_drm_drv.h"
#include "rockchip_drm_vop.h"
@@ -28,6 +31,12 @@
#define to_inno_hdmi(x) container_of(x, struct inno_hdmi, x)
+struct audio_info {
+ int sample_rate;
+ int channels;
+ int sample_width;
+};
+
struct hdmi_data_info {
int vic;
bool sink_is_hdmi;
@@ -52,8 +61,10 @@ struct inno_hdmi {
struct drm_device *drm_dev;
int irq;
+ struct clk *aclk;
struct clk *pclk;
void __iomem *regs;
+ struct regmap *regmap;
struct drm_connector connector;
struct drm_encoder encoder;
@@ -63,6 +74,9 @@ struct inno_hdmi {
unsigned int tmds_rate;
+ struct platform_device *audio_pdev;
+ bool audio_enable;
+
struct hdmi_data_info hdmi_data;
struct drm_display_mode previous_mode;
};
@@ -189,11 +203,17 @@ static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
{
+
+ u8 value;
+
switch (mode) {
case NORMAL:
inno_hdmi_sys_power(hdmi, false);
-
- hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
+ if (hdmi->tmds_rate > 140000000)
+ value = 0x6f;
+ else
+ value = 0x3f;
+ hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, value);
hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
@@ -301,6 +321,21 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
}
+static int inno_hdmi_config_audio_aai(struct inno_hdmi *hdmi,
+ struct audio_info *audio)
+{
+ struct hdmi_audio_infoframe *faudio;
+ union hdmi_infoframe frame;
+ int rc;
+
+ rc = hdmi_audio_infoframe_init(&frame.audio);
+ faudio = (struct hdmi_audio_infoframe *)&frame;
+
+ faudio->channels = audio->channels;
+
+ return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AAI, 0, 0, 0);
+}
+
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{
struct hdmi_data_info *data = &hdmi->hdmi_data;
@@ -383,6 +418,11 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
{
int value;
+ value = BIT(20) | BIT(21);
+ value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? BIT(4) : 0;
+ value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? BIT(5) : 0;
+ regmap_write(hdmi->regmap, 0x148, value);
+
/* Set detail external video timing polarity and interlace mode */
value = v_EXTERANL_VIDEO(1);
value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
@@ -402,7 +442,7 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
- value = mode->hsync_start - mode->hdisplay;
+ value = mode->htotal - mode->hsync_start;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
@@ -417,7 +457,7 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
value = mode->vtotal - mode->vdisplay;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
- value = mode->vsync_start - mode->vdisplay;
+ value = mode->vtotal - mode->vsync_start;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
value = mode->vsync_end - mode->vsync_start;
@@ -473,8 +513,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
inno_hdmi_i2c_init(hdmi);
/* Unmute video and audio output */
- hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
- v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+ hdmi_modb(hdmi, HDMI_AV_MUTE, m_VIDEO_BLACK, v_VIDEO_MUTE(0));
+ if (hdmi->audio_enable)
+ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE, v_AUDIO_MUTE(0));
return 0;
}
@@ -521,6 +562,7 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
s->output_mode = ROCKCHIP_OUT_MODE_P888;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+ s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
return 0;
}
@@ -597,6 +639,175 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
.mode_valid = inno_hdmi_connector_mode_valid,
};
+int inno_hdmi_audio_config_set(struct inno_hdmi *hdmi, struct audio_info *audio)
+{
+ int rate, N, channel;
+
+ if (audio->channels < 3)
+ channel = I2S_CHANNEL_1_2;
+ else if (audio->channels < 5)
+ channel = I2S_CHANNEL_3_4;
+ else if (audio->channels < 7)
+ channel = I2S_CHANNEL_5_6;
+ else
+ channel = I2S_CHANNEL_7_8;
+
+ switch (audio->sample_rate) {
+ case 32000:
+ rate = AUDIO_32K;
+ N = N_32K;
+ break;
+ case 44100:
+ rate = AUDIO_441K;
+ N = N_441K;
+ break;
+ case 48000:
+ rate = AUDIO_48K;
+ N = N_48K;
+ break;
+ case 88200:
+ rate = AUDIO_882K;
+ N = N_882K;
+ break;
+ case 96000:
+ rate = AUDIO_96K;
+ N = N_96K;
+ break;
+ case 176400:
+ rate = AUDIO_1764K;
+ N = N_1764K;
+ break;
+ case 192000:
+ rate = AUDIO_192K;
+ N = N_192K;
+ break;
+ default:
+ dev_err(hdmi->dev, "[%s] not support such sample rate %d\n",
+ __func__, audio->sample_rate);
+ return -ENOENT;
+ }
+
+ /* set_audio source I2S */
+ hdmi_writeb(hdmi, HDMI_AUDIO_CTRL1, 0x01);
+ hdmi_writeb(hdmi, AUDIO_SAMPLE_RATE, rate);
+ hdmi_writeb(hdmi, AUDIO_I2S_MODE, v_I2S_MODE(I2S_STANDARD) |
+ v_I2S_CHANNEL(channel));
+
+ hdmi_writeb(hdmi, AUDIO_I2S_MAP, 0x00);
+ hdmi_writeb(hdmi, AUDIO_I2S_SWAPS_SPDIF, 0);
+
+ /* Set N value */
+ hdmi_writeb(hdmi, AUDIO_N_H, (N >> 16) & 0x0F);
+ hdmi_writeb(hdmi, AUDIO_N_M, (N >> 8) & 0xFF);
+ hdmi_writeb(hdmi, AUDIO_N_L, N & 0xFF);
+
+ /*Set hdmi nlpcm mode to support hdmi bitstream*/
+ hdmi_writeb(hdmi, HDMI_AUDIO_CHANNEL_STATUS, v_AUDIO_STATUS_NLPCM(0));
+
+ return inno_hdmi_config_audio_aai(hdmi, audio);
+}
+
+static int inno_hdmi_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+ struct audio_info audio = {
+ .sample_width = params->sample_width,
+ .sample_rate = params->sample_rate,
+ .channels = params->channels,
+ };
+
+ if (!hdmi->hdmi_data.sink_has_audio) {
+ dev_err(hdmi->dev, "Sink do not support audio!\n");
+ return -ENODEV;
+ }
+
+ if (!hdmi->encoder.crtc)
+ return -ENODEV;
+
+ switch (daifmt->fmt) {
+ case HDMI_I2S:
+ break;
+ default:
+ dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+ return -EINVAL;
+ }
+
+ return inno_hdmi_audio_config_set(hdmi, &audio);
+}
+
+static void inno_hdmi_audio_shutdown(struct device *dev, void *data)
+{
+ /* do nothing */
+}
+
+static int inno_hdmi_audio_digital_mute(struct device *dev, void *data, bool mute)
+{
+ struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+
+ if (!hdmi->hdmi_data.sink_has_audio) {
+ dev_err(hdmi->dev, "Sink do not support audio!\n");
+ return -ENODEV;
+ }
+
+ hdmi->audio_enable = !mute;
+
+ if (mute)
+ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_AUDIO_PD,
+ v_AUDIO_MUTE(1) | v_AUDIO_PD(1));
+ else
+ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_AUDIO_PD,
+ v_AUDIO_MUTE(0) | v_AUDIO_PD(0));
+
+ return 0;
+}
+
+static int inno_hdmi_audio_get_eld(struct device *dev, void *data,
+ uint8_t *buf, size_t len)
+{
+ struct inno_hdmi *hdmi = dev_get_drvdata(dev);
+ struct drm_mode_config *config = &hdmi->encoder.dev->mode_config;
+ struct drm_connector *connector;
+ int ret = -ENODEV;
+
+ mutex_lock(&config->mutex);
+ list_for_each_entry(connector, &config->connector_list, head) {
+ if (&hdmi->encoder == connector->encoder) {
+ memcpy(buf, connector->eld,
+ min(sizeof(connector->eld), len));
+ ret = 0;
+ }
+ }
+ mutex_unlock(&config->mutex);
+
+ return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+ .hw_params = inno_hdmi_audio_hw_params,
+ .audio_shutdown = inno_hdmi_audio_shutdown,
+ //.digital_mute = inno_hdmi_audio_digital_mute,
+ .get_eld = inno_hdmi_audio_get_eld,
+};
+
+static int inno_hdmi_audio_codec_init(struct inno_hdmi *hdmi,
+ struct device *dev)
+{
+ struct hdmi_codec_pdata codec_data = {
+ .i2s = 1,
+ .ops = &audio_codec_ops,
+ .max_i2s_channels = 8,
+ };
+
+ hdmi->audio_enable = false;
+ hdmi->audio_pdev = platform_device_register_data(
+ dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_NONE,
+ &codec_data, sizeof(codec_data));
+
+ return PTR_ERR_OR_ZERO(hdmi->audio_pdev);
+}
+
static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
{
struct drm_encoder *encoder = &hdmi->encoder;
@@ -627,6 +838,8 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
drm_connector_attach_encoder(&hdmi->connector, encoder);
+ inno_hdmi_audio_codec_init(hdmi, dev);
+
return 0;
}
@@ -826,23 +1039,44 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
if (IS_ERR(hdmi->regs))
return PTR_ERR(hdmi->regs);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ hdmi->aclk = devm_clk_get(hdmi->dev, "aclk");
+ if (IS_ERR(hdmi->aclk)) {
+ dev_err(hdmi->dev, "Unable to get HDMI aclk clk\n");
+ return PTR_ERR(hdmi->aclk);
+ }
+
hdmi->pclk = devm_clk_get(hdmi->dev, "pclk");
if (IS_ERR(hdmi->pclk)) {
DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI pclk clk\n");
return PTR_ERR(hdmi->pclk);
}
+ ret = clk_prepare_enable(hdmi->aclk);
+ if (ret) {
+ DRM_DEV_ERROR(hdmi->dev,
+ "Cannot enable HDMI aclk clock: %d\n", ret);
+ return ret;
+ }
+
+
ret = clk_prepare_enable(hdmi->pclk);
if (ret) {
DRM_DEV_ERROR(hdmi->dev,
"Cannot enable HDMI pclk clock: %d\n", ret);
- return ret;
+ goto err_disable_aclk;
}
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = irq;
- goto err_disable_clk;
+ hdmi->regmap =
+ syscon_regmap_lookup_by_phandle(hdmi->dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(hdmi->regmap)) {
+ dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
+ ret = PTR_ERR(hdmi->regmap);
+ goto err_disable_aclk;
}
inno_hdmi_reset(hdmi);
@@ -851,7 +1085,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
if (IS_ERR(hdmi->ddc)) {
ret = PTR_ERR(hdmi->ddc);
hdmi->ddc = NULL;
- goto err_disable_clk;
+ goto err_disable_pclk;
}
/*
@@ -884,9 +1118,12 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
hdmi->encoder.funcs->destroy(&hdmi->encoder);
err_put_adapter:
i2c_put_adapter(hdmi->ddc);
-err_disable_clk:
+err_disable_pclk:
clk_disable_unprepare(hdmi->pclk);
- return ret;
+err_disable_aclk:
+ clk_disable_unprepare(hdmi->aclk);
+
+return ret;
}
static void inno_hdmi_unbind(struct device *dev, struct device *master,
@@ -899,6 +1136,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
i2c_put_adapter(hdmi->ddc);
clk_disable_unprepare(hdmi->pclk);
+ clk_disable_unprepare(hdmi->aclk);
}
static const struct component_ops inno_hdmi_ops = {
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.h b/drivers/gpu/drm/rockchip/inno_hdmi.h
index 93245b55f967..b722afc4e41f 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.h
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.h
@@ -96,11 +96,13 @@ enum {
#define HDMI_AV_MUTE 0x05
#define m_AVMUTE_CLEAR (1 << 7)
#define m_AVMUTE_ENABLE (1 << 6)
+#define m_AUDIO_PD (1 << 2)
#define m_AUDIO_MUTE (1 << 1)
#define m_VIDEO_BLACK (1 << 0)
#define v_AVMUTE_CLEAR(n) (n << 7)
#define v_AVMUTE_ENABLE(n) (n << 6)
#define v_AUDIO_MUTE(n) (n << 1)
+#define v_AUDIO_PD(n) (n << 2)
#define v_VIDEO_MUTE(n) (n << 0)
#define HDMI_VIDEO_TIMING_CTL 0x08
From 36a713bfb58368d37e19926d79709971183d86c8 Mon Sep 17 00:00:00 2001
From: Phong LE <ple@baylibre.com>
Date: Wed, 11 Mar 2020 13:51:33 +0100
Subject: [PATCH] dt-bindings: display: bridge: add it66121 bindings
Add the ITE bridge HDMI it66121 bindings.
Signed-off-by: Phong LE <ple@baylibre.com>
---
.../bindings/display/bridge/ite,it66121.yaml | 98 +++++++++++++++++++
1 file changed, 98 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
new file mode 100644
index 000000000000..1717e880d130
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/ite,it66121.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ITE it66121 HDMI bridge Device Tree Bindings
+
+maintainers:
+ - Phong LE <ple@baylibre.com>
+ - Neil Armstrong <narmstrong@baylibre.com>
+
+description: |
+ The IT66121 is a high-performance and low-power single channel HDMI
+ transmitter, fully compliant with HDMI 1.3a, HDCP 1.2 and backward compatible
+ to DVI 1.0 specifications.
+
+properties:
+ compatible:
+ const: ite,it66121
+
+ reg:
+ maxItems: 1
+ description: base I2C address of the device
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO connected to active low reset
+
+ vrf12-supply:
+ maxItems: 1
+ description: Regulator for 1.2V analog core power.
+
+ vcn33-supply:
+ maxItems: 1
+ description: Regulator for 3.3V digital core power.
+
+ vcn18-supply:
+ maxItems: 1
+ description: Regulator for 1.8V IO core power.
+
+ interrupts:
+ maxItems: 1
+
+ pclk-dual-edge:
+ maxItems: 1
+ description: enable pclk dual edge mode.
+
+ port:
+ type: object
+
+ properties:
+ endpoint:
+ type: object
+ description: |
+ Input endpoints of the bridge.
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - reset-gpios
+ - vrf12-supply
+ - vcn33-supply
+ - vcn18-supply
+ - interrupts
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c6 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ it66121hdmitx: it66121hdmitx@4c {
+ compatible = "ite,it66121";
+ pinctrl-names = "default";
+ pinctrl-0 = <&ite_pins_default>;
+ vcn33-supply = <&mt6358_vcn33_wifi_reg>;
+ vcn18-supply = <&mt6358_vcn18_reg>;
+ vrf12-supply = <&mt6358_vrf12_reg>;
+ reset-gpios = <&pio 160 1 /* GPIO_ACTIVE_LOW */>;
+ interrupt-parent = <&pio>;
+ interrupts = <4 8 /* IRQ_TYPE_LEVEL_LOW */>;
+ reg = <0x4c>;
+ pclk-dual-edge;
+
+ port {
+ it66121_in: endpoint {
+ remote-endpoint = <&display_out>;
+ };
+ };
+ };
+ };
From 55408304bc6a2c5e546743701e589f2ece3f3877 Mon Sep 17 00:00:00 2001
From: Phong LE <ple@baylibre.com>
Date: Wed, 11 Mar 2020 13:51:34 +0100
Subject: [PATCH] drm: bridge: add it66121 driver
This commit is a simple driver for bridge HMDI it66121.
The input format is RBG and there is no color conversion.
Audio, HDCP and CEC are not supported yet.
Signed-off-by: Phong LE <ple@baylibre.com>
---
drivers/gpu/drm/bridge/Kconfig | 8 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/ite-it66121.c | 997 +++++++++++++++++++++++++++
3 files changed, 1006 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/ite-it66121.c
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 43271c21d3fc..204246d65f44 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -48,6 +48,14 @@ config DRM_DISPLAY_CONNECTOR
on ARM-based platforms. Saying Y here when this driver is not needed
will not cause any issue.
+config DRM_ITE_IT66121
+ tristate "ITE IT66121 HDMI bridge"
+ depends on OF
+ select DRM_KMS_HELPER
+ select REGMAP_I2C
+ help
+ Support for ITE IT66121 HDMI bridge.
+
config DRM_LVDS_CODEC
tristate "Transparent LVDS encoders and decoders support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index d63d4b7e4347..ffa91a5a6bda 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
+obj-$(CONFIG_DRM_ITE_IT66121) += ite-it66121.o
obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
new file mode 100644
index 000000000000..7e1a90319a6a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -0,0 +1,997 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BayLibre, SAS
+ * Author: Phong LE <ple@baylibre.com>
+ * Copyright (C) 2018-2019, Artem Mygaiev
+ * Copyright (C) 2017, Fresco Logic, Incorporated.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#define IT66121_MASTER_SEL_REG 0x10
+#define IT66121_MASTER_SEL_HOST BIT(0)
+
+#define IT66121_AFE_DRV_REG 0x61
+#define IT66121_AFE_DRV_RST BIT(4)
+#define IT66121_AFE_DRV_PWD BIT(5)
+
+#define IT66121_INPUT_MODE_REG 0x70
+#define IT66121_INPUT_MODE_RGB (0 << 6)
+#define IT66121_INPUT_MODE_YUV422 BIT(6)
+#define IT66121_INPUT_MODE_YUV444 (2 << 6)
+#define IT66121_INPUT_MODE_CCIR656 BIT(4)
+#define IT66121_INPUT_MODE_SYNCEMB BIT(3)
+#define IT66121_INPUT_MODE_DDR BIT(2)
+
+#define IT66121_INPUT_CSC_REG 0x72
+#define IT66121_INPUT_CSC_ENDITHER BIT(7)
+#define IT66121_INPUT_CSC_ENUDFILTER BIT(6)
+#define IT66121_INPUT_CSC_DNFREE_GO BIT(5)
+#define IT66121_INPUT_CSC_RGB_TO_YUV 0x02
+#define IT66121_INPUT_CSC_YUV_TO_RGB 0x03
+#define IT66121_INPUT_CSC_NO_CONV 0x00
+
+#define IT66121_AFE_XP_REG 0x62
+#define IT66121_AFE_XP_GAINBIT BIT(7)
+#define IT66121_AFE_XP_PWDPLL BIT(6)
+#define IT66121_AFE_XP_ENI BIT(5)
+#define IT66121_AFE_XP_ENO BIT(4)
+#define IT66121_AFE_XP_RESETB BIT(3)
+#define IT66121_AFE_XP_PWDI BIT(2)
+
+#define IT66121_AFE_IP_REG 0x64
+#define IT66121_AFE_IP_GAINBIT BIT(7)
+#define IT66121_AFE_IP_PWDPLL BIT(6)
+#define IT66121_AFE_IP_CKSEL_05 (0 << 4)
+#define IT66121_AFE_IP_CKSEL_1 BIT(4)
+#define IT66121_AFE_IP_CKSEL_2 (2 << 4)
+#define IT66121_AFE_IP_CKSEL_2OR4 (3 << 4)
+#define IT66121_AFE_IP_ER0 BIT(3)
+#define IT66121_AFE_IP_RESETB BIT(2)
+#define IT66121_AFE_IP_ENC BIT(1)
+#define IT66121_AFE_IP_EC1 BIT(0)
+
+#define IT66121_AFE_XP_EC1_REG 0x68
+#define IT66121_AFE_XP_EC1_LOWCLK BIT(4)
+
+#define IT66121_SW_RST_REG 0x04
+#define IT66121_SW_RST_REF BIT(5)
+#define IT66121_SW_RST_AREF BIT(4)
+#define IT66121_SW_RST_VID BIT(3)
+#define IT66121_SW_RST_AUD BIT(2)
+#define IT66121_SW_RST_HDCP BIT(0)
+
+#define IT66121_DDC_COMMAND_REG 0x15
+#define IT66121_DDC_COMMAND_BURST_READ 0x0
+#define IT66121_DDC_COMMAND_EDID_READ 0x3
+#define IT66121_DDC_COMMAND_FIFO_CLR 0x9
+#define IT66121_DDC_COMMAND_SCL_PULSE 0xA
+#define IT66121_DDC_COMMAND_ABORT 0xF
+
+#define IT66121_HDCP_REG 0x20
+#define IT66121_HDCP_CPDESIRED BIT(0)
+#define IT66121_HDCP_EN1P1FEAT BIT(1)
+
+#define IT66121_INT_STATUS1_REG 0x06
+#define IT66121_INT_STATUS1_AUD_OVF BIT(7)
+#define IT66121_INT_STATUS1_DDC_NOACK BIT(5)
+#define IT66121_INT_STATUS1_DDC_FIFOERR BIT(4)
+#define IT66121_INT_STATUS1_DDC_BUSHANG BIT(2)
+#define IT66121_INT_STATUS1_RX_SENS_STATUS BIT(1)
+#define IT66121_INT_STATUS1_HPD_STATUS BIT(0)
+
+#define IT66121_DDC_HEADER_REG 0x11
+#define IT66121_DDC_HEADER_HDCP 0x74
+#define IT66121_DDC_HEADER_EDID 0xA0
+
+#define IT66121_DDC_OFFSET_REG 0x12
+#define IT66121_DDC_BYTE_REG 0x13
+#define IT66121_DDC_SEGMENT_REG 0x14
+#define IT66121_DDC_RD_FIFO_REG 0x17
+
+#define IT66121_CLK_BANK_REG 0x0F
+#define IT66121_CLK_BANK_PWROFF_RCLK BIT(6)
+#define IT66121_CLK_BANK_PWROFF_ACLK BIT(5)
+#define IT66121_CLK_BANK_PWROFF_TXCLK BIT(4)
+#define IT66121_CLK_BANK_PWROFF_CRCLK BIT(3)
+#define IT66121_CLK_BANK_0 0
+#define IT66121_CLK_BANK_1 1
+
+#define IT66121_INT_REG 0x05
+#define IT66121_INT_ACTIVE_HIGH BIT(7)
+#define IT66121_INT_OPEN_DRAIN BIT(6)
+#define IT66121_INT_TX_CLK_OFF BIT(0)
+
+#define IT66121_INT_MASK1_REG 0x09
+#define IT66121_INT_MASK1_AUD_OVF BIT(7)
+#define IT66121_INT_MASK1_DDC_NOACK BIT(5)
+#define IT66121_INT_MASK1_DDC_FIFOERR BIT(4)
+#define IT66121_INT_MASK1_DDC_BUSHANG BIT(2)
+#define IT66121_INT_MASK1_RX_SENS BIT(1)
+#define IT66121_INT_MASK1_HPD BIT(0)
+
+#define IT66121_INT_CLR1_REG 0x0C
+#define IT66121_INT_CLR1_PKTACP BIT(7)
+#define IT66121_INT_CLR1_PKTNULL BIT(6)
+#define IT66121_INT_CLR1_PKTGEN BIT(5)
+#define IT66121_INT_CLR1_KSVLISTCHK BIT(4)
+#define IT66121_INT_CLR1_AUTHDONE BIT(3)
+#define IT66121_INT_CLR1_AUTHFAIL BIT(2)
+#define IT66121_INT_CLR1_RX_SENS BIT(1)
+#define IT66121_INT_CLR1_HPD BIT(0)
+
+#define IT66121_AV_MUTE_REG 0xC1
+#define IT66121_AV_MUTE_ON BIT(0)
+#define IT66121_AV_MUTE_BLUESCR BIT(1)
+
+#define IT66121_PKT_GEN_CTRL_REG 0xC6
+#define IT66121_PKT_GEN_CTRL_ON BIT(0)
+#define IT66121_PKT_GEN_CTRL_RPT BIT(1)
+
+#define IT66121_AVIINFO_DB1_REG 0x158
+#define IT66121_AVIINFO_DB2_REG 0x159
+#define IT66121_AVIINFO_DB3_REG 0x15A
+#define IT66121_AVIINFO_DB4_REG 0x15B
+#define IT66121_AVIINFO_DB5_REG 0x15C
+#define IT66121_AVIINFO_CSUM_REG 0x15D
+#define IT66121_AVIINFO_DB6_REG 0x15E
+#define IT66121_AVIINFO_DB7_REG 0x15F
+#define IT66121_AVIINFO_DB8_REG 0x160
+#define IT66121_AVIINFO_DB9_REG 0x161
+#define IT66121_AVIINFO_DB10_REG 0x162
+#define IT66121_AVIINFO_DB11_REG 0x163
+#define IT66121_AVIINFO_DB12_REG 0x164
+#define IT66121_AVIINFO_DB13_REG 0x165
+
+#define IT66121_AVI_INFO_PKT_REG 0xCD
+#define IT66121_AVI_INFO_PKT_ON BIT(0)
+#define IT66121_AVI_INFO_PKT_RPT BIT(1)
+
+#define IT66121_HDMI_MODE_REG 0xC0
+#define IT66121_HDMI_MODE_HDMI BIT(0)
+
+#define IT66121_SYS_STATUS_REG 0x0E
+#define IT66121_SYS_STATUS_ACTIVE_IRQ BIT(7)
+#define IT66121_SYS_STATUS_HPDETECT BIT(6)
+#define IT66121_SYS_STATUS_SENDECTECT BIT(5)
+#define IT66121_SYS_STATUS_VID_STABLE BIT(4)
+#define IT66121_SYS_STATUS_AUD_CTS_CLR BIT(1)
+#define IT66121_SYS_STATUS_CLEAR_IRQ BIT(0)
+
+#define IT66121_DDC_STATUS_REG 0x16
+#define IT66121_DDC_STATUS_TX_DONE BIT(7)
+#define IT66121_DDC_STATUS_ACTIVE BIT(6)
+#define IT66121_DDC_STATUS_NOACK BIT(5)
+#define IT66121_DDC_STATUS_WAIT_BUS BIT(4)
+#define IT66121_DDC_STATUS_ARBI_LOSE BIT(3)
+#define IT66121_DDC_STATUS_FIFO_FULL BIT(2)
+#define IT66121_DDC_STATUS_FIFO_EMPTY BIT(1)
+#define IT66121_DDC_STATUS_FIFO_VALID BIT(0)
+
+#define IT66121_VENDOR_ID0 0x54
+#define IT66121_VENDOR_ID1 0x49
+#define IT66121_DEVICE_ID0 0x12
+#define IT66121_DEVICE_ID1 0x06
+#define IT66121_DEVICE_MASK 0x0F
+#define IT66121_EDID_SLEEP 20000
+#define IT66121_EDID_TIMEOUT 200000
+#define IT66121_EDID_FIFO_SIZE 32
+#define IT66121_AFE_CLK_HIGH 80000
+
+struct it66121_conf {
+ unsigned int input_mode_reg;
+ unsigned int input_conversion_reg;
+};
+
+struct it66121_ctx {
+ struct regmap *regmap;
+ struct drm_bridge bridge;
+ struct drm_connector connector;
+ struct device *dev;
+ struct gpio_desc *gpio_reset;
+ struct i2c_client *client;
+ struct regulator_bulk_data supplies[3];
+ bool dual_edge;
+ const struct it66121_conf *conf;
+ struct mutex lock; /* Protects fields below and device registers */
+ struct edid *edid;
+ struct hdmi_avi_infoframe hdmi_avi_infoframe;
+};
+
+static const struct regmap_range_cfg it66121_regmap_banks[] = {
+ {
+ .name = "it66121",
+ .range_min = 0x00,
+ .range_max = 0x1FF,
+ .selector_reg = IT66121_CLK_BANK_REG,
+ .selector_mask = 0x1,
+ .selector_shift = 0,
+ .window_start = 0x00,
+ .window_len = 0x130,
+ },
+};
+
+static const struct regmap_config it66121_regmap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = 0x1FF,
+ .ranges = it66121_regmap_banks,
+ .num_ranges = ARRAY_SIZE(it66121_regmap_banks),
+};
+
+static const struct it66121_conf it66121_conf_simple = {
+ .input_mode_reg = IT66121_INPUT_MODE_RGB | IT66121_INPUT_MODE_DDR,
+ .input_conversion_reg = IT66121_INPUT_CSC_NO_CONV,
+};
+
+static void it66121_hw_reset(struct it66121_ctx *ctx)
+{
+ gpiod_set_value(ctx->gpio_reset, 1);
+ msleep(20);
+ gpiod_set_value(ctx->gpio_reset, 0);
+}
+
+static int ite66121_power_on(struct it66121_ctx *ctx)
+{
+ return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int ite66121_power_off(struct it66121_ctx *ctx)
+{
+ return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int it66121_preamble_ddc(struct it66121_ctx *ctx)
+{
+ return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG,
+ IT66121_MASTER_SEL_HOST);
+}
+
+static int it66121_fire_afe(struct it66121_ctx *ctx)
+{
+ return regmap_write(ctx->regmap, IT66121_AFE_DRV_REG, 0);
+}
+
+static int it66121_configure_input(struct it66121_ctx *ctx)
+{
+ int ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_INPUT_MODE_REG,
+ ctx->conf->input_mode_reg);
+ if (ret)
+ return ret;
+
+ return regmap_write(ctx->regmap, IT66121_INPUT_CSC_REG,
+ ctx->conf->input_conversion_reg);
+}
+
+/**
+ * it66121_configure_afe() - Configure the analog front end
+ * @ctx: it66121_ctx object
+ *
+ * RETURNS:
+ * zero if success, a negative error code otherwise.
+ */
+static int it66121_configure_afe(struct it66121_ctx *ctx,
+ const struct drm_display_mode *mode)
+{
+ int ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_AFE_DRV_REG,
+ IT66121_AFE_DRV_RST);
+ if (ret)
+ return ret;
+
+ if (mode->clock > IT66121_AFE_CLK_HIGH) {
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
+ IT66121_AFE_XP_GAINBIT |
+ IT66121_AFE_XP_ENO,
+ IT66121_AFE_XP_GAINBIT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
+ IT66121_AFE_IP_GAINBIT |
+ IT66121_AFE_IP_ER0 |
+ IT66121_AFE_IP_EC1,
+ IT66121_AFE_IP_GAINBIT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG,
+ IT66121_AFE_XP_EC1_LOWCLK, 0x80);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
+ IT66121_AFE_XP_GAINBIT |
+ IT66121_AFE_XP_ENO,
+ IT66121_AFE_XP_ENO);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
+ IT66121_AFE_IP_GAINBIT |
+ IT66121_AFE_IP_ER0 |
+ IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 |
+ IT66121_AFE_IP_EC1);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG,
+ IT66121_AFE_XP_EC1_LOWCLK,
+ IT66121_AFE_XP_EC1_LOWCLK);
+ if (ret)
+ return ret;
+ }
+
+ /* Clear reset flags */
+ ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG,
+ IT66121_SW_RST_REF | IT66121_SW_RST_VID,
+ ~(IT66121_SW_RST_REF | IT66121_SW_RST_VID) &
+ 0xFF);
+ if (ret)
+ return ret;
+
+ return it66121_fire_afe(ctx);
+}
+
+static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx)
+{
+ int ret, val;
+
+ ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG,
+ val, true,
+ IT66121_EDID_SLEEP,
+ IT66121_EDID_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (val & (IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS |
+ IT66121_DDC_STATUS_ARBI_LOSE))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx)
+{
+ int ret;
+
+ ret = it66121_preamble_ddc(ctx);
+ if (ret)
+ return ret;
+
+ return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,
+ IT66121_DDC_COMMAND_FIFO_CLR);
+}
+
+static int it66121_abort_ddc_ops(struct it66121_ctx *ctx)
+{
+ int ret;
+ unsigned int swreset, cpdesire;
+
+ ret = regmap_read(ctx->regmap, IT66121_SW_RST_REG, &swreset);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(ctx->regmap, IT66121_HDCP_REG, &cpdesire);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_HDCP_REG,
+ cpdesire & (~IT66121_HDCP_CPDESIRED & 0xFF));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_SW_RST_REG,
+ swreset | IT66121_SW_RST_HDCP);
+ if (ret)
+ return ret;
+
+ ret = it66121_preamble_ddc(ctx);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,
+ IT66121_DDC_COMMAND_ABORT);
+ if (ret)
+ return ret;
+
+ return it66121_wait_ddc_ready(ctx);
+}
+
+static int it66121_get_edid_block(void *context, u8 *buf,
+ unsigned int block, size_t len)
+{
+ struct it66121_ctx *ctx = context;
+ unsigned int val;
+ int remain = len;
+ int offset = 0;
+ int ret, cnt;
+
+ offset = (block % 2) * len;
+ block = block / 2;
+
+ ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);
+ if (ret)
+ return ret;
+
+ if (val & IT66121_INT_STATUS1_DDC_BUSHANG) {
+ ret = it66121_abort_ddc_ops(ctx);
+ if (ret)
+ return ret;
+ }
+
+ ret = it66121_clear_ddc_fifo(ctx);
+ if (ret)
+ return ret;
+
+ while (remain > 0) {
+ cnt = (remain > IT66121_EDID_FIFO_SIZE) ?
+ IT66121_EDID_FIFO_SIZE : remain;
+ ret = it66121_preamble_ddc(ctx);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,
+ IT66121_DDC_COMMAND_FIFO_CLR);
+ if (ret)
+ return ret;
+
+ ret = it66121_wait_ddc_ready(ctx);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);
+ if (ret)
+ return ret;
+
+ if (val & IT66121_INT_STATUS1_DDC_BUSHANG) {
+ ret = it66121_abort_ddc_ops(ctx);
+ if (ret)
+ return ret;
+ }
+
+ ret = it66121_preamble_ddc(ctx);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG,
+ IT66121_DDC_HEADER_EDID);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_BYTE_REG, cnt);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_SEGMENT_REG, block);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG,
+ IT66121_DDC_COMMAND_EDID_READ);
+ if (ret)
+ return ret;
+
+ offset += cnt;
+ remain -= cnt;
+ msleep(20);
+
+ ret = it66121_wait_ddc_ready(ctx);
+ if (ret)
+ return ret;
+
+ do {
+ ret = regmap_read(ctx->regmap,
+ IT66121_DDC_RD_FIFO_REG, &val);
+ if (ret)
+ return ret;
+ *(buf++) = val;
+ cnt--;
+ } while (cnt > 0);
+ }
+
+ return 0;
+}
+
+static int it66121_connector_get_modes(struct drm_connector *connector)
+{
+ int ret, num_modes = 0;
+ struct it66121_ctx *ctx = container_of(connector, struct it66121_ctx,
+ connector);
+
+ if (ctx->edid)
+ return drm_add_edid_modes(connector, ctx->edid);
+
+ mutex_lock(&ctx->lock);
+
+ ctx->edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx);
+ if (!ctx->edid) {
+ DRM_ERROR("Failed to read EDID\n");
+ goto unlock;
+ }
+
+ ret = drm_connector_update_edid_property(connector,
+ ctx->edid);
+ if (ret) {
+ DRM_ERROR("Failed to update EDID property: %d\n", ret);
+ goto unlock;
+ }
+
+ num_modes = drm_add_edid_modes(connector, ctx->edid);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+
+ return num_modes;
+}
+
+static bool it66121_is_hpd_detect(struct it66121_ctx *ctx)
+{
+ int val;
+
+ if (regmap_read(ctx->regmap, IT66121_SYS_STATUS_REG, &val))
+ return false;
+
+ return (val & IT66121_SYS_STATUS_HPDETECT);
+}
+
+static int it66121_connector_detect_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *c,
+ bool force)
+{
+ struct it66121_ctx *ctx = container_of(connector, struct it66121_ctx,
+ connector);
+
+ return (it66121_is_hpd_detect(ctx)) ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static enum drm_mode_status
+it66121_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ unsigned long max_clock;
+ struct it66121_ctx *ctx = container_of(connector, struct it66121_ctx,
+ connector);
+
+ max_clock = ctx->dual_edge ? 74250 : 148500;
+
+ if (mode->clock > max_clock)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock < 25000)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs it66121_connector_helper_funcs = {
+ .get_modes = it66121_connector_get_modes,
+ .detect_ctx = it66121_connector_detect_ctx,
+ .mode_valid = it66121_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs it66121_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int it66121_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ int ret;
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx,
+ bridge);
+
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
+ DRM_ERROR("Fix bridge driver to make connector optional!");
+ return -EINVAL;
+ }
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_RCLK, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG,
+ IT66121_INT_TX_CLK_OFF, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_DRV_REG,
+ IT66121_AFE_DRV_PWD, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
+ IT66121_AFE_XP_PWDI | IT66121_AFE_XP_PWDPLL, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
+ IT66121_AFE_IP_PWDPLL, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_DRV_REG,
+ IT66121_AFE_DRV_RST, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
+ IT66121_AFE_XP_RESETB, IT66121_AFE_XP_RESETB);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
+ IT66121_AFE_IP_RESETB, IT66121_AFE_IP_RESETB);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG,
+ IT66121_SW_RST_REF,
+ IT66121_SW_RST_REF);
+ if (ret)
+ return ret;
+
+ msleep(50);
+
+ ret = drm_connector_init(bridge->dev, &ctx->connector,
+ &it66121_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ return ret;
+
+ ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ drm_connector_helper_add(&ctx->connector,
+ &it66121_connector_helper_funcs);
+
+ ret = drm_connector_attach_encoder(&ctx->connector, bridge->encoder);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_register(&ctx->connector);
+ if (ret)
+ return ret;
+
+ /* Start interrupts */
+ return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG,
+ IT66121_INT_MASK1_DDC_NOACK |
+ IT66121_INT_MASK1_HPD |
+ IT66121_INT_MASK1_DDC_FIFOERR |
+ IT66121_INT_MASK1_DDC_BUSHANG,
+ ~(IT66121_INT_MASK1_DDC_NOACK |
+ IT66121_INT_MASK1_HPD |
+ IT66121_INT_MASK1_DDC_FIFOERR |
+ IT66121_INT_MASK1_DDC_BUSHANG) & 0xFF);
+}
+
+static int it66121_set_mute(struct it66121_ctx *ctx, bool mute)
+{
+ int ret;
+ unsigned int val;
+
+ val = mute ? IT66121_AV_MUTE_ON : (~IT66121_AV_MUTE_ON & 0xFF);
+ ret = regmap_write_bits(ctx->regmap, IT66121_AV_MUTE_REG,
+ IT66121_AV_MUTE_ON, val);
+ if (ret)
+ return ret;
+
+ return regmap_write(ctx->regmap, IT66121_PKT_GEN_CTRL_REG,
+ IT66121_PKT_GEN_CTRL_ON |
+ IT66121_PKT_GEN_CTRL_RPT);
+}
+
+static void it66121_bridge_enable(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx,
+ bridge);
+
+ it66121_set_mute(ctx, false);
+}
+
+static void it66121_bridge_disable(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx,
+ bridge);
+
+ it66121_set_mute(ctx, true);
+}
+
+static
+void it66121_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ int ret, i;
+ u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx,
+ bridge);
+ const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = {
+ IT66121_AVIINFO_DB1_REG,
+ IT66121_AVIINFO_DB2_REG,
+ IT66121_AVIINFO_DB3_REG,
+ IT66121_AVIINFO_DB4_REG,
+ IT66121_AVIINFO_DB5_REG,
+ IT66121_AVIINFO_DB6_REG,
+ IT66121_AVIINFO_DB7_REG,
+ IT66121_AVIINFO_DB8_REG,
+ IT66121_AVIINFO_DB9_REG,
+ IT66121_AVIINFO_DB10_REG,
+ IT66121_AVIINFO_DB11_REG,
+ IT66121_AVIINFO_DB12_REG,
+ IT66121_AVIINFO_DB13_REG
+ };
+
+ mutex_lock(&ctx->lock);
+
+ hdmi_avi_infoframe_init(&ctx->hdmi_avi_infoframe);
+
+ ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe,
+ &ctx->connector,
+ adjusted_mode);
+ if (ret) {
+ DRM_ERROR("Failed to setup AVI infoframe: %d\n", ret);
+ goto unlock;
+ }
+
+ ret = hdmi_avi_infoframe_pack(&ctx->hdmi_avi_infoframe, buf,
+ sizeof(buf));
+ if (ret < 0) {
+ DRM_ERROR("Failed to pack infoframe: %d\n", ret);
+ goto unlock;
+ }
+
+ /* Write new AVI infoframe packet */
+ for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) {
+ if (regmap_write(ctx->regmap, aviinfo_reg[i],
+ buf[i + HDMI_INFOFRAME_HEADER_SIZE]))
+ goto unlock;
+ }
+ if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3]))
+ goto unlock;
+
+ /* Enable AVI infoframe */
+ if (regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG,
+ IT66121_AVI_INFO_PKT_ON |
+ IT66121_AVI_INFO_PKT_RPT))
+ goto unlock;
+
+ /* Set TX mode to HDMI */
+ if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG,
+ IT66121_HDMI_MODE_HDMI))
+ goto unlock;
+
+ if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_TXCLK,
+ IT66121_CLK_BANK_PWROFF_TXCLK))
+ goto unlock;
+
+ if (it66121_configure_input(ctx))
+ goto unlock;
+
+ if (it66121_configure_afe(ctx, adjusted_mode))
+ goto unlock;
+
+ regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_TXCLK,
+ ~IT66121_CLK_BANK_PWROFF_TXCLK & 0xFF);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+}
+
+static const struct drm_bridge_funcs it66121_bridge_funcs = {
+ .attach = it66121_bridge_attach,
+ .enable = it66121_bridge_enable,
+ .disable = it66121_bridge_disable,
+ .mode_set = it66121_bridge_mode_set,
+};
+
+static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id)
+{
+ int ret;
+ unsigned int val;
+ struct it66121_ctx *ctx = dev_id;
+ struct device *dev = ctx->dev;
+ bool event = false;
+
+ mutex_lock(&ctx->lock);
+
+ ret = regmap_read(ctx->regmap, IT66121_SYS_STATUS_REG, &val);
+ if (ret)
+ goto unlock;
+
+ if (val & IT66121_SYS_STATUS_ACTIVE_IRQ) {
+ ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val);
+ if (ret) {
+ dev_err(dev, "Cannot read STATUS1_REG %d\n", ret);
+ } else {
+ if (val & IT66121_INT_STATUS1_DDC_FIFOERR)
+ it66121_clear_ddc_fifo(ctx);
+ if (val & (IT66121_INT_STATUS1_DDC_BUSHANG |
+ IT66121_INT_STATUS1_DDC_NOACK))
+ it66121_abort_ddc_ops(ctx);
+ if (val & IT66121_INT_STATUS1_HPD_STATUS) {
+ regmap_write_bits(ctx->regmap,
+ IT66121_INT_CLR1_REG,
+ IT66121_INT_CLR1_HPD,
+ IT66121_INT_CLR1_HPD);
+
+ if (!it66121_is_hpd_detect(ctx)) {
+ kfree(ctx->edid);
+ ctx->edid = NULL;
+ }
+ event = true;
+ }
+ }
+
+ regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG,
+ IT66121_SYS_STATUS_CLEAR_IRQ,
+ IT66121_SYS_STATUS_CLEAR_IRQ);
+ }
+
+unlock:
+ mutex_unlock(&ctx->lock);
+
+ if (event)
+ drm_helper_hpd_irq_event(ctx->bridge.dev);
+
+ return IRQ_HANDLED;
+}
+
+static int it66121_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ u8 ids[4];
+ int i, ret;
+ struct it66121_ctx *ctx;
+ struct device *dev = &client->dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(dev, "I2C check functionality failed.\n");
+ return -ENXIO;
+ }
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+ ctx->client = client;
+ i2c_set_clientdata(client, ctx);
+ mutex_init(&ctx->lock);
+ ctx->conf = (struct it66121_conf *)of_device_get_match_data(dev);
+ if (!ctx->conf)
+ return -ENODEV;
+
+ ctx->supplies[0].supply = "vcn33";
+ ctx->supplies[1].supply = "vcn18";
+ ctx->supplies[2].supply = "vrf12";
+ ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies);
+ if (ret) {
+ dev_err(ctx->dev, "regulator_bulk failed\n");
+ return ret;
+ }
+
+ ctx->dual_edge = of_property_read_bool(dev->of_node, "pclk-dual-edge");
+
+ ret = ite66121_power_on(ctx);
+ if (ret)
+ return ret;
+
+ it66121_hw_reset(ctx);
+
+ ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config);
+ if (IS_ERR(ctx->regmap)) {
+ ite66121_power_off(ctx);
+ return PTR_ERR(ctx);
+ }
+
+ for (i = 0; i < 4; i++) {
+ regmap_read(ctx->regmap, i, &ret);
+ ids[i] = ret;
+ }
+
+ if (ids[0] != IT66121_VENDOR_ID0 ||
+ ids[1] != IT66121_VENDOR_ID1 ||
+ ids[2] != IT66121_DEVICE_ID0 ||
+ ((ids[3] & IT66121_DEVICE_MASK) != IT66121_DEVICE_ID1)) {
+ ite66121_power_off(ctx);
+ return -ENODEV;
+ }
+
+ ctx->bridge.funcs = &it66121_bridge_funcs;
+ ctx->bridge.of_node = dev->of_node;
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ it66121_irq_threaded_handler,
+ IRQF_SHARED | IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT,
+ dev_name(dev),
+ ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret);
+ ite66121_power_off(ctx);
+ return ret;
+ }
+
+ drm_bridge_add(&ctx->bridge);
+
+ return 0;
+}
+
+static int it66121_remove(struct i2c_client *client)
+{
+ struct it66121_ctx *ctx = i2c_get_clientdata(client);
+
+ ite66121_power_off(ctx);
+ drm_bridge_remove(&ctx->bridge);
+ kfree(ctx->edid);
+ mutex_destroy(&ctx->lock);
+
+ return 0;
+}
+
+static const struct of_device_id it66121_dt_match[] = {
+ { .compatible = "ite,it66121",
+ .data = &it66121_conf_simple,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, it66121_dt_match);
+
+static const struct i2c_device_id it66121_id[] = {
+ { "it66121", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, it66121_id);
+
+static struct i2c_driver it66121_driver = {
+ .driver = {
+ .name = "it66121",
+ .of_match_table = it66121_dt_match,
+ },
+ .probe = it66121_probe,
+ .remove = it66121_remove,
+ .id_table = it66121_id,
+};
+
+module_i2c_driver(it66121_driver);
+
+MODULE_AUTHOR("Phong LE");
+MODULE_DESCRIPTION("IT66121 HDMI transmitter driver");
+MODULE_LICENSE("GPL v2");
From 16bbf295dbfda3cf315c62bbf42cd5e6a0796e45 Mon Sep 17 00:00:00 2001
From: Phong LE <ple@baylibre.com>
Date: Wed, 11 Mar 2020 13:51:35 +0100
Subject: [PATCH] MAINTAINERS: add it66121 HDMI bridge driver entry
Add Neil Armstrong and myself as maintainers
Signed-off-by: Phong LE <ple@baylibre.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0a14444b5f65..587c013211ae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9309,6 +9309,14 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
F: drivers/media/tuners/it913x*
+ITE IT66121 HDMI BRIDGE DRIVER
+M: Phong LE <ple@baylibre.com>
+M: Neil Armstrong <narmstrong@baylibre.com>
+S: Maintained
+F: drivers/gpu/drm/bridge/ite-it66121.c
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
+
IVTV VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: linux-media@vger.kernel.org
From 687354cbf754607f5a6746a85289ff1096cf0242 Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Tue, 26 May 2020 13:48:12 +0200
Subject: [PATCH] drm: bridge: it66121: add IT66121FN variant
---
drivers/gpu/drm/bridge/ite-it66121.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index 7e1a90319a6a..68f7e50fdddd 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -242,6 +242,11 @@ static const struct it66121_conf it66121_conf_simple = {
.input_conversion_reg = IT66121_INPUT_CSC_NO_CONV,
};
+static const struct it66121_conf it66121fn_conf_simple = {
+ .input_mode_reg = IT66121_INPUT_MODE_RGB,
+ .input_conversion_reg = IT66121_INPUT_CSC_NO_CONV,
+};
+
static void it66121_hw_reset(struct it66121_ctx *ctx)
{
gpiod_set_value(ctx->gpio_reset, 1);
@@ -970,6 +975,9 @@ static const struct of_device_id it66121_dt_match[] = {
{ .compatible = "ite,it66121",
.data = &it66121_conf_simple,
},
+ { .compatible = "ite,it66121fn",
+ .data = &it66121fn_conf_simple,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, it66121_dt_match);
From 2877f7c88109d3f0573073ca5183c8baef437c9d Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Sun, 16 Aug 2020 23:40:24 +0200
Subject: [PATCH] WIP: ARM: dts: rockchip add vpll clock to RK322Xs hdmi node
---
arch/arm/boot/dts/rk322x.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/rk322x.dtsi b/arch/arm/boot/dts/rk322x.dtsi
index 4bc631881c05..f98a945c68d3 100644
--- a/arch/arm/boot/dts/rk322x.dtsi
+++ b/arch/arm/boot/dts/rk322x.dtsi
@@ -766,8 +766,8 @@ hdmi: hdmi@200a0000 {
interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
assigned-clocks = <&cru SCLK_HDMI_PHY>;
assigned-clock-parents = <&hdmi_phy>;
- clocks = <&cru SCLK_HDMI_HDCP>, <&cru PCLK_HDMI_CTRL>, <&cru SCLK_HDMI_CEC>;
- clock-names = "isfr", "iahb", "cec";
+ clocks = <&cru SCLK_HDMI_HDCP>, <&cru PCLK_HDMI_CTRL>, <&hdmi_phy>, <&cru SCLK_HDMI_CEC>;
+ clock-names = "isfr", "iahb", "vpll", "cec";
pinctrl-names = "default";
pinctrl-0 = <&hdmii2c_xfer &hdmi_hpd &hdmi_cec>;
resets = <&cru SRST_HDMI_P>;
From d1df7c9067139e04b9e0f6c11e5b1b88d4a6a07c Mon Sep 17 00:00:00 2001
From: Alex Bee <knaerzche@gmail.com>
Date: Tue, 18 Aug 2020 11:19:53 +0200
Subject: [PATCH] drm/bridge: !cleanup: remove pr_infos
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
index e6953219beee..2cfd5b418c05 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
@@ -97,8 +97,6 @@ static int dw_hdmi_cec_transmit(struct cec_adapter *adap, u8 attempts,
struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
unsigned int i, ctrl;
- pr_info("%s: attempts=%u signal_free_time=%u msg=%*ph (sequence: %u)\n", __func__, attempts, signal_free_time, msg->len, msg->msg, msg->sequence);
-
switch (signal_free_time) {
case CEC_SIGNAL_FREE_TIME_RETRY:
ctrl = CEC_CTRL_RETRY;
@@ -188,8 +186,6 @@ static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data)
ret = IRQ_WAKE_THREAD;
}
- pr_info("%s: stat=%x ret=%x tx_done=%d rx_done=%d tx_status=%u tx_attempts=%u\n", __func__, stat, ret, cec->tx_done, cec->rx_done, cec->tx_status, cec->tx_attempts);
-
return ret;
}
@@ -198,8 +194,6 @@ static irqreturn_t dw_hdmi_cec_thread(int irq, void *data)
struct cec_adapter *adap = data;
struct dw_hdmi_cec *cec = cec_get_drvdata(adap);
- //pr_info("%s: tx_done=%d rx_done=%d tx_status=%u tx_attempts=%u\n", __func__, cec->tx_done, cec->rx_done, cec->tx_status, cec->tx_attempts);
-
if (cec->tx_done) {
cec->tx_done = false;
if (cec->tx_status == CEC_TX_STATUS_LOW_DRIVE)
From b145995aa4a89abae1799b469297fef1857d62b3 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 18 Nov 2017 11:09:39 +0100
Subject: [PATCH] rockchip: vop: force skip lines if image too big
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 9b1cc0f413fc..a34ff7593e1f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -932,6 +932,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
int format;
int is_yuv = fb->format->is_yuv;
int i;
+ int skiplines = 0;
/*
* can't update plane when vop is disabled.
@@ -950,8 +951,14 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
obj = fb->obj[0];
rk_obj = to_rockchip_obj(obj);
+ /*
+ * Force skip lines when image is yuv and 3840 width,
+ * fixes a "jumping" green lines issue on RK3328.
+ */
actual_w = drm_rect_width(src) >> 16;
- actual_h = drm_rect_height(src) >> 16;
+ if (actual_w == 3840 && is_yuv)
+ skiplines = 1;
+ actual_h = drm_rect_height(src) >> (16 + skiplines);
act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
dsp_info = (drm_rect_height(dest) - 1) << 16;
@@ -993,7 +1000,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
VOP_WIN_SET(vop, win, format, format);
VOP_WIN_SET(vop, win, fmt_10, is_fmt_10(fb->format->format));
- VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
+ VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4 >> skiplines));
VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
VOP_WIN_SET(vop, win, y_mir_en,
@@ -1017,7 +1024,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
- VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4));
+ VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4 >> skiplines));
VOP_WIN_SET(vop, win, uv_mst, dma_addr);
for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) {