From 0af28d066edbb4297d5140ff4c93db7b0bc0b182 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 35e601dd9730b541bde93ec5cd5320b2c14a84fa Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 61b85ebc7d075446c5f8a57607eb1fc87b16e2a8 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 a1675f5032286c3f2e939a7392361fc15099c0b7 Mon Sep 17 00:00:00 2001 From: Yakir Yang 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 Signed-off-by: Jonas Karlman --- 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 15a5f1ae8e0f570e81affb35b9b4aa4c0f485614 Mon Sep 17 00:00:00 2001 From: Nickey Yang 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 Signed-off-by: Jonas Karlman --- 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 43ac4790ecf27cf8db51e68298c541224af046a6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson 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 Signed-off-by: Yakir Yang Signed-off-by: Jonas Karlman --- 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 7604ef86c3d010fbfa61cf030b055b3860a53682 Mon Sep 17 00:00:00 2001 From: Douglas Anderson 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 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 Signed-off-by: Yakir Yang Signed-off-by: Jonas Karlman --- 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 119aca9dc20be1639fe8146237d2821695a2bc6b Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 9f31d6e1e2951eeaa979e1f9eb4b04cb64a4daf8 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 3 May 2020 22:36:23 +0000 Subject: [PATCH] drm/rockchip: dw-hdmi: limit resolution to 3840x2160 Signed-off-by: Jonas Karlman --- 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 bea1222407f1eb9d6af776d98eae5bd0f3544134 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 0bd355e25df8c93481ea6995bec7f452da97d5bf Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 2ac006e99c03..1f9176a5cc07 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 957c41ed6b5a182b12880b6f16f6e17d034bf483 Mon Sep 17 00:00:00 2001 From: Nickey Yang 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 Signed-off-by: Jonas Karlman --- 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 0f7295f13d592ba60dee79a0cc23302ed2febffa Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 e9d9694635e5cd9a84779e045c670239fc255606 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 2e18be0e366e9afcfe7dcdb69c3c90cce472bfa0 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 b2d23eae37a78a425144a21eb8bfbedc03edc754 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 f5dee5f447bb..3e44bf8eac5c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -1640,11 +1640,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>; @@ -1816,10 +1811,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 c94f4e569387f6874b2c96a3db684751529fb6b6 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 8af324175e002a9ee48be8618dce51ffc348a409 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 15 Jul 2020 15:24:47 +0000 Subject: [PATCH] drm/rockchip: vop: fix crtc duplicate state Signed-off-by: Jonas Karlman --- 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 cf2f0b3497dd7094b96d0f61d71e07927aae3e0c Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 1400714403b22c227d281c8fc5a3c07cc6312740 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 ea791312a81f11f719a019e30c7736e7ab314739 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 0c79a9ba48bb..50199329ad6f 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 0ea5bddefeeecd75d9dbee409ad8a8fddff6d378 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 50199329ad6f..2581789178c7 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 153e20ab5fd968cb5aff1e0cd7191d1ca1a6744e Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 2581789178c7..6d319b95b992 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 21174838c2d39a88ea46dbf64ced1ac133f32e7c Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 fbf69694a8f832cfa942f16a4bd8a630aa8f0f3f Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 6d319b95b992..c2425d7fc465 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 */ @@ -2544,8 +2541,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, @@ -2557,8 +2567,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; @@ -2581,29 +2589,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; } /* @@ -2612,40 +2624,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; @@ -2825,11 +2848,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 6e9f31fe455b92a19f12c4bae9c9f4b00efde58e Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 9784111ea746..ddff1582b271 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[] = { { @@ -242,6 +243,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); } @@ -280,6 +286,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: @@ -287,6 +294,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, @@ -294,9 +311,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; } @@ -308,10 +340,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 83871fb221f71065f78cff3c8019f8684481a155 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 c2425d7fc465..f86b8fa40ab6 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 @@ -2431,7 +2434,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); @@ -2499,6 +2503,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 553cc49748b8f0d074735f18cd45f050fca567e7 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 ddff1582b271..eea8e1491204 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 { @@ -294,10 +295,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; @@ -314,12 +327,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); @@ -353,7 +376,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); @@ -502,6 +528,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 = { @@ -517,6 +544,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 6127108151898a2c69fb50749d7e647ac35cf8aa Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 eea8e1491204..c25ebe31b98d 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -306,9 +306,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; @@ -345,6 +357,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); @@ -365,6 +382,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; @@ -379,6 +397,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; @@ -538,6 +559,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 = { @@ -554,6 +576,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 c70da56116a103e5f6bd154b7819217410a9c4f7 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 b41c8312d8bcd440bc42a799dd09b4acd9d4eca2 Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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 --- 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 478dd53361097d99541d777ce0f7fec7a79b7fa8 Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Sat, 15 Aug 2020 21:11:08 +0200 Subject: [PATCH] !fixup drm/rockchip: rk3368's vop does not support 10-bit formats --- 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 1f05ca5dfe7bd1553d32be9f450614b545be0912 Mon Sep 17 00:00:00 2001 From: Alex Bee 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 c25ebe31b98d..5562326e4bce 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -515,6 +515,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 = { @@ -523,6 +524,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 4273e39fec795fe18f83414655d30b0b9c5420d9 Mon Sep 17 00:00:00 2001 From: Alex Bee 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 --- 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 08b0db205ddef2853dd75c0691d7d6d6f94c611b Mon Sep 17 00:00:00 2001 From: Alex Bee 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 --- 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 77f8b49a4d17..27a04c5bc2fd 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 = { @@ -397,6 +408,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 = { @@ -410,6 +423,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 = { @@ -495,6 +510,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 = { @@ -509,6 +527,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 9be348c6cade7e709be7347d336c7638bf603b46 Mon Sep 17 00:00:00 2001 From: Alex Bee 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 27a04c5bc2fd..b47f036d4a2c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -555,6 +555,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 195b202dbc5abe9c65e029826a7f3e2a2d71067a Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Wed, 22 Jul 2020 20:22:02 +0200 Subject: [PATCH] rockchip/drm: add dsp_data_swap register for RK3066 Signed-off-by: Alex Bee --- 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 b47f036d4a2c..ae4a27704ad6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -448,6 +448,7 @@ static const struct vop_common rk3066_common = { .dither_up = VOP_REG(RK3066_DSP_CTRL0, 0x1, 9), .dsp_lut_en = VOP_REG(RK3066_SYS_CTRL1, 0x1, 31), .data_blank = VOP_REG(RK3066_DSP_CTRL1, 0x1, 25), + .dsp_data_swap = VOP_REG(RK3066_DSP_CTRL1, 0x1f, 26), }; static const struct vop_win_data rk3066_vop_win_data[] = { From 526739e44d5b0936db93bb92d2a98835723502c3 Mon Sep 17 00:00:00 2001 From: Alex Bee 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 = ; - 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 d7668a437c70..a86e1dc7fb5b 100644 --- a/arch/arm/boot/dts/rk3036.dtsi +++ b/arch/arm/boot/dts/rk3036.dtsi @@ -397,11 +397,14 @@ hdmi: hdmi@20034000 { compatible = "rockchip,rk3036-inno-hdmi"; reg = <0x20034000 0x4000>; interrupts = ; - 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 { @@ -414,6 +417,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 #include #include +#include #include #include @@ -21,6 +22,8 @@ #include #include +#include + #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 a8d962000848a237f8e8f0de8395d4e7df2a7197 Mon Sep 17 00:00:00 2001 From: Alex Bee 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 = ; 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 30c781872a7b883b18193fa5aed75c312b06f4ab Mon Sep 17 00:00:00 2001 From: Jonas Karlman 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++) { From b26ffb40570c456d0f84a3fa7886e2120c0397c2 Mon Sep 17 00:00:00 2001 From: Alex Bee 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 a52fbdfbb0bccb1202beb25b58bdc29517c178d2 Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 18 Aug 2020 23:15:45 +0200 Subject: [PATCH] HACK: drm/rockchip: return false in ... if no platform device is found This check will perfectly fail for bridges which are connected via i2c and are therefore no platform devices. TODO: look for a smarter solution Signed-off-by: Alex Bee --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index b7654f5e4225..44ec8003e2a4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -274,7 +274,7 @@ int rockchip_drm_endpoint_is_subdriver(struct device_node *ep) pdev = of_find_device_by_node(node); of_node_put(node); if (!pdev) - return -ENODEV; + return false; /* * All rockchip subdrivers have probed at this point, so From 1fca49c650ba0441613da75e43323afb5525c8de Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 18 Aug 2020 14:22:32 +0200 Subject: [PATCH] drm/bridge: it66121 fix regmap config registers 0x00~0x2f don't need/must not have a selector also: fix regmap range --- drivers/gpu/drm/bridge/ite-it66121.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 68f7e50fdddd..90f0881818d6 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -218,21 +218,21 @@ struct it66121_ctx { static const struct regmap_range_cfg it66121_regmap_banks[] = { { - .name = "it66121", - .range_min = 0x00, - .range_max = 0x1FF, + .name = "it66121-banks", + .range_min = 0x30, + .range_max = 0x1bf, .selector_reg = IT66121_CLK_BANK_REG, .selector_mask = 0x1, .selector_shift = 0, - .window_start = 0x00, - .window_len = 0x130, + .window_start = 0x30, + .window_len = 0xcf, }, }; static const struct regmap_config it66121_regmap_config = { .val_bits = 8, .reg_bits = 8, - .max_register = 0x1FF, + .max_register = 0x1bf, .ranges = it66121_regmap_banks, .num_ranges = ARRAY_SIZE(it66121_regmap_banks), }; From 525f46ff24e5db028d341d04d1aede0cfc672c39 Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 18 Aug 2020 17:29:54 +0200 Subject: [PATCH] drm/bridge: it66121 add detection message --- drivers/gpu/drm/bridge/ite-it66121.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 90f0881818d6..650dd2f063cc 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -936,8 +936,11 @@ static int it66121_probe(struct i2c_client *client, ids[2] != IT66121_DEVICE_ID0 || ((ids[3] & IT66121_DEVICE_MASK) != IT66121_DEVICE_ID1)) { ite66121_power_off(ctx); + DRM_INFO("HDMITX it66121 could not be indentified.\n"); return -ENODEV; - } + } else + DRM_INFO("HDMITX it66121 rev %d succsessfully indentified.\n", + ids[3] >> 4); ctx->bridge.funcs = &it66121_bridge_funcs; ctx->bridge.of_node = dev->of_node; From ff31ba7e3c18672f7fa3ac17f9399301cc55cacb Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 18 Aug 2020 22:28:21 +0200 Subject: [PATCH] drm/bridge it66121 add AFE_XP_PLL_CTRL settings Add IT66121_AFE_XP_PLL_CTRL settings, it's unknown what they actualy mean, since there is no documentation about it. Values have been taken from vendor driver and it helps stablize signal at 1080p modes. Signed-off-by: Alex Bee --- drivers/gpu/drm/bridge/ite-it66121.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 650dd2f063cc..bc6ca85e5ee1 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -72,6 +72,10 @@ #define IT66121_AFE_XP_EC1_REG 0x68 #define IT66121_AFE_XP_EC1_LOWCLK BIT(4) +#define IT66121_AFE_XP_PLL_CTRL 0x6a +#define IT66121_AFE_XP_PLL_HIGH_CLK_MASK (BIT(4) | BIT(5) | \ + BIT(6)) + #define IT66121_SW_RST_REG 0x04 #define IT66121_SW_RST_REF BIT(5) #define IT66121_SW_RST_AREF BIT(4) @@ -325,6 +329,13 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, IT66121_AFE_XP_EC1_LOWCLK, 0x80); if (ret) return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_PLL_CTRL, + IT66121_AFE_XP_PLL_HIGH_CLK_MASK, + IT66121_AFE_XP_PLL_HIGH_CLK_MASK); + if (ret) + return ret; + } else { ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, IT66121_AFE_XP_GAINBIT | @@ -346,6 +357,13 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, IT66121_AFE_XP_EC1_LOWCLK); if (ret) return ret; + + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_PLL_CTRL, + IT66121_AFE_XP_PLL_HIGH_CLK_MASK, + ~(IT66121_AFE_XP_PLL_HIGH_CLK_MASK) & 0xff); + if (ret) + return ret; } /* Clear reset flags */ From 2a962b60c92993e1a1c82e74a403b33641d1cfbd Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Wed, 19 Aug 2020 19:02:02 +0200 Subject: [PATCH] drm/bridge it66121 fire_afe if IT66121_SYS_STATUS_VID_STABLE There is an interrupt if video is stable - we should fire afe after that arrived. Signed-off-by: Alex Bee --- drivers/gpu/drm/bridge/ite-it66121.c | 101 ++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index bc6ca85e5ee1..81d1f53d50f5 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -95,12 +95,30 @@ #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_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_INT_STATUS2_REG 0x07 +#define IT66121_INT_STATUS2_PKT_3D BIT(7) +#define IT66121_INT_STATUS2_VID_UNSTABLE BIT(6) +#define IT66121_INT_STATUS2_PKT_ACP BIT(5) +#define IT66121_INT_STATUS2_PKT_NULL BIT(4) +#define IT66121_INT_STATUS2_PKT_GEN BIT(3) +#define IT66121_INT_STATUS2_CHK_KSV_LIST BIT(2) +#define IT66121_INT_STATUS2_AUTH_DONE BIT(1) +#define IT66121_INT_STATUS2_AUTH_FAIL BIT(0) + +#define IT66121_INT_STATUS3_REG 0x08 +#define IT66121_INT_STATUS3_AUD_CTS BIT(6) +#define IT66121_INT_STATUS3_VSYNC BIT(5) +#define IT66121_INT_STATUS3_VID_STABLE BIT(4) +#define IT66121_INT_STATUS2_PKT_MPEG BIT(3) +#define IT66121_INT_STATUS3_PKT_AUD BIT(1) +#define IT66121_INT_STATUS3_PKT_AVI BIT(0) #define IT66121_DDC_HEADER_REG 0x11 #define IT66121_DDC_HEADER_HDCP 0x74 @@ -132,16 +150,43 @@ #define IT66121_INT_MASK1_RX_SENS BIT(1) #define IT66121_INT_MASK1_HPD BIT(0) +#define IT66121_INT_MASK2_REG 0x0a +#define IT66121_INT_MASK2_PKT_AVI BIT(7) +#define IT66121_INT_MASK2_VID_UNSTABLE BIT(6) +#define IT66121_INT_MASK2_PKT_ACP BIT(5) +#define IT66121_INT_MASK2_PKT_NULL BIT(4) +#define IT66121_INT_MASK2_PKT_GEN BIT(3) +#define IT66121_INT_MASK2_CHK_KSV_LIST BIT(2) +#define IT66121_INT_MASK2_AUTH_DONE BIT(1) +#define IT66121_INT_MASK2_AUTH_FAIL BIT(0) + +#define IT66121_INT_MASK3_REG 0x0b +#define IT66121_INT_MASK3_PKT_3D BIT(6) +#define IT66121_INT_MASK3_AUD_CTS BIT(5) +#define IT66121_INT_MASK3_VSYNC BIT(4) +#define IT66121_INT_MASK3_VID_STABLE BIT(3) +#define IT66121_INT_MASK3_PKT_MPEG BIT(2) +#define IT66121_INT_MASK3_PKT_AUD 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_PKT_ACP BIT(7) +#define IT66121_INT_CLR1_PKT_NULL BIT(6) +#define IT66121_INT_CLR1_PKT_GEN BIT(5) +#define IT66121_INT_CLR1_CHK_KSV_LIST BIT(4) +#define IT66121_INT_CLR1_AUTH_DONE BIT(3) +#define IT66121_INT_CLR1_AUTH_FAIL BIT(2) #define IT66121_INT_CLR1_RX_SENS BIT(1) #define IT66121_INT_CLR1_HPD BIT(0) +#define IT66121_INT_CLR2_REG 0x0d +#define IT66121_INT_CLR2_VSYNC BIT(7) +#define IT66121_INT_CLR2_VID_STABLE BIT(6) +#define IT66121_INT_CLR2_PKT_MPEG BIT(5) +#define IT66121_INT_CLR2_PKT_AUD BIT(3) +#define IT66121_INT_CLR2_PKT_AVI BIT(2) +#define IT66121_INT_CLR2_PKT_3D BIT(1) +#define IT66121_INT_CLR2_VID_UNSTABLE BIT(0) + #define IT66121_AV_MUTE_REG 0xC1 #define IT66121_AV_MUTE_ON BIT(0) #define IT66121_AV_MUTE_BLUESCR BIT(1) @@ -371,10 +416,9 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, 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); + return ret; + } static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx) @@ -711,6 +755,12 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, return ret; /* Start interrupts */ + ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK3_REG, + IT66121_INT_MASK3_VID_STABLE, + ~(IT66121_INT_MASK3_VID_STABLE) & 0xff); + if (ret) + return ret; + return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, IT66121_INT_MASK1_DDC_NOACK | IT66121_INT_MASK1_HPD | @@ -847,18 +897,18 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) { int ret; - unsigned int val; + unsigned int val, sys_status; 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); + ret = regmap_read(ctx->regmap, IT66121_SYS_STATUS_REG, &sys_status); if (ret) goto unlock; - if (val & IT66121_SYS_STATUS_ACTIVE_IRQ) { + if (sys_status & 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); @@ -882,6 +932,21 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) } } + ret = regmap_read(ctx->regmap, IT66121_INT_STATUS3_REG, &val); + if (ret) { + dev_err(dev, "Cannot read STATUS3_REG %d\n", ret); + } else if (val) { + if (val & IT66121_INT_STATUS3_VID_STABLE) { + if (sys_status & IT66121_SYS_STATUS_VID_STABLE) + it66121_fire_afe(ctx); + + regmap_write_bits(ctx->regmap, + IT66121_INT_CLR2_REG, + IT66121_INT_CLR2_VID_STABLE, + IT66121_INT_CLR2_VID_STABLE); + } + } + regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, IT66121_SYS_STATUS_CLEAR_IRQ, IT66121_SYS_STATUS_CLEAR_IRQ); From 2ec0caed703ceb5aad46c162c1f3fde4789fd635 Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 18 Aug 2020 22:44:12 +0200 Subject: [PATCH] drm/bridge it66121: add I2S sound support Add sound support for it66121 - todos: - TODOs - refactor some huge functions - smarter error handling (whole driver) Signed-off-by: Alex Bee --- drivers/gpu/drm/bridge/Kconfig | 1 + drivers/gpu/drm/bridge/ite-it66121.c | 685 +++++++++++++++++++++++++++ 2 files changed, 686 insertions(+) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 6fe281070602..fd026f7cb4ba 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -64,6 +64,7 @@ config DRM_LONTIUM_LT9611 config DRM_ITE_IT66121 tristate "ITE IT66121 HDMI bridge" depends on OF + select SND_SOC_HDMI_CODEC if SND_SOC select DRM_KMS_HELPER select REGMAP_I2C help diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 81d1f53d50f5..98f3d68fe01f 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (C) 2020 Alex Bee * Copyright (C) 2020 BayLibre, SAS * Author: Phong LE * Copyright (C) 2018-2019, Artem Mygaiev @@ -26,6 +27,9 @@ #include #include +#include +#include + #define IT66121_MASTER_SEL_REG 0x10 #define IT66121_MASTER_SEL_HOST BIT(0) @@ -245,11 +249,149 @@ #define IT66121_EDID_FIFO_SIZE 32 #define IT66121_AFE_CLK_HIGH 80000 +#define IT66121_AUD_CLK_CTRL_REG 0x58 +#define IT66121_AUD_AUT_IP_CLK BIT(0) +#define IT66121_AUD_AUT_OVR_SMPL_CLK BIT(4) +#define IT66121_AUD_EXT_MCLK_128FS (0 << 2) +#define IT66121_AUD_EXT_MCLK_256FS BIT(2) +#define IT66121_AUD_EXT_MCLK_512FS (2 << 2) +#define IT66121_AUD_EXT_MCLK_1024FS (3 << 2) + +#define IT66121_AUD_INFO_REG_SZ 5 +#define IT66121_AUD_INFO_DB1_REG 0x168 +#define IT66121_AUD_INFO_DB2_REG 0x169 +#define IT66121_AUD_INFO_DB3_REG 0x16a +#define IT66121_AUD_INFO_DB4_REG 0x16b +#define IT66121_AUD_INFO_DB5_REG 0x16c +#define IT66121_AUD_INFO_CSUM_REG 0x16d + +#define IT66121_AUD_INFO_PKT_REG 0xce +#define IT66121_AUD_INFO_PKT_ON BIT(0) +#define IT66121_AUD_INFO_PKT_RPT BIT(1) + +#define IT66121_AUD_PKT_N1_REG 0x133 +#define IT66121_AUD_PKT_N2_REG 0x134 +#define IT66121_AUD_PKT_N3_REG 0x135 + +#define IT66121_AUD_PKT_CTS_MODE_REG 0xc5 +#define IT66121_AUD_PKT_CTS_MODE_USER BIT(1) +#define IT66121_AUD_PKT_CTS_MODE_AUTO_VAL 0 +#define IT66121_AUD_PKT_CTS1_REG 0x130 +#define IT66121_AUD_PKT_CTS2_REG 0x131 +#define IT66121_AUD_PKT_CTS3_REG 0x132 +#define IT66121_AUD_PKT_AUTO_CNT_CTS1_REG 0x135 +#define IT66121_AUD_PKT_AUTO_CNT_CTS2_REG 0x136 +#define IT66121_AUD_PKT_AUTO_CNT_CTS3_REG 0x137 + +#define IT66121_AUD_CHST_MODE_REG 0x191 +#define IT66121_AUD_CHST_MODE_NLPCM BIT(1) +#define IT66121_AUD_CHST_CAT_REG 0x192 +#define IT66121_AUD_CHST_SRCNUM_REG 0x193 +#define IT66121_AUD_CHST_CHTNUM_REG 0x194 +#define IT66121_AUD_CHST_CA_FS_REG 0x198 +#define IT66121_AUD_CHST_OFS_WL_REG 0x199 + +#define IT66121_AUD_CTRL0_REG 0xe0 +#define IT66121_AUD_SWL_16BIT (0 << 6) +#define IT66121_AUD_SWL_18BIT BIT(6) +#define IT66121_AUD_SWL_20BIT (2 << 6) +#define IT66121_AUD_SWL_24BIT (3 << 6) +#define IT66121_AUD_SWL_MASK IT66121_AUD_SWL_24BIT +#define IT66121_AUD_SPDIFTC BIT(5) +#define IT66121_AUD_SPDIF BIT(4) +#define IT66121_AUD_I2S (0 << 4) +#define IT66121_AUD_TYPE_MASK IT66121_AUD_SPDIF +#define IT66121_AUD_EN_I2S3 BIT(3) +#define IT66121_AUD_EN_I2S2 BIT(2) +#define IT66121_AUD_EN_I2S1 BIT(1) +#define IT66121_AUD_EN_I2S0 BIT(0) +#define IT66121_AUD_EN_I2S_MASK 0x0f +#define IT66121_AUD_EN_SPDIF 1 + +#define IT66121_AUD_CTRL1_REG 0xe1 +#define IT66121_AUD_FMT_STD_I2S (0 << 0) +#define IT66121_AUD_FMT_32BIT_I2S BIT(0) +#define IT66121_AUD_FMT_LEFT_JUSTIFY (0 << 1) +#define IT66121_AUD_FMT_RIGHT_JUSTIFY BIT(1) +#define IT66121_AUD_FMT_DELAY_1T_TO_WS (0 << 2) +#define IT66121_AUD_FMT_NO_DELAY_TO_WS BIT(2) +#define IT66121_AUD_FMT_WS0_LEFT (0 << 3) +#define IT66121_AUD_FMT_WS0_RIGHT BIT(3) +#define IT66121_AUD_FMT_MSB_SHIFT_FIRST (0 << 4) +#define IT66121_AUD_FMT_LSB_SHIFT_FIRST BIT(4) +#define IT66121_AUD_FMT_RISE_EDGE_SAMPLE_WS (0 << 5) +#define IT66121_AUD_FMT_FALL_EDGE_SAMPLE_WS BIT(5) +#define IT66121_AUD_FMT_FULLPKT BIT(6) + +#define IT66121_AUD_FIFOMAP_REG 0xe2 +#define IT66121_AUD_FIFO3_SEL 6 +#define IT66121_AUD_FIFO2_SEL 4 +#define IT66121_AUD_FIFO1_SEL 2 +#define IT66121_AUD_FIFO0_SEL 0 +#define IT66121_AUD_FIFO_SELSRC3 3 +#define IT66121_AUD_FIFO_SELSRC2 2 +#define IT66121_AUD_FIFO_SELSRC1 1 +#define IT66121_AUD_FIFO_SELSRC0 0 +#define IT66121_AUD_FIFOMAP_DEFAULT (IT66121_AUD_FIFO_SELSRC0 \ + << IT66121_AUD_FIFO0_SEL | \ + IT66121_AUD_FIFO_SELSRC1 \ + << IT66121_AUD_FIFO1_SEL | \ + IT66121_AUD_FIFO_SELSRC2 \ + << IT66121_AUD_FIFO2_SEL | \ + IT66121_AUD_FIFO_SELSRC3 \ + << IT66121_AUD_FIFO3_SEL) + +#define IT66121_AUD_CTRL3_REG 0xe3 +#define IT66121_AUD_MULCH BIT(7) +#define IT66121_AUD_ZERO_CTS BIT(6) +#define IT66121_AUD_CHSTSEL BIT(4) +#define IT66121_AUD_S3RLCHG BIT(3) +#define IT66121_AUD_S2RLCHG BIT(2) +#define IT66121_AUD_S1RLCHG BIT(1) +#define IT66121_AUD_S0RLCHG BIT(0) +#define IT66121_AUD_SRLCHG_MASK 0x0f +#define IT66121_AUD_SRLCHG_DEFAULT ((~IT66121_AUD_S0RLCHG & \ + ~IT66121_AUD_S1RLCHG & \ + ~IT66121_AUD_S2RLCHG & \ + ~IT66121_AUD_S3RLCHG) & \ + IT66121_AUD_SRLCHG_MASK) + +#define IT66121_AUD_SRC_VALID_FLAT_REG 0xe4 +#define IT66121_AUD_SPXFLAT_SRC3 BIT(7) +#define IT66121_AUD_SPXFLAT_SRC2 BIT(6) +#define IT66121_AUD_SPXFLAT_SRC1 BIT(5) +#define IT66121_AUD_SPXFLAT_SRC0 BIT(4) +#define IT66121_AUD_SPXFLAT_MASK 0xf0 +#define IT66121_AUD_SPXFLAT_SRC_ALL (IT66121_AUD_SPXFLAT_SRC0 | \ + IT66121_AUD_SPXFLAT_SRC1 | \ + IT66121_AUD_SPXFLAT_SRC2 | \ + IT66121_AUD_SPXFLAT_SRC3) +#define IT66121_AUD_ERR2FLAT BIT(3) +#define IT66121_AUD_S3VALID BIT(2) +#define IT66121_AUD_S2VALID BIT(1) +#define IT66121_AUD_S1VALID BIT(0) + +#define IT66121_AUD_HDAUDIO_REG 0xe5 +#define IT66121_AUD_HBR BIT(3) +#define IT66121_AUD_DSD BIT(1) + struct it66121_conf { unsigned int input_mode_reg; unsigned int input_conversion_reg; }; +struct it66121_audio { + struct platform_device *pdev; + unsigned int sources; + unsigned int n; + unsigned int cts; + unsigned int format; + unsigned int sample_wl; + bool is_hbr; + struct hdmi_audio_infoframe audio_infoframe; + struct snd_aes_iec958 aes_iec958; +}; + struct it66121_ctx { struct regmap *regmap; struct drm_bridge bridge; @@ -260,6 +402,8 @@ struct it66121_ctx { struct regulator_bulk_data supplies[3]; bool dual_edge; const struct it66121_conf *conf; + bool sink_has_audio; + struct it66121_audio audio; struct mutex lock; /* Protects fields below and device registers */ struct edid *edid; struct hdmi_avi_infoframe hdmi_avi_infoframe; @@ -610,6 +754,7 @@ static int it66121_connector_get_modes(struct drm_connector *connector) } num_modes = drm_add_edid_modes(connector, ctx->edid); + ctx->sink_has_audio = drm_detect_monitor_audio(ctx->edid); unlock: mutex_unlock(&ctx->lock); @@ -894,6 +1039,536 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { .mode_set = it66121_bridge_mode_set, }; +/* Audio related functions */ + +static int it66121_audio_reset_fifo(struct it66121_ctx *ctx) +{ + int ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD, + IT66121_SW_RST_AUD); + if (ret) + return ret; + + usleep_range(2000, 4000); + + return regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD, + ~(IT66121_SW_RST_AUD) & 0xff); +} + +static int it66121_audio_set_infoframe(struct it66121_ctx *ctx) +{ + int i, ret; + u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; + + const u16 audioinfo_reg[IT66121_AUD_INFO_REG_SZ] = { + IT66121_AUD_INFO_DB1_REG, + IT66121_AUD_INFO_DB2_REG, + IT66121_AUD_INFO_DB3_REG, + IT66121_AUD_INFO_DB4_REG, + IT66121_AUD_INFO_DB5_REG + }; + + ret = hdmi_audio_infoframe_pack(&ctx->audio.audio_infoframe, infoframe_buf, + sizeof(infoframe_buf)); + if (ret < 0) { + dev_err(ctx->dev, "%s: failed to pack audio infoframe: %d\n", + __func__, ret); + return ret; + } + + for (i = 0; i < IT66121_AUD_INFO_REG_SZ; i++) { + ret = regmap_write(ctx->regmap, audioinfo_reg[i], + infoframe_buf[i + HDMI_INFOFRAME_HEADER_SIZE]); + if (ret) + return ret; + } + + /* + * TODO: linux defines 10 bytes; currently only 5 are filled + * if that ever changes checksum will differ since + * it66121 takes max 5 bytes -> calc checksum here???? + */ + ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_CSUM_REG, infoframe_buf[3]); + + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG, + IT66121_AUD_INFO_PKT_ON | IT66121_AUD_INFO_PKT_RPT); +} + +static int it66121_audio_set_cts_n(struct it66121_ctx *ctx) +{ + int ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N1_REG, + ctx->audio.n & 0xff); + if (ret) + return ret; + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N2_REG, + (ctx->audio.n >> 8) & 0xff); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N3_REG, + (ctx->audio.n >> 16) & 0xf); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS1_REG, + ctx->audio.cts & 0xff); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS2_REG, + (ctx->audio.cts >> 8) & 0xff); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS3_REG, + (ctx->audio.cts >> 16) & 0xf); + if (ret) + return ret; + + /* + * magic values ("password") have to be written to + * f8 register to enable writing to IT66121_AUD_PKT_CTS_MODE_REG + */ + + ret = regmap_write(ctx->regmap, 0xf8, 0xc3); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, 0xf8, 0xa5); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, + IT66121_AUD_PKT_CTS_MODE_REG, + IT66121_AUD_PKT_CTS_MODE_USER, + ctx->audio.cts == IT66121_AUD_PKT_CTS_MODE_AUTO_VAL + ? ~IT66121_AUD_PKT_CTS_MODE_USER & 0xff + : IT66121_AUD_PKT_CTS_MODE_USER); + if (ret) + return ret; + + /* + * disabling write to IT66121_AUD_PKT_CTS_MODE_REG + * again by overwriting perviously set "password" + */ + return regmap_write(ctx->regmap, 0xf8, 0xff); +} + +static int it66121_audio_set_channel_status(struct it66121_ctx *ctx) +{ + int ret; + unsigned int val; + + /* TODO: check: always use NLPCM - would to cover LPCM also?*/ + val = IT66121_AUD_CHST_MODE_NLPCM; + val |= ctx->audio.aes_iec958.status[0] & IEC958_AES0_CON_NOT_COPYRIGHT + ? BIT(3) + : (0 << 3); + val |= (ctx->audio.aes_iec958.status[0] & IEC958_AES0_CON_EMPHASIS) << 4; + ret = regmap_write(ctx->regmap, + IT66121_AUD_CHST_MODE_REG, + val); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, + IT66121_AUD_CHST_CAT_REG, + ctx->audio.aes_iec958.status[1]); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, + IT66121_AUD_CHST_SRCNUM_REG, + ctx->audio.aes_iec958.status[2] & + IEC958_AES2_CON_SOURCE); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, + IT66121_AUD_CHST_CHTNUM_REG, + ((ctx->audio.aes_iec958.status[2] & + IEC958_AES2_CON_CHANNEL) >> 4)); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, + IT66121_AUD_CHST_CA_FS_REG, + (ctx->audio.aes_iec958.status[3] & + IEC958_AES3_CON_CLOCK) << 2 | + (ctx->audio.aes_iec958.status[3] & + IEC958_AES3_CON_FS)); + if (ret) + return ret; + + return regmap_write(ctx->regmap, + IT66121_AUD_CHST_OFS_WL_REG, + ctx->audio.aes_iec958.status[4]); +} + +static int it66121_audio_mute(struct it66121_ctx *ctx, bool enable) +{ + return regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_EN_I2S_MASK, + enable ? 0x0 : ctx->audio.sources); +} + +static int it66121_audio_set_controls(struct it66121_ctx *ctx) +{ + int ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_TYPE_MASK | IT66121_AUD_SWL_MASK, + IT66121_AUD_I2S | ctx->audio.sample_wl); + + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL1_REG, + ctx->audio.format); + if (ret) + return ret; + + /* default fifo mapping: fifo0 => source0, fifo1 => source1, ... */ + ret = regmap_write(ctx->regmap, IT66121_AUD_FIFOMAP_REG, + IT66121_AUD_FIFOMAP_DEFAULT); + if (ret) + return ret; + + /* Do not swap R/L for any source */ + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL3_REG, + IT66121_AUD_SRLCHG_MASK, + IT66121_AUD_SRLCHG_DEFAULT); + if (ret) + return ret; + + /* "unflat" all sources */ + ret = regmap_write_bits(ctx->regmap, + IT66121_AUD_SRC_VALID_FLAT_REG, + IT66121_AUD_SPXFLAT_MASK, + ~IT66121_AUD_SPXFLAT_SRC_ALL & 0xff); + if (ret) + return ret; + + /* TODO: check if we really support HBR audio yet */ + return regmap_write_bits(ctx->regmap, IT66121_AUD_HDAUDIO_REG, + IT66121_AUD_HBR, + ctx->audio.is_hbr + ? IT66121_AUD_HBR + : ~IT66121_AUD_HBR & 0xff); +} + +static int it66121_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + int ret; + unsigned int sources = 0; + + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + if (!ctx->sink_has_audio) { + dev_err(ctx->dev, "%s: sink has no audio", __func__); + return -EINVAL; + } + + /* for now i2s only */ + if (daifmt->bit_clk_master | daifmt->frame_clk_master) { + dev_err(ctx->dev, + "%s: only clk_master and frame_clk_master formats are supported\n", + __func__); + return -EINVAL; + } + + /* TODO: move all these switches in functions ? */ + switch (daifmt->fmt) { + case HDMI_I2S: + ctx->audio.format = IT66121_AUD_FMT_32BIT_I2S; + break; + case HDMI_RIGHT_J: + ctx->audio.format = IT66121_AUD_FMT_RIGHT_JUSTIFY; + break; + case HDMI_LEFT_J: + ctx->audio.format = IT66121_AUD_FMT_LEFT_JUSTIFY; + break; + default: + dev_err(ctx->dev, "%s: unsupported daiformat: %u\n", + __func__, daifmt->fmt); + return -EINVAL; + } + + switch (params->channels) { + case 7 ... 8: + ctx->audio.sources |= IT66121_AUD_EN_I2S3; + sources++; + fallthrough; + case 5 ... 6: + ctx->audio.sources |= IT66121_AUD_EN_I2S2; + sources++; + fallthrough; + case 3 ... 4: + ctx->audio.sources |= IT66121_AUD_EN_I2S1; + sources++; + fallthrough; + case 1 ... 2: + ctx->audio.sources |= IT66121_AUD_EN_I2S0; + sources++; + break; + default: + dev_err(ctx->dev, "%s: unsupported channel count: %d\n", + __func__, params->channels); + return -EINVAL; + } + + switch (params->sample_width) { + case 16: + ctx->audio.sample_wl = IT66121_AUD_SWL_16BIT; + break; + case 18: + ctx->audio.sample_wl = IT66121_AUD_SWL_18BIT; + break; + case 20: + ctx->audio.sample_wl = IT66121_AUD_SWL_20BIT; + break; + case 24: + case 32: + /* assume 24 bit wordlength for 32 bit width */ + ctx->audio.sample_wl = IT66121_AUD_SWL_24BIT; + break; + default: + dev_err(ctx->dev, "%s: unsupported sample width: %d\n", + __func__, params->channels); + return -EINVAL; + } + + switch (params->sample_rate) { + case 32000: + case 48000: + case 96000: + case 192000: + ctx->audio.n = 128 * params->sample_rate / 1000; + ctx->audio.is_hbr = false; + break; + case 44100: + case 88200: + case 176400: + ctx->audio.n = 128 * params->sample_rate / 900; + ctx->audio.is_hbr = false; + break; + case 768000: + ctx->audio.n = 24576; + ctx->audio.is_hbr = true; + break; + default: + dev_err(ctx->dev, "%s: unsupported sample_rate: %d\n", + __func__, params->sample_rate); + return -EINVAL; + } + + /* not all bits are correctly filled in snd_aes_iec958 - fill them here */ + if ((params->iec.status[2] & IEC958_AES2_CON_SOURCE) == IEC958_AES2_CON_SOURCE_UNSPEC) + params->iec.status[2] |= sources; + + if ((params->iec.status[2] & IEC958_AES2_CON_CHANNEL) == IEC958_AES2_CON_CHANNEL_UNSPEC) + params->iec.status[2] |= params->channels << 4; + + /* OFs is 1-complement of Fs */ + if ((params->iec.status[4] & IEC958_AES4_CON_ORIGFS) == IEC958_AES4_CON_ORIGFS_NOTID && + (params->iec.status[3] & IEC958_AES3_CON_FS) != IEC958_AES3_CON_FS_NOTID) + params->iec.status[4] |= (~(params->iec.status[3] & IEC958_AES3_CON_FS) & 0xf) << 4; + + ctx->audio.format |= IT66121_AUD_FMT_FULLPKT; + ctx->audio.cts = IT66121_AUD_PKT_CTS_MODE_AUTO_VAL; + ctx->audio.audio_infoframe = params->cea; + ctx->audio.aes_iec958 = params->iec; + + ret = it66121_audio_reset_fifo(ctx); + if (ret) + return ret; + + ret = it66121_audio_set_infoframe(ctx); + if (ret) { + dev_err(ctx->dev, + "%s: failed to assemble/enable audio infoframe: %d\n", + __func__, ret); + /* TODO: Really fail here? */ + return ret; + } + + ret = it66121_audio_set_cts_n(ctx); + if (ret) { + dev_err(ctx->dev, + "%s: failed to write cts/n values: %d\n", + __func__, ret); + return ret; + } + + ret = it66121_audio_set_channel_status(ctx); + if (ret) { + dev_err(ctx->dev, + "%s: failed to write channel_status %d\n", + __func__, ret); + return ret; + } + + ret = it66121_audio_set_controls(ctx); + if (ret) { + dev_err(ctx->dev, + "%s: failed to write audio controls %d\n", + __func__, ret); + return ret; + } + + return ret; +} + +static void it66121_audio_shutdown(struct device *dev, void *data) +{ + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF); + + regmap_write(ctx->regmap, IT66121_AUD_CLK_CTRL_REG, 0x0); + + regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, + IT66121_INT_MASK1_AUD_OVF, + IT66121_INT_MASK1_AUD_OVF); + + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_ACLK, + IT66121_CLK_BANK_PWROFF_ACLK); + + regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + ~(IT66121_SW_RST_AUD | IT66121_SW_RST_AREF) & 0xff); +} + +static int it66121_audio_startup(struct device *dev, void *data) +{ + int ret; + + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF); + if (ret) + return ret; + /* TODO: check how to determine Fs at runtime -> only for spdif???*/ + ret = regmap_write(ctx->regmap, IT66121_AUD_CLK_CTRL_REG, + IT66121_AUD_AUT_OVR_SMPL_CLK | + IT66121_AUD_EXT_MCLK_256FS); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_ACLK, + ~IT66121_CLK_BANK_PWROFF_ACLK & 0xff); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, + IT66121_INT_MASK1_AUD_OVF, + ~IT66121_INT_MASK1_AUD_OVF & 0xff); + if (ret) + return ret; + + return regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + ~(IT66121_SW_RST_AUD | IT66121_SW_RST_AREF) & 0xff); +} + +int it66121_audio_mute_stream(struct device *dev, void *data, bool enable, + int direction) +{ + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + return it66121_audio_mute(ctx, enable); +} + +static int it66121_audio_get_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct of_endpoint of_ep; + int ret; + + ret = of_graph_parse_endpoint(endpoint, &of_ep); + if (ret < 0) + return ret; + + /* + * HDMI sound should be located as reg = <2> + * Then, it is sound port 0 + */ + if (of_ep.port == 2) + return 0; + + return -EINVAL; +} + +static int it66121_audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + memcpy(buf, ctx->connector.eld, + min(sizeof(ctx->connector.eld), len)); + + return 0; +} + +static const struct hdmi_codec_ops it66121_audio_codec_ops = { + .audio_shutdown = it66121_audio_shutdown, + .audio_startup = it66121_audio_startup, + .mute_stream = it66121_audio_mute_stream, + .no_capture_mute = 1, + .hw_params = it66121_audio_hw_params, + .get_eld = it66121_audio_get_eld, + .get_dai_id = it66121_audio_get_dai_id, +}; + +static const struct hdmi_codec_pdata codec_data = { + .ops = &it66121_audio_codec_ops, + .i2s = 1, /* Only i2s support for now. */ + .spdif = 0, + .max_i2s_channels = 8, +}; + +static int it66121_audio_codec_init(struct it66121_ctx *it66121, + struct device *dev) +{ + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + ctx->audio.pdev = platform_device_register_data( + dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, + &codec_data, sizeof(codec_data)); + + if (IS_ERR(ctx->audio.pdev)) + return PTR_ERR(ctx->audio.pdev); + + DRM_INFO("%s has been bound to to HDMITX it66121\n", + HDMI_CODEC_DRV_NAME); + + return 0; +} + +static void it66121_audio_codec_exit(struct it66121_ctx *ctx) +{ + if (ctx->audio.pdev) { + platform_device_unregister(ctx->audio.pdev); + ctx->audio.pdev = NULL; + } +} + static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) { int ret; @@ -930,6 +1605,8 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) } event = true; } + if (val & IT66121_INT_STATUS1_AUD_OVF) + it66121_audio_reset_fifo(ctx); } ret = regmap_read(ctx->regmap, IT66121_INT_STATUS3_REG, &val); @@ -1042,6 +1719,12 @@ static int it66121_probe(struct i2c_client *client, drm_bridge_add(&ctx->bridge); + ret = it66121_audio_codec_init(ctx, dev); + if (ret) { + dev_err(dev, "Failed to initialize audio codec %d\n", ret); + return ret; + } + return 0; } @@ -1049,6 +1732,7 @@ static int it66121_remove(struct i2c_client *client) { struct it66121_ctx *ctx = i2c_get_clientdata(client); + it66121_audio_codec_exit(ctx); ite66121_power_off(ctx); drm_bridge_remove(&ctx->bridge); kfree(ctx->edid); @@ -1086,6 +1770,7 @@ static struct i2c_driver it66121_driver = { module_i2c_driver(it66121_driver); +MODULE_AUTHOR("Alex Bee"); MODULE_AUTHOR("Phong LE"); MODULE_DESCRIPTION("IT66121 HDMI transmitter driver"); MODULE_LICENSE("GPL v2"); From a6545c717b35bb1e53f8df75093e7582d1b75635 Mon Sep 17 00:00:00 2001 From: Alex Bee Date: Tue, 18 Aug 2020 22:46:01 +0200 Subject: [PATCH] drm/bridge: it66121: increase reset time Signed-off-by: Alex Bee --- drivers/gpu/drm/bridge/ite-it66121.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 98f3d68fe01f..a01d2e46bb0c 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -443,7 +443,7 @@ static const struct it66121_conf it66121fn_conf_simple = { static void it66121_hw_reset(struct it66121_ctx *ctx) { gpiod_set_value(ctx->gpio_reset, 1); - msleep(20); + msleep(50); gpiod_set_value(ctx->gpio_reset, 0); }