drm/rockchip: dw_hdmi: Support HDMI 2.0 YCbCr 4:2:0

Some old code has too many conflicts with the upstream code,
so recombine and commit these changes.

Including these changes:
1.Support yuv420.
2.Limit rk3229/rk3328 max output resolution.
3.Support dynamically get input/out color info.
4.Introduce mpll_cfg_420.
5.use drm_mode_is_420 instead of DRM_MODE_FLAG_420_MASK.

Change-Id: I42462284b16f26b7adef0e9455903ee5fc71e432
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
This commit is contained in:
Algea Cao
2019-06-28 10:50:04 +08:00
committed by Tao Huang
parent 5e5845517a
commit 6bd6c35e9d
3 changed files with 124 additions and 16 deletions

View File

@@ -760,6 +760,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) {
@@ -989,7 +1003,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:
@@ -1278,6 +1293,10 @@ 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;
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++)
if (mpixelclock <= mpll_config->mpixelclock)
@@ -1463,6 +1482,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;
@@ -1619,10 +1640,11 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
u8 inv_val;
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 hdisplay, vdisplay;
vmode->mpixelclock = mode->clock * 1000;
if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
vmode->mpixelclock /= 2;
dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
/* Set up HDMI_FC_INVIDCONF */
@@ -1659,6 +1681,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;
@@ -1676,8 +1714,8 @@ 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);
@@ -1801,6 +1839,7 @@ static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
{
int ret;
void *data = hdmi->plat_data->phy_data;
hdmi_disable_overflow_interrupts(hdmi);
@@ -1812,10 +1851,13 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
}
if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
(hdmi->vic == 21) || (hdmi->vic == 22) ||
(hdmi->vic == 2) || (hdmi->vic == 3) ||
(hdmi->vic == 17) || (hdmi->vic == 18))
if (hdmi->plat_data->get_enc_out_encoding)
hdmi->hdmi_data.enc_out_encoding =
hdmi->plat_data->get_enc_out_encoding(data);
else if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
(hdmi->vic == 21) || (hdmi->vic == 22) ||
(hdmi->vic == 2) || (hdmi->vic == 3) ||
(hdmi->vic == 17) || (hdmi->vic == 18))
hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
else
hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
@@ -1824,22 +1866,34 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
/* TOFIX: Get input format from plat data or fallback to RGB888 */
if (hdmi->plat_data->input_bus_format)
if (hdmi->plat_data->get_input_bus_format)
hdmi->hdmi_data.enc_in_bus_format =
hdmi->plat_data->get_input_bus_format(data);
else if (hdmi->plat_data->input_bus_format)
hdmi->hdmi_data.enc_in_bus_format =
hdmi->plat_data->input_bus_format;
else
hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
hdmi->hdmi_data.enc_in_bus_format =
MEDIA_BUS_FMT_RGB888_1X24;
/* TOFIX: Default to RGB888 output format */
if (hdmi->plat_data->get_output_bus_format)
hdmi->hdmi_data.enc_out_bus_format =
hdmi->plat_data->get_output_bus_format(data);
else
hdmi->hdmi_data.enc_out_bus_format =
MEDIA_BUS_FMT_RGB888_1X24;
/* TOFIX: Get input encoding from plat data or fallback to none */
if (hdmi->plat_data->input_bus_encoding)
if (hdmi->plat_data->get_enc_in_encoding)
hdmi->hdmi_data.enc_in_encoding =
hdmi->plat_data->get_enc_in_encoding(data);
else if (hdmi->plat_data->input_bus_encoding)
hdmi->hdmi_data.enc_in_encoding =
hdmi->plat_data->input_bus_encoding;
else
hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
/* TOFIX: Default to RGB888 output format */
hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
hdmi->hdmi_data.pix_repet_factor = 0;
hdmi->hdmi_data.hdcp_enable = 0;
hdmi->hdmi_data.video_mode.mdataenablepolarity = true;

View File

@@ -187,6 +187,46 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
}
};
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
{
30666000, {
{ 0x00b7, 0x0000 },
{ 0x2157, 0x0000 },
{ 0x40f7, 0x0000 },
},
}, {
92000000, {
{ 0x00b7, 0x0000 },
{ 0x2143, 0x0001 },
{ 0x40a3, 0x0001 },
},
}, {
184000000, {
{ 0x0073, 0x0001 },
{ 0x2146, 0x0002 },
{ 0x4062, 0x0002 },
},
}, {
340000000, {
{ 0x0052, 0x0003 },
{ 0x214d, 0x0003 },
{ 0x4065, 0x0003 },
},
}, {
600000000, {
{ 0x0041, 0x0003 },
{ 0x3b4d, 0x0003 },
{ 0x5a65, 0x0003 },
},
}, {
~0UL, {
{ 0x0000, 0x0000 },
{ 0x0000, 0x0000 },
{ 0x0000, 0x0000 },
},
}
};
static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
/* pixelclk bpp8 bpp10 bpp12 */
{
@@ -354,14 +394,21 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
struct drm_display_info *info = &conn_state->connector->display_info;
if (drm_mode_is_420(info, &crtc_state->mode)) {
if (conn_state->connector->ycbcr_420_allowed &&
crtc_state->mode.crtc_clock > 340000 &&
drm_mode_is_420(info, &crtc_state->mode)) {
s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
s->bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
if (hdmi->phy)
phy_set_bus_width(hdmi->phy, 4);
} else {
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
if (hdmi->phy)
phy_set_bus_width(hdmi->phy, 8);
}
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
@@ -481,6 +528,7 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
.mode_valid = dw_hdmi_rockchip_mode_valid,
.mpll_cfg = rockchip_mpll_cfg,
.mpll_cfg_420 = rockchip_mpll_cfg_420,
.cur_ctr = rockchip_cur_ctr,
.phy_config = rockchip_phy_config,
.phy_data = &rk3399_chip_data,

View File

@@ -145,11 +145,17 @@ 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,
const struct dw_hdmi_plat_data *pdata,
unsigned long mpixelclock);
unsigned long (*get_input_bus_format)(void *data);
unsigned long (*get_output_bus_format)(void *data);
unsigned long (*get_enc_in_encoding)(void *data);
unsigned long (*get_enc_out_encoding)(void *data);
};
struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,