mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 10:31:46 +09:00
drm/rockchip: dw_hdmi: Add property MODE_COLOR_CAPACITY
This property is used to report the supported color format for each resolution to userspace. Change-Id: Ib2e8c712d7590b1cff77d03eb789bc49c18d6e74 Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
This commit is contained in:
@@ -3308,8 +3308,25 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
|
||||
mutex_unlock(&hdmi->mutex);
|
||||
}
|
||||
|
||||
static int drm_hdmi_probe_single_connector_modes(struct drm_connector *connector,
|
||||
uint32_t maxX, uint32_t maxY)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi =
|
||||
container_of(connector, struct dw_hdmi_qp, connector);
|
||||
struct drm_display_info *info = &connector->display_info;
|
||||
void *data = hdmi->plat_data->phy_data;
|
||||
int ret;
|
||||
|
||||
ret = drm_helper_probe_single_connector_modes(connector, maxX, maxY);
|
||||
|
||||
if (hdmi->plat_data->get_mode_color_caps)
|
||||
hdmi->plat_data->get_mode_color_caps(connector, info, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.fill_modes = drm_hdmi_probe_single_connector_modes,
|
||||
.detect = dw_hdmi_connector_detect,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.force = dw_hdmi_connector_force,
|
||||
|
||||
@@ -3622,8 +3622,25 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
|
||||
mutex_unlock(&hdmi->mutex);
|
||||
}
|
||||
|
||||
static int drm_hdmi_probe_single_connector_modes(struct drm_connector *connector,
|
||||
uint32_t maxX, uint32_t maxY)
|
||||
{
|
||||
struct dw_hdmi *hdmi =
|
||||
container_of(connector, struct dw_hdmi, connector);
|
||||
struct drm_display_info *info = &connector->display_info;
|
||||
void *data = hdmi->plat_data->phy_data;
|
||||
int ret;
|
||||
|
||||
ret = drm_helper_probe_single_connector_modes(connector, maxX, maxY);
|
||||
|
||||
if (hdmi->plat_data->get_mode_color_caps)
|
||||
hdmi->plat_data->get_mode_color_caps(connector, info, data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.fill_modes = drm_hdmi_probe_single_connector_modes,
|
||||
.detect = dw_hdmi_connector_detect,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.force = dw_hdmi_connector_force,
|
||||
|
||||
@@ -238,6 +238,22 @@ enum hdmi_frl_rate_per_lane {
|
||||
FRL_3G_PER_LANE = 3,
|
||||
};
|
||||
|
||||
enum mode_color_caps_mask {
|
||||
RGB_8BIT = 0,
|
||||
RGB_10BIT,
|
||||
YUV444_8BIT,
|
||||
YUV444_10BIT,
|
||||
YUV422_8BIT,
|
||||
YUV422_10BIT,
|
||||
YUV420_8BIT,
|
||||
YUV420_10BIT,
|
||||
};
|
||||
|
||||
struct mode_color_caps {
|
||||
struct drm_mode_modeinfo umode;
|
||||
u8 color_caps;
|
||||
};
|
||||
|
||||
struct rockchip_hdmi {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
@@ -295,9 +311,11 @@ struct rockchip_hdmi {
|
||||
struct drm_property *allm_capacity;
|
||||
struct drm_property *allm_enable;
|
||||
struct drm_property *hdcp_state_property;
|
||||
struct drm_property *mode_color_capacity;
|
||||
|
||||
struct drm_property_blob *hdr_panel_blob_ptr;
|
||||
struct drm_property_blob *next_hdr_data_ptr;
|
||||
struct drm_property_blob *mode_color_caps_ptr;
|
||||
|
||||
unsigned int colordepth;
|
||||
unsigned int colorimetry;
|
||||
@@ -324,6 +342,7 @@ struct rockchip_hdmi {
|
||||
struct pinctrl *p;
|
||||
struct pinctrl_state *idle_state;
|
||||
struct pinctrl_state *default_state;
|
||||
struct mode_color_caps *mode_color_caps;
|
||||
bool timing_force_output;
|
||||
struct drm_display_mode force_mode;
|
||||
u32 force_bus_format;
|
||||
@@ -3063,6 +3082,100 @@ static void dw_hdmi_rockchip_force_frl_rate(void *data, u8 rate)
|
||||
phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
|
||||
}
|
||||
|
||||
static u8 mode_color_caps_init(struct drm_connector *connector, struct drm_display_mode *mode,
|
||||
struct drm_display_info *info)
|
||||
{
|
||||
u8 color_caps_mask = RGB_8BIT;
|
||||
|
||||
if (info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)
|
||||
color_caps_mask |= BIT(RGB_10BIT);
|
||||
|
||||
if (info->color_formats & DRM_COLOR_FORMAT_YCBCR444) {
|
||||
color_caps_mask |= BIT(YUV444_8BIT);
|
||||
if (info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)
|
||||
color_caps_mask |= BIT(YUV444_10BIT);
|
||||
}
|
||||
|
||||
/* For hdmi, yuv422 8bit and 10bit are the same format */
|
||||
if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422) {
|
||||
color_caps_mask |= BIT(YUV422_8BIT);
|
||||
color_caps_mask |= BIT(YUV422_10BIT);
|
||||
}
|
||||
|
||||
if (connector->ycbcr_420_allowed && drm_mode_is_420(info, mode)) {
|
||||
color_caps_mask |= BIT(YUV420_8BIT);
|
||||
if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
|
||||
color_caps_mask |= BIT(YUV420_10BIT);
|
||||
}
|
||||
|
||||
return color_caps_mask;
|
||||
}
|
||||
|
||||
static void dw_hdmi_rockchip_get_mode_color_caps(struct drm_connector *connector,
|
||||
struct drm_display_info *info,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
struct drm_display_mode *mode;
|
||||
u8 color_caps_mask;
|
||||
u32 max_tmds_clock = info->max_tmds_clock;
|
||||
u32 size = 0;
|
||||
struct mode_color_caps *caps;
|
||||
struct drm_property *property = hdmi->mode_color_capacity;
|
||||
|
||||
if (list_empty(&connector->modes))
|
||||
return;
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head)
|
||||
size++;
|
||||
|
||||
kfree(hdmi->mode_color_caps);
|
||||
|
||||
size = sizeof(struct mode_color_caps) * size;
|
||||
|
||||
hdmi->mode_color_caps = kmalloc(size, GFP_KERNEL);
|
||||
if (!hdmi->mode_color_caps)
|
||||
return;
|
||||
caps = hdmi->mode_color_caps;
|
||||
|
||||
max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk);
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
color_caps_mask = mode_color_caps_init(connector, mode, info);
|
||||
|
||||
drm_mode_convert_to_umode(&caps->umode, mode);
|
||||
/* hdmi 2.1 frl mode */
|
||||
if (mode->clock > 600000) {
|
||||
if (mode->clock > 1188000)
|
||||
color_caps_mask = BIT(YUV420_8BIT) | BIT(YUV420_10BIT);
|
||||
|
||||
caps->color_caps = color_caps_mask;
|
||||
caps++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* RGB/YUV444 10BIT is out of range */
|
||||
if ((mode->clock * 10 / 8) > max_tmds_clock && mode->clock <= max_tmds_clock) {
|
||||
color_caps_mask &= ~(BIT(RGB_10BIT) | BIT(YUV444_10BIT));
|
||||
/* only support YUV420 */
|
||||
} else if (mode->clock > max_tmds_clock && (mode->clock / 2) <= max_tmds_clock) {
|
||||
color_caps_mask &= ~(BIT(RGB_10BIT) | BIT(RGB_8BIT) | BIT(YUV444_10BIT) |
|
||||
BIT(YUV444_8BIT) | BIT(YUV422_10BIT) |
|
||||
BIT(YUV422_8BIT));
|
||||
|
||||
/* YUV420 10BIT is out of range */
|
||||
if ((mode->clock / 2) * 10 / 8 > max_tmds_clock)
|
||||
color_caps_mask &= ~BIT(YUV420_10BIT);
|
||||
}
|
||||
|
||||
caps->color_caps = color_caps_mask;
|
||||
caps++;
|
||||
}
|
||||
|
||||
drm_property_replace_global_blob(connector->dev, &hdmi->mode_color_caps_ptr, size,
|
||||
hdmi->mode_color_caps, &connector->base, property);
|
||||
}
|
||||
|
||||
static const struct drm_prop_enum_list color_depth_enum_list[] = {
|
||||
{ 0, "Automatic" }, /* Prefer highest color depth */
|
||||
{ 8, "24bit" },
|
||||
@@ -3259,6 +3372,13 @@ dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
|
||||
}
|
||||
hdmi->hdcp_state_property = prop;
|
||||
drm_object_attach_property(&connector->base, prop, RK_IF_HDCP_ENCRYPTED_NONE);
|
||||
|
||||
prop = drm_property_create(connector->dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE,
|
||||
"MODE_COLOR_CAPACITY", 0);
|
||||
if (prop) {
|
||||
hdmi->mode_color_capacity = prop;
|
||||
drm_object_attach_property(&connector->base, prop, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3331,6 +3451,13 @@ dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector,
|
||||
drm_property_destroy(connector->dev, hdmi->allm_enable);
|
||||
hdmi->allm_enable = NULL;
|
||||
}
|
||||
|
||||
if (hdmi->mode_color_capacity) {
|
||||
kfree(hdmi->mode_color_caps);
|
||||
hdmi->mode_color_caps = NULL;
|
||||
drm_property_destroy(connector->dev, hdmi->mode_color_capacity);
|
||||
hdmi->mode_color_capacity = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -3399,6 +3526,8 @@ dw_hdmi_rockchip_set_property(struct drm_connector *connector,
|
||||
return 0;
|
||||
} else if (property == hdmi->hdcp_state_property) {
|
||||
return 0;
|
||||
} else if (property == hdmi->mode_color_capacity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("Unknown property [PROP:%d:%s]\n",
|
||||
@@ -3482,6 +3611,9 @@ dw_hdmi_rockchip_get_property(struct drm_connector *connector,
|
||||
else
|
||||
*val = RK_IF_HDCP_ENCRYPTED_NONE;
|
||||
return 0;
|
||||
} else if (property == hdmi->mode_color_capacity) {
|
||||
*val = hdmi->mode_color_caps_ptr ? hdmi->mode_color_caps_ptr->base.id : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("Unknown property [PROP:%d:%s]\n",
|
||||
@@ -4205,6 +4337,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
dw_hdmi_rockchip_get_refclk_rate;
|
||||
plat_data->force_frl_rate =
|
||||
dw_hdmi_rockchip_force_frl_rate;
|
||||
plat_data->get_mode_color_caps =
|
||||
dw_hdmi_rockchip_get_mode_color_caps;
|
||||
plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
|
||||
|
||||
secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id);
|
||||
|
||||
@@ -273,6 +273,8 @@ struct dw_hdmi_plat_data {
|
||||
struct drm_display_mode *(*get_force_timing)(void *data);
|
||||
u32 (*get_refclk_rate)(void *data);
|
||||
void (*force_frl_rate)(void *data, u8 rate);
|
||||
void (*get_mode_color_caps)(struct drm_connector *connector, struct drm_display_info *info,
|
||||
void *data);
|
||||
|
||||
/* Vendor Property support */
|
||||
const struct dw_hdmi_property_ops *property_ops;
|
||||
|
||||
Reference in New Issue
Block a user