Files
LibreELEC.tv/projects/Rockchip/patches/linux/default/linux-0006-experimental.patch
2019-12-31 09:09:33 +00:00

1771 lines
64 KiB
Diff

From e6c01bebbf7314ee8d7153f9cc641211f367558e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +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 9ca20c947283..b0ac1d3ee390 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -790,8 +790,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 8ce091601b75b00b550fc0c97147fb4b1b87a940 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +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 b0ac1d3ee390..093d2334e8cd 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -745,10 +745,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 baab1d214a00b07c10b0d56575d71dc57418345e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +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 093d2334e8cd..06db69c8373e 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -714,7 +714,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;
@@ -737,9 +737,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 be24cafb54f1985552ff1eff2d46f197073a8927 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +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 06db69c8373e..3a59a6da0440 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -1020,9 +1020,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;
@@ -1030,7 +1031,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 eddb4aea9de211d49574edd94f263423aea0719e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Fri, 20 Dec 2019 08:12:42 +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: 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 3a59a6da0440..3719309ad0d0 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -245,6 +245,7 @@ struct inno_hdmi_phy {
struct clk_hw hw;
struct clk *phyclk;
unsigned long pixclock;
+ unsigned long tmdsclock;
};
struct pre_pll_config {
@@ -485,6 +486,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;
@@ -509,6 +512,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;
@@ -628,6 +633,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);
@@ -670,6 +678,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
}
inno->pixclock = rate;
+ inno->tmdsclock = tmdsclock;
return 0;
}
@@ -781,6 +790,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);
@@ -820,6 +832,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
}
inno->pixclock = rate;
+ inno->tmdsclock = tmdsclock;
return 0;
}
From 4d1e6adf0979b6ecf4707c2cf0e1f0cf8a602e76 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 21 Dec 2019 23:52:16 +0000
Subject: [PATCH] drm/rockchip: vop: limit resolution width to 3840
Using a destination width that is more then 3840 pixels
is not supported in scl_vop_cal_scl_fac().
Work around this limitation by filtering all modes with
a width above 3840.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index d04b3492bdac..f181897cbfad 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1036,6 +1036,15 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
}
+enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ if (mode->hdisplay > 3840)
+ return MODE_BAD_HVALUE;
+
+ 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)
@@ -1377,6 +1386,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 7aac4403118cdb9f9afe5ca721b0918c3d522d80 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 12:43:36 +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 7f56d8c3491d..fae38b323a0c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -318,6 +318,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);
+
return phy_power_on(hdmi->phy);
}
From 9982b1998a3548b3b3ce4f3096d386e2253b8cbb Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 12:35:16 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: require valid vpll clock rate on
rk3228/rk3328
RK3228/RK3328 can only support clock rates defined in the pre pll table.
Lets validate the mode clock rate against the pre pll config and filter
out any mode with a clock rate returning error from clk_round_rate().
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index fae38b323a0c..45fcdce3f27f 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -245,6 +245,22 @@ static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
{
}
+static enum drm_mode_status
+dw_hdmi_rockchip_encoder_mode_valid(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode)
+{
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ long rate;
+
+ if (hdmi->vpll_clk) {
+ rate = clk_round_rate(hdmi->vpll_clk, mode->clock * 1000);
+ if (rate < 0)
+ return MODE_CLOCK_RANGE;
+ }
+
+ return MODE_OK;
+}
+
static bool
dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
@@ -306,6 +322,7 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
}
static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
+ .mode_valid = dw_hdmi_rockchip_encoder_mode_valid,
.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
.mode_set = dw_hdmi_rockchip_encoder_mode_set,
.enable = dw_hdmi_rockchip_encoder_enable,
From 9f54bf49e3fcee4489df5b75b5875792103f8001 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 23:42:45 +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 d17cfb7a3ff4..25f79af22cb8 100644
--- a/drivers/clk/rockchip/clk-rk3228.c
+++ b/drivers/clk/rockchip/clk-rk3228.c
@@ -410,7 +410,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 98f89f5897d0b265ad59aa07aed09f163a601bfe Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 15 Dec 2019 10:05:46 +0000
Subject: [PATCH] arm64: dts: rockchip: increase vop clock rate on rk3328
The VOP on RK3328 needs to run faster in order to procude 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 901c525deb27..7ea820904aad 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
@@ -817,8 +817,8 @@
<0>, <24000000>,
<24000000>, <24000000>,
<15000000>, <15000000>,
- <100000000>, <100000000>,
- <100000000>, <100000000>,
+ <300000000>, <100000000>,
+ <400000000>, <100000000>,
<50000000>, <100000000>,
<100000000>, <100000000>,
<50000000>, <50000000>,
From 977008094558ae64e311fa7007b289058f5d2dfe Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 23:43:40 +0000
Subject: [PATCH] arm64: dts: rockchip: add vpll clock to hdmi node on rk3328
Add the hdmiphy clock as the vpll in hdmi node.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
index 7ea820904aad..52033d55936d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
@@ -733,9 +733,11 @@
<GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_HDMI>,
<&cru SCLK_HDMI_SFC>,
+ <&hdmiphy>,
<&cru SCLK_RTC32K>;
clock-names = "iahb",
"isfr",
+ "vpll",
"cec";
phys = <&hdmiphy>;
phy-names = "hdmi";
From 73e80971172e55a9a105f46335c7851c0cdc8953 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 23:43:58 +0000
Subject: [PATCH] ARM: dts: rockchip: add vpll clock to hdmi node on rk3228
Add the hdmiphy clock as the vpll in hdmi node.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
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 340ed6ccb08f..16ad240d5f7f 100644
--- a/arch/arm/boot/dts/rk322x.dtsi
+++ b/arch/arm/boot/dts/rk322x.dtsi
@@ -639,8 +639,8 @@
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 c046025964f229cdbf9b2815165ad8a4ce82571f Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 10:42:02 +0000
Subject: [PATCH] drm/rockchip: dw-hdmi: limit tmds to 340mhz on rk3228/rk3328
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.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 45fcdce3f27f..66c14df4a680 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -237,6 +237,24 @@ dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
return (valid) ? MODE_OK : MODE_BAD;
}
+static enum drm_mode_status
+dw_hdmi_rk3228_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ struct drm_display_info *info = &connector->display_info;
+ int max_tmds_clock = max(info->max_tmds_clock, 165000);
+ int clock = mode->clock;
+
+ if (connector->ycbcr_420_allowed && drm_mode_is_420(info, mode) &&
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420))
+ clock /= 2;
+
+ if (clock > max_tmds_clock || clock > 340000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
@@ -424,7 +442,7 @@ 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,
+ .mode_valid = dw_hdmi_rk3228_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
@@ -461,7 +479,7 @@ 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,
+ .mode_valid = dw_hdmi_rk3228_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
From 0a0edf1dbfcbf305aa65774d2845cced564f9612 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 22 Dec 2019 10:42:27 +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 66c14df4a680..a813299e97a2 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -443,9 +443,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_rk3228_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",
@@ -480,9 +477,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_rk3228_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 a4e5019e8ae29f5ac56a1d34bc1bb695e92bdcce Mon Sep 17 00:00:00 2001
From: Algea Cao <algea.cao@rock-chips.com>
Date: Fri, 18 May 2018 14:32:08 +0800
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>
---
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 3719309ad0d0..bb8bdf5e3301 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -291,32 +291,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 2e9f27acab60593ec385a5e7e4570b2c52085562 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 | 121 +++++++++++++++++++-----------
1 file changed, 78 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index acc8f33c7020..5e214b0f5842 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1848,6 +1848,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_mode *mode)
{
@@ -1857,28 +1872,12 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
unsigned int vdisplay, hdisplay;
- vmode->mtmdsclock = vmode->mpixelclock = mode->clock * 1000;
+ 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);
-
- 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 = (u64)vmode->mpixelclock * 2;
- break;
- case 12:
- vmode->mtmdsclock = (u64)vmode->mpixelclock * 3 / 2;
- break;
- case 10:
- vmode->mtmdsclock = (u64)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 tmdsclk = %d\n", vmode->mtmdsclock);
/* Set up HDMI_FC_INVIDCONF */
@@ -2505,8 +2504,21 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
* - MEDIA_BUS_FMT_RGB888_1X24,
*/
-/* Can return a maximum of 12 possible output formats for a mode/connector */
-#define MAX_OUTPUT_SEL_FORMATS 12
+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 = max(info->max_tmds_clock, 165000);
+
+ if (max_tmds_clock >= tmdsclock)
+ return true;
+
+ return false;
+}
+
+/* Can return a maximum of 16 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS 16
static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
@@ -2518,8 +2530,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;
int i = 0;
@@ -2534,29 +2544,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;
}
/*
@@ -2565,40 +2579,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;
@@ -2805,12 +2830,22 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
{
struct dw_hdmi *hdmi = bridge->driver_private;
struct drm_connector *connector = &hdmi->connector;
+ struct drm_display_info *info = &connector->display_info;
enum drm_mode_status mode_status = MODE_OK;
+ int max_tmds_clock = max(info->max_tmds_clock, 165000);
+ int clock = mode->clock;
/* We don't support double-clocked modes */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
+ if (connector->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 (hdmi->plat_data->mode_valid)
mode_status = hdmi->plat_data->mode_valid(connector, mode);
From 3d9e14d176287ba537ef3eb2e60bc3d885c52895 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sat, 21 Dec 2019 06:23:52 +0000
Subject: [PATCH] WIP: drm/bridge: dw-hdmi: do not enable csc for yuv2yuv
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 5e214b0f5842..11b6676d9099 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -966,6 +966,14 @@ static void hdmi_video_sample(struct dw_hdmi *hdmi)
static int is_color_space_conversion(struct dw_hdmi *hdmi)
{
+ if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format) &&
+ hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+ return 0;
+
+ if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format) &&
+ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
+ return 0;
+
return hdmi->hdmi_data.enc_in_bus_format != hdmi->hdmi_data.enc_out_bus_format;
}
@@ -2044,7 +2052,9 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
/* Enable csc path */
- if (is_color_space_conversion(hdmi)) {
+ if (is_color_space_conversion(hdmi) ||
+ is_color_space_decimation(hdmi) ||
+ is_color_space_interpolation(hdmi)) {
hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
}
From 92a71467e4261914871947339573c17523d99d2f 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.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 65 ++++++++++++++---------------
1 file changed, 31 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index a813299e97a2..8bc42090d0a3 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -67,6 +67,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;
@@ -259,15 +260,11 @@ static const struct drm_encoder_funcs dw_hdmi_rockchip_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
-{
-}
-
static enum drm_mode_status
-dw_hdmi_rockchip_encoder_mode_valid(struct drm_encoder *encoder,
- const struct drm_display_mode *mode)
+dw_hdmi_rockchip_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
{
- struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
long rate;
if (hdmi->vpll_clk) {
@@ -279,26 +276,20 @@ dw_hdmi_rockchip_encoder_mode_valid(struct drm_encoder *encoder,
return MODE_OK;
}
-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;
-}
-
-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_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(encoder);
+ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(bridge);
- clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+ clk_set_rate(hdmi->vpll_clk, adjusted_mode->clock * 1000);
}
-static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
+static void dw_hdmi_rockchip_bridge_enable(struct drm_bridge *bridge)
{
- 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;
@@ -327,9 +318,10 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
}
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);
@@ -339,13 +331,11 @@ 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_valid = dw_hdmi_rockchip_encoder_mode_valid,
- .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 const struct drm_bridge_funcs dw_hdmi_rockchip_bridge_funcs = {
+ .mode_valid = dw_hdmi_rockchip_bridge_mode_valid,
+ .mode_set = dw_hdmi_rockchip_bridge_mode_set,
+ .enable = dw_hdmi_rockchip_bridge_enable,
+ .atomic_check = dw_hdmi_rockchip_bridge_atomic_check,
};
static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
@@ -523,6 +513,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;
@@ -576,13 +567,15 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
return ret;
}
- drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
+ hdmi->bridge.funcs = &dw_hdmi_rockchip_bridge_funcs;
+ drm_bridge_attach(encoder, &hdmi->bridge, NULL);
+
platform_set_drvdata(pdev, hdmi);
- hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
+ hdmi->hdmi = dw_hdmi_probe(pdev, plat_data);
/*
* If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
@@ -594,6 +587,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
clk_disable_unprepare(hdmi->vpll_clk);
}
+ next_bridge = of_drm_find_bridge(pdev->dev.of_node);
+ if (next_bridge)
+ drm_bridge_attach(encoder, next_bridge, &hdmi->bridge);
+
return ret;
}
From cc0d8618a2a77728151a1f418a8cd525752a8acd 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: enable bridge bus format negotiation
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 | 35 +++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8bc42090d0a3..83a22a061e52 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -317,6 +317,16 @@ static void dw_hdmi_rockchip_bridge_enable(struct drm_bridge *bridge)
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_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
@@ -331,11 +341,36 @@ dw_hdmi_rockchip_bridge_atomic_check(struct drm_bridge *bridge,
return 0;
}
+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_valid = dw_hdmi_rockchip_bridge_mode_valid,
.mode_set = dw_hdmi_rockchip_bridge_mode_set,
.enable = dw_hdmi_rockchip_bridge_enable,
.atomic_check = dw_hdmi_rockchip_bridge_atomic_check,
+ .atomic_get_input_bus_fmts = dw_hdmi_rockchip_get_input_bus_fmts,
};
static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
From 8a912c9841d570d159153b8dee4afb29c4c57a93 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 ++
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 ++++++-
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 83a22a061e52..3c8a95cd38dc 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -76,6 +76,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 c5b06048124e..af1b85d8f7ee 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -30,6 +30,8 @@ struct rockchip_crtc_state {
int output_mode;
int output_bpc;
int output_flags;
+ u32 bus_format;
+ int bus_width;
};
#define to_rockchip_crtc_state(s) \
container_of(s, struct rockchip_crtc_state, base)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index f181897cbfad..e59213d3ea62 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1402,13 +1402,21 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
{
- struct rockchip_crtc_state *rockchip_state;
+ struct rockchip_crtc_state *rockchip_state, *s;
+
+ if (WARN_ON(!crtc->state))
+ return NULL;
rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL);
if (!rockchip_state)
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base);
+
+ s = to_rockchip_crtc_state(crtc->state);
+ rockchip_state->bus_format = s->bus_format;
+ rockchip_state->bus_width = s->bus_width;
+
return &rockchip_state->base;
}
From 811047c36ad28926715e83e67a8d31d58bac57f7 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 | 29 ++++++++++++++++++++++++++++-
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 29 +++++++++++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 5 +++++
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 10 ++++++++++
4 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 3c8a95cd38dc..9b3c2318ce0e 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -61,6 +61,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);
@@ -539,6 +565,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 = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index e59213d3ea62..5bf833ea8ce2 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -274,6 +274,17 @@ static enum vop_data_format vop_convert_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:
+ 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)
@@ -1181,6 +1192,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) {
@@ -1254,6 +1266,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
@@ -1269,6 +1283,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 0b3d18c457b2..9393cf12b1db 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -77,6 +77,11 @@ 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 {
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 7a9d979c8d5d..b99e902d949e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -606,6 +606,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),
};
/*
@@ -914,6 +919,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 d667d43143d8cdb38a2b935670b814dbeb0959d3 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 | 22 ++++++++++++++++++++++
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, 47 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 9b3c2318ce0e..eb405cb3d1f6 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;
@@ -575,6 +596,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 = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 5bf833ea8ce2..b8c0d2fcc52a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -275,6 +275,19 @@ static enum vop_data_format vop_convert_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:
@@ -1266,7 +1279,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);
@@ -1283,6 +1296,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 9393cf12b1db..89fe8d5c7721 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -79,6 +79,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;
@@ -230,11 +231,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 b99e902d949e..73d24c6bbf05 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -608,6 +608,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),
@@ -921,6 +922,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 b2e3d3201407816f49a66ea70456fd4b83fb75cb Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 8 Dec 2019 23:49:00 +0000
Subject: [PATCH] WIP: drm/bridge: dw-hdmi: signal default quant range
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 11b6676d9099..4d6aace8dfc7 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -1667,6 +1667,9 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
break;
}
+ drm_hdmi_avi_infoframe_quant_range(&frame, &hdmi->connector, mode,
+ drm_default_rgb_quant_range(mode));
+
drm_hdmi_avi_infoframe_content_type(&frame, conn_state);
hdmi_infoframe_log(KERN_INFO, hdmi->dev, (union hdmi_infoframe *)&frame);
From 4ea71cee3402ece03d7f2bbb7b2cfa9faa62a42c 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: use avmute during modeset
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 4d6aace8dfc7..1bddef56eeb7 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2322,10 +2322,14 @@ static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
hdmi->bridge_is_on = true;
dw_hdmi_get_edid(&hdmi->connector);
dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+
+ hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
}
static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
{
+ hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
+
if (hdmi->phy.enabled) {
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
hdmi->phy.enabled = false;
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
index 27a91128d0cc..3810326794cc 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
@@ -842,6 +842,10 @@ enum {
HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
+/* HDMI_FC_GCP */
+ HDMI_FC_GCP_SET_AVMUTE = 0x2,
+ HDMI_FC_GCP_CLEAR_AVMUTE = 0x1,
+
/* FC_DBGFORCE field values */
HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,