mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
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:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user