mirror of
https://github.com/torvalds/linux.git
synced 2024-11-19 10:31:48 +00:00
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. YUV420 Synopsys PHY support will need some specific configuration table to support theses modes. 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> Tested-by: Heiko Stuebner <heiko@sntech.de> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> Link: https://patchwork.freedesktop.org/patch/msgid/1549022873-40549-5-git-send-email-narmstrong@baylibre.com
This commit is contained in:
parent
74f6d1e1cb
commit
ba9877e236
@ -99,6 +99,7 @@ struct hdmi_vmode {
|
||||
unsigned int mpixelclock;
|
||||
unsigned int mpixelrepetitioninput;
|
||||
unsigned int mpixelrepetitionoutput;
|
||||
unsigned int mtmdsclock;
|
||||
};
|
||||
|
||||
struct hdmi_data_info {
|
||||
@ -543,7 +544,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);
|
||||
}
|
||||
@ -552,7 +553,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);
|
||||
}
|
||||
@ -653,6 +654,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) {
|
||||
@ -882,7 +897,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:
|
||||
@ -1036,7 +1052,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) {
|
||||
@ -1198,6 +1214,8 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
|
||||
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
|
||||
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
|
||||
|
||||
/* TOFIX Will need 420 specific PHY configuration tables */
|
||||
|
||||
/* PLL/MPLL Cfg - always match on final entry */
|
||||
for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
|
||||
if (mpixelclock <= mpll_config->mpixelclock)
|
||||
@ -1245,6 +1263,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
|
||||
const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
|
||||
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
|
||||
unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
|
||||
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
|
||||
int ret;
|
||||
|
||||
dw_hdmi_phy_power_off(hdmi);
|
||||
@ -1273,7 +1292,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
|
||||
}
|
||||
|
||||
/* Wait for resuming transmission of TMDS clock and data */
|
||||
if (mpixelclock > HDMI14_MAX_TMDSCLK)
|
||||
if (mtmdsclock > HDMI14_MAX_TMDSCLK)
|
||||
msleep(100);
|
||||
|
||||
return dw_hdmi_phy_power_on(hdmi);
|
||||
@ -1390,6 +1409,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;
|
||||
|
||||
@ -1547,15 +1568,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 > HDMI14_MAX_TMDSCLK ||
|
||||
vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
|
||||
hdmi_info->scdc.scrambling.low_rates ?
|
||||
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
|
||||
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
|
||||
@ -1589,6 +1613,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;
|
||||
@ -1607,7 +1647,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
|
||||
|
||||
/* Scrambling Control */
|
||||
if (hdmi_info->scdc.supported) {
|
||||
if (vmode->mpixelclock > HDMI14_MAX_TMDSCLK ||
|
||||
if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
|
||||
hdmi_info->scdc.scrambling.low_rates) {
|
||||
/*
|
||||
* HDMI2.0 Specifies the following procedure:
|
||||
@ -1645,15 +1685,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);
|
||||
|
||||
@ -1661,7 +1700,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);
|
||||
|
||||
@ -1669,7 +1707,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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user