mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
drm/rockchip: hdmi: support modify color depth
This patch introduce a drm property hdmi_output_depth to get/set HDMI color depth, the possible value could be - Automatic This indicates prefer highest color depth, it is 30bit on rockcip platform. - 24bit - 30bit The default value of property is 24bit. The max_tmds_clock is 0 on some display device, we think it's max_tmds_clock is 340MHz. If tmdsclock > max_tmds_clock, real output color depth fallback to 24bit. Change-Id: I666ac85d1ce5e73af31251eae324d1a6ae00b31e Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
This commit is contained in:
@@ -90,6 +90,9 @@ struct rockchip_hdmi {
|
||||
bool unsupported_yuv_input;
|
||||
bool unsupported_deep_color;
|
||||
unsigned long bus_format;
|
||||
|
||||
struct drm_property *color_depth_property;
|
||||
unsigned int colordepth;
|
||||
};
|
||||
|
||||
#define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x)
|
||||
@@ -443,6 +446,39 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
||||
ret ? "LIT" : "BIG");
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
dw_hdmi_rockchip_check_depth(struct drm_display_info *info,
|
||||
unsigned long pixelclock, int depth)
|
||||
{
|
||||
unsigned long tmdsclock;
|
||||
unsigned int colordepth;
|
||||
|
||||
if (!depth)
|
||||
colordepth = info->bpc;
|
||||
else
|
||||
colordepth = depth;
|
||||
|
||||
/* Color depth on rockchip platform is limited up to 10bit */
|
||||
if (colordepth > 10)
|
||||
colordepth = 10;
|
||||
|
||||
if (colordepth == 10)
|
||||
tmdsclock = pixelclock * 5 / 4;
|
||||
else
|
||||
tmdsclock = pixelclock;
|
||||
|
||||
/*
|
||||
* For some display device, max_tmds_clock is 0, we think
|
||||
* max_tmds_clock is 340MHz. If tmdsclock > max_tmds_clock,
|
||||
* fallback to 8bit.
|
||||
*/
|
||||
if ((!info->max_tmds_clock && tmdsclock > 340000) ||
|
||||
(info->max_tmds_clock && tmdsclock > info->max_tmds_clock))
|
||||
colordepth = 8;
|
||||
|
||||
return colordepth;
|
||||
}
|
||||
|
||||
static int
|
||||
dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
@@ -451,19 +487,30 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
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;
|
||||
unsigned long pixclock = crtc_state->adjusted_mode.crtc_clock;
|
||||
unsigned int colordepth;
|
||||
|
||||
colordepth = dw_hdmi_rockchip_check_depth(info, pixclock,
|
||||
hdmi->colordepth);
|
||||
|
||||
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 (colordepth > 8)
|
||||
s->bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
|
||||
else
|
||||
s->bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
|
||||
if (hdmi->phy)
|
||||
phy_set_bus_width(hdmi->phy, 4);
|
||||
phy_set_bus_width(hdmi->phy, colordepth / 2);
|
||||
} else {
|
||||
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
|
||||
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
if (colordepth > 8)
|
||||
s->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
|
||||
else
|
||||
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
if (hdmi->phy)
|
||||
phy_set_bus_width(hdmi->phy, 8);
|
||||
phy_set_bus_width(hdmi->phy, colordepth);
|
||||
}
|
||||
s->output_type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
s->output_if = VOP_OUTPUT_IF_HDMI0;
|
||||
@@ -490,6 +537,89 @@ dw_hdmi_rockchip_get_output_bus_format(void *data)
|
||||
return hdmi->bus_format;
|
||||
}
|
||||
|
||||
static const struct drm_prop_enum_list color_depth_enum_list[] = {
|
||||
{ 0, "Automatic" }, /* Prefer highest color depth */
|
||||
{ 8, "24bit" },
|
||||
{ 10, "30bit" },
|
||||
};
|
||||
|
||||
static void
|
||||
dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
|
||||
int version, void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
struct drm_property *prop;
|
||||
|
||||
/* RK3368 does not support deep color mode */
|
||||
if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) {
|
||||
prop = drm_property_create_enum(connector->dev, 0,
|
||||
"hdmi_output_depth",
|
||||
color_depth_enum_list,
|
||||
ARRAY_SIZE(color_depth_enum_list));
|
||||
if (prop) {
|
||||
hdmi->color_depth_property = prop;
|
||||
drm_object_attach_property(&connector->base, prop, 0);
|
||||
hdmi->colordepth = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
if (hdmi->color_depth_property) {
|
||||
drm_property_destroy(connector->dev,
|
||||
hdmi->color_depth_property);
|
||||
hdmi->color_depth_property = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
dw_hdmi_rockchip_set_property(struct drm_connector *connector,
|
||||
struct drm_connector_state *state,
|
||||
struct drm_property *property,
|
||||
u64 val,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
if (property == hdmi->color_depth_property) {
|
||||
hdmi->colordepth = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("failed to set rockchip hdmi connector property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
dw_hdmi_rockchip_get_property(struct drm_connector *connector,
|
||||
const struct drm_connector_state *state,
|
||||
struct drm_property *property,
|
||||
u64 *val,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
if (property == hdmi->color_depth_property) {
|
||||
*val = hdmi->colordepth;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("failed to get rockchip hdmi connector property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = {
|
||||
.attach_properties = dw_hdmi_rockchip_attach_properties,
|
||||
.destroy_properties = dw_hdmi_rockchip_destroy_properties,
|
||||
.set_property = dw_hdmi_rockchip_set_property,
|
||||
.get_property = dw_hdmi_rockchip_get_property,
|
||||
};
|
||||
|
||||
static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj)
|
||||
@@ -752,6 +882,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
dw_hdmi_rockchip_get_input_bus_format;
|
||||
plat_data->get_output_bus_format =
|
||||
dw_hdmi_rockchip_get_output_bus_format;
|
||||
plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
|
||||
|
||||
encoder = &hdmi->encoder;
|
||||
|
||||
encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm, dev->of_node);
|
||||
|
||||
Reference in New Issue
Block a user