mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-27 17:21:34 +00:00
198 lines
7.2 KiB
Diff
198 lines
7.2 KiB
Diff
From 3e7f3ec3de8753faefdeb02ed6d00cc580e6ad52 Mon Sep 17 00:00:00 2001
|
|
From: Neil Armstrong <narmstrong@baylibre.com>
|
|
Date: Wed, 14 Nov 2018 17:19:36 +0100
|
|
Subject: [PATCH] drm/bridge: dw-hdmi: add support for YUV420 output
|
|
|
|
In order to support the HDMI2.0 YUV420 display modes, this patch
|
|
adds support for the YUV420 TMDS Clock divided by 2 and the controller
|
|
passthrough mode.
|
|
|
|
This patch is based on work from Zheng Yang <zhengyang@rock-chips.com> in
|
|
the Rockchip Linux 4.4 BSP at [1]
|
|
|
|
[1] https://github.com/rockchip-linux/kernel/tree/release-4.4
|
|
|
|
Cc: Zheng Yang <zhengyang@rock-chips.com>
|
|
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
|
|
---
|
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 63 ++++++++++++++++++++++++-------
|
|
1 file changed, 50 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
index 2a30d83..c3e4ed1 100644
|
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
|
@@ -94,6 +94,7 @@ struct hdmi_vmode {
|
|
unsigned int mpixelclock;
|
|
unsigned int mpixelrepetitioninput;
|
|
unsigned int mpixelrepetitionoutput;
|
|
+ unsigned int mtmdsclock;
|
|
};
|
|
|
|
struct hdmi_data_info {
|
|
@@ -549,7 +550,7 @@ static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
|
|
static void hdmi_clk_regenerator_update_pixel_clock(struct dw_hdmi *hdmi)
|
|
{
|
|
mutex_lock(&hdmi->audio_mutex);
|
|
- hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
|
|
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
|
|
hdmi->sample_rate);
|
|
mutex_unlock(&hdmi->audio_mutex);
|
|
}
|
|
@@ -558,7 +559,7 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
|
|
{
|
|
mutex_lock(&hdmi->audio_mutex);
|
|
hdmi->sample_rate = rate;
|
|
- hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock,
|
|
+ hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mtmdsclock,
|
|
hdmi->sample_rate);
|
|
mutex_unlock(&hdmi->audio_mutex);
|
|
}
|
|
@@ -659,6 +660,20 @@ static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
|
|
}
|
|
}
|
|
|
|
+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
|
|
+{
|
|
+ switch (bus_format) {
|
|
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
|
|
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
|
|
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
|
|
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
|
|
+ return true;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
|
|
{
|
|
switch (bus_format) {
|
|
@@ -888,7 +903,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
|
|
u8 val, vp_conf;
|
|
|
|
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
|
|
- hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) {
|
|
+ hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) ||
|
|
+ hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
|
|
switch (hdmi_bus_fmt_color_depth(
|
|
hdmi->hdmi_data.enc_out_bus_format)) {
|
|
case 8:
|
|
@@ -1029,7 +1045,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
|
|
|
|
void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi)
|
|
{
|
|
- unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mpixelclock;
|
|
+ unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
|
|
|
|
/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
|
|
if (hdmi->connector.display_info.hdmi.scdc.supported) {
|
|
@@ -1370,6 +1386,9 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
|
struct hdmi_avi_infoframe frame;
|
|
u8 val;
|
|
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ is_hdmi2_sink = true;
|
|
+
|
|
/* Initialise info frame from DRM mode */
|
|
drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, is_hdmi2_sink);
|
|
|
|
@@ -1377,6 +1396,8 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
|
|
frame.colorspace = HDMI_COLORSPACE_YUV444;
|
|
else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
|
|
frame.colorspace = HDMI_COLORSPACE_YUV422;
|
|
+ else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ frame.colorspace = HDMI_COLORSPACE_YUV420;
|
|
else
|
|
frame.colorspace = HDMI_COLORSPACE_RGB;
|
|
|
|
@@ -1534,15 +1555,18 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
|
|
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
|
|
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
|
|
- unsigned int vdisplay;
|
|
+ unsigned int vdisplay, hdisplay;
|
|
|
|
- vmode->mpixelclock = mode->clock * 1000;
|
|
+ vmode->mtmdsclock = vmode->mpixelclock = mode->clock * 1000;
|
|
|
|
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
|
|
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
|
|
+ vmode->mtmdsclock /= 2;
|
|
+
|
|
/* Set up HDMI_FC_INVIDCONF */
|
|
inv_val = (hdmi->hdmi_data.hdcp_enable ||
|
|
- vmode->mpixelclock > 340000000 ||
|
|
+ vmode->mtmdsclock > 340000000 ||
|
|
hdmi_info->scdc.scrambling.low_rates ?
|
|
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
|
|
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
|
|
@@ -1576,6 +1600,22 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
|
|
hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
|
|
|
|
+ hdisplay = mode->hdisplay;
|
|
+ hblank = mode->htotal - mode->hdisplay;
|
|
+ h_de_hs = mode->hsync_start - mode->hdisplay;
|
|
+ hsync_len = mode->hsync_end - mode->hsync_start;
|
|
+
|
|
+ /*
|
|
+ * When we're setting a YCbCr420 mode, we need
|
|
+ * to adjust the horizontal timing to suit.
|
|
+ */
|
|
+ if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) {
|
|
+ hdisplay /= 2;
|
|
+ hblank /= 2;
|
|
+ h_de_hs /= 2;
|
|
+ hsync_len /= 2;
|
|
+ }
|
|
+
|
|
vdisplay = mode->vdisplay;
|
|
vblank = mode->vtotal - mode->vdisplay;
|
|
v_de_vs = mode->vsync_start - mode->vdisplay;
|
|
@@ -1594,7 +1634,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
|
|
/* Scrambling Control */
|
|
if (hdmi_info->scdc.supported) {
|
|
- if (vmode->mpixelclock > 340000000 ||
|
|
+ if (vmode->mtmdsclock > 340000000 ||
|
|
hdmi_info->scdc.scrambling.low_rates) {
|
|
drm_scdc_readb(&hdmi->i2c->adap, SCDC_SINK_VERSION,
|
|
&bytes);
|
|
@@ -1613,15 +1653,14 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
}
|
|
|
|
/* Set up horizontal active pixel width */
|
|
- hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
|
|
- hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
|
|
+ hdmi_writeb(hdmi, hdisplay >> 8, HDMI_FC_INHACTV1);
|
|
+ hdmi_writeb(hdmi, hdisplay, HDMI_FC_INHACTV0);
|
|
|
|
/* Set up vertical active lines */
|
|
hdmi_writeb(hdmi, vdisplay >> 8, HDMI_FC_INVACTV1);
|
|
hdmi_writeb(hdmi, vdisplay, HDMI_FC_INVACTV0);
|
|
|
|
/* Set up horizontal blanking pixel region width */
|
|
- hblank = mode->htotal - mode->hdisplay;
|
|
hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
|
|
hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
|
|
|
|
@@ -1629,7 +1668,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
|
|
|
|
/* Set up HSYNC active edge delay width (in pixel clks) */
|
|
- h_de_hs = mode->hsync_start - mode->hdisplay;
|
|
hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
|
|
hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
|
|
|
|
@@ -1637,7 +1675,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
|
hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
|
|
|
|
/* Set up HSYNC active pulse width (in pixel clks) */
|
|
- hsync_len = mode->hsync_end - mode->hsync_start;
|
|
hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
|
|
hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
|
|
|