drm/rockchip: analogix_dp: add support for color format yuv444/yuv422

The detailed changes as follows:
1.Add flag max_bpc and format_yuv to check whether the
  platform support 10 bit per component and YUV444/YUV422.
2.Add exact bpp related to output format in bandwidth
  calculation, which is fixed to 24 before the patch.
3.Add .atomic_get_input_bus_fmts() and .atomic_get_output_bus_fmts()
  to get the supported input and output bus formats.

Change-Id: I78ef43d19b3a2970a961b0f668a75ef857951dfe
Signed-off-by: Damon Ding <damon.ding@rock-chips.com>
This commit is contained in:
Damon Ding
2024-04-19 11:46:23 +08:00
committed by Tao Huang
parent f019bedaa1
commit a7620fa846
4 changed files with 170 additions and 29 deletions

View File

@@ -54,23 +54,52 @@ struct bridge_init {
struct device_node *node;
};
static const struct analogix_dp_output_format possible_output_fmts[] = {
{ MEDIA_BUS_FMT_RGB101010_1X30, DRM_COLOR_FORMAT_RGB444, 10 },
{ MEDIA_BUS_FMT_RGB888_1X24, DRM_COLOR_FORMAT_RGB444, 8 },
{ MEDIA_BUS_FMT_RGB666_1X24_CPADHI, DRM_COLOR_FORMAT_RGB444, 6 },
{ MEDIA_BUS_FMT_YUV10_1X30, DRM_COLOR_FORMAT_YCBCR444, 10 },
{ MEDIA_BUS_FMT_YUV8_1X24, DRM_COLOR_FORMAT_YCBCR444, 8},
{ MEDIA_BUS_FMT_YUYV10_1X20, DRM_COLOR_FORMAT_YCBCR422, 10 },
{ MEDIA_BUS_FMT_YUYV8_1X16, DRM_COLOR_FORMAT_YCBCR422, 8 },
};
static u8 analogix_dp_get_output_bpp(const struct analogix_dp_output_format *fmt)
{
switch (fmt->color_format) {
case DRM_COLOR_FORMAT_YCBCR422:
return fmt->bpc * 2;
case DRM_COLOR_FORMAT_RGB444:
case DRM_COLOR_FORMAT_YCBCR444:
default:
return fmt->bpc * 3;
}
}
const struct analogix_dp_output_format *analogix_dp_get_output_format(u32 bus_format)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(possible_output_fmts); i++)
if (possible_output_fmts[i].bus_format == bus_format)
return &possible_output_fmts[i];
return &possible_output_fmts[1];
}
EXPORT_SYMBOL_GPL(analogix_dp_get_output_format);
static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *adj_mode);
static bool analogix_dp_bandwidth_ok(struct analogix_dp_device *dp,
const struct drm_display_mode *mode,
const struct drm_display_mode *mode, u32 bpp,
unsigned int rate, unsigned int lanes)
{
const struct drm_display_info *info;
u32 max_bw, req_bw, bpp = 24;
u32 max_bw, req_bw;
if (dp->plat_data->skip_connector)
return true;
info = &dp->connector.display_info;
if (info->bpc)
bpp = 3 * info->bpc;
req_bw = mode->crtc_clock * bpp / 8;
max_bw = lanes * rate;
if (req_bw > max_bw)
@@ -691,8 +720,9 @@ static u8 analogix_dp_select_link_rate_from_table(struct analogix_dp_device *dp)
for (i = 0; i < dp->nr_link_rate_table; i++) {
bw_code = drm_dp_link_rate_to_bw_code(dp->link_rate_table[i]);
if (!analogix_dp_bandwidth_ok(dp, &dp->video_info.mode, dp->link_rate_table[i],
dp->link_train.lane_count))
if (!analogix_dp_bandwidth_ok(dp, &dp->video_info.mode,
analogix_dp_get_output_bpp(dp->output_fmt),
dp->link_rate_table[i], dp->link_train.lane_count))
continue;
if (dp->link_rate_table[i] <= max_link_rate &&
@@ -832,7 +862,7 @@ static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
return -EINVAL;
}
if (!analogix_dp_bandwidth_ok(dp, &video->mode,
if (!analogix_dp_bandwidth_ok(dp, &video->mode, analogix_dp_get_output_bpp(dp->output_fmt),
drm_dp_bw_code_to_link_rate(dp->link_train.link_rate),
dp->link_train.lane_count)) {
dev_err(dp->dev, "bandwidth overflow\n");
@@ -1746,6 +1776,12 @@ analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
struct analogix_dp_device *dp = bridge->driver_private;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_bridge_state *new_bridge_state;
new_bridge_state = drm_atomic_get_new_bridge_state(old_state, bridge);
if (WARN_ON(!new_bridge_state))
return;
dp->output_fmt = analogix_dp_get_output_format(new_bridge_state->output_bus_cfg.format);
crtc = analogix_dp_get_new_crtc(dp, old_state);
if (!crtc)
@@ -1967,7 +2003,6 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *adj_mode)
{
struct analogix_dp_device *dp = bridge->driver_private;
struct drm_display_info *display_info = &dp->connector.display_info;
struct video_info *video = &dp->video_info;
struct drm_display_mode *mode = &video->mode;
struct device_node *dp_node = dp->dev->of_node;
@@ -1998,7 +2033,7 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
video->dynamic_range = VESA;
/* Input vide bpc and color_formats */
switch (display_info->bpc) {
switch (dp->output_fmt->bpc) {
case 12:
video->color_depth = COLOR_12;
break;
@@ -2015,10 +2050,10 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
video->color_depth = COLOR_8;
break;
}
if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR444) {
if (dp->output_fmt->color_format == DRM_COLOR_FORMAT_YCBCR444) {
video->color_space = COLOR_YCBCR444;
video->ycbcr_coeff = COLOR_YCBCR709;
} else if (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422) {
} else if (dp->output_fmt->color_format == DRM_COLOR_FORMAT_YCBCR422) {
video->color_space = COLOR_YCBCR422;
video->ycbcr_coeff = COLOR_YCBCR709;
} else {
@@ -2057,18 +2092,26 @@ analogix_dp_bridge_mode_valid(struct drm_bridge *bridge,
struct analogix_dp_device *dp = bridge->driver_private;
struct drm_display_mode m;
u32 max_link_rate, max_lane_count;
u32 min_bpp;
drm_mode_copy(&m, mode);
if (dp->plat_data->split_mode || dp->plat_data->dual_connector_split)
dp->plat_data->convert_to_origin_mode(&m);
if (info->color_formats & DRM_COLOR_FORMAT_YCBCR422)
min_bpp = 16;
else if (info->color_formats & DRM_COLOR_FORMAT_RGB444)
min_bpp = 18;
else
min_bpp = 24;
max_link_rate = min_t(u32, dp->video_info.max_link_rate,
dp->link_train.link_rate);
max_lane_count = min_t(u32, dp->video_info.max_lane_count,
dp->link_train.lane_count);
if (analogix_dp_link_config_validate(max_link_rate, max_lane_count) &&
!analogix_dp_bandwidth_ok(dp, &m,
!analogix_dp_bandwidth_ok(dp, &m, min_bpp,
drm_dp_bw_code_to_link_rate(max_link_rate),
max_lane_count))
return MODE_BAD;
@@ -2076,10 +2119,67 @@ analogix_dp_bridge_mode_valid(struct drm_bridge *bridge,
return MODE_OK;
}
static u32 *analogix_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
unsigned int *num_output_fmts)
{
struct analogix_dp_device *dp = bridge->driver_private;
struct drm_display_info *di = &dp->connector.display_info;
u32 *output_fmts;
unsigned int i, j = 0;
if (dp->plat_data->panel) {
*num_output_fmts = 1;
output_fmts = kzalloc(sizeof(*output_fmts), GFP_KERNEL);
if (!output_fmts)
return NULL;
if (di->num_bus_formats && di->bus_formats)
output_fmts[0] = di->bus_formats[0];
else
output_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
return output_fmts;
}
*num_output_fmts = 0;
output_fmts = kcalloc(ARRAY_SIZE(possible_output_fmts), sizeof(*output_fmts), GFP_KERNEL);
if (!output_fmts)
return NULL;
for (i = 0; i < ARRAY_SIZE(possible_output_fmts); i++) {
const struct analogix_dp_output_format *fmt = &possible_output_fmts[i];
if (fmt->bpc > conn_state->max_bpc || fmt->bpc > dp->plat_data->max_bpc)
continue;
if (!(di->color_formats & fmt->color_format))
continue;
if (!analogix_dp_bandwidth_ok(dp, &crtc_state->mode,
analogix_dp_get_output_bpp(fmt),
drm_dp_bw_code_to_link_rate(dp->link_train.link_rate),
dp->link_train.lane_count))
continue;
output_fmts[j++] = fmt->bus_format;
}
*num_output_fmts = j;
return output_fmts;
}
static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.atomic_get_output_bus_fmts = analogix_dp_bridge_atomic_get_output_bus_fmts,
.atomic_pre_enable = analogix_dp_bridge_atomic_pre_enable,
.atomic_enable = analogix_dp_bridge_atomic_enable,
.atomic_disable = analogix_dp_bridge_atomic_disable,

View File

@@ -206,6 +206,8 @@ struct analogix_dp_device {
struct analogix_dp_compliance compliance;
u32 split_area;
const struct analogix_dp_output_format *output_fmt;
};
/* analogix_dp_reg.c */

View File

@@ -70,6 +70,8 @@ struct rockchip_dp_chip_data {
bool ssc;
bool audio;
bool split_mode;
bool format_yuv;
u8 max_bpc;
};
struct rockchip_dp_device {
@@ -230,15 +232,16 @@ static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
struct drm_connector *connector)
{
struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
struct drm_display_info *di = &connector->display_info;
/* VOP couldn't output YUV video format for eDP rightly */
u32 mask = DRM_COLOR_FORMAT_YCBCR444 | DRM_COLOR_FORMAT_YCBCR422;
if ((di->color_formats & mask)) {
DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
di->color_formats &= ~mask;
di->color_formats |= DRM_COLOR_FORMAT_RGB444;
di->bpc = 8;
if (!dp->data->format_yuv) {
if ((di->color_formats & mask)) {
DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
di->color_formats &= ~mask;
di->color_formats |= DRM_COLOR_FORMAT_RGB444;
}
}
return 0;
@@ -463,12 +466,19 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
struct rockchip_dp_device *dp = encoder_to_dp(encoder);
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct drm_display_info *di = &conn_state->connector->display_info;
struct drm_bridge_state *new_bridge_state;
struct drm_bridge *bridge;
const struct analogix_dp_output_format *output_fmt;
int refresh_rate;
if (di->num_bus_formats)
s->bus_format = di->bus_formats[0];
else
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
bridge = drm_bridge_chain_get_first_bridge(encoder);
new_bridge_state = drm_atomic_get_new_bridge_state(conn_state->state, bridge);
if (!new_bridge_state)
return 0;
dev_dbg(dp->dev, "input format 0x%04x, output format 0x%04x\n",
new_bridge_state->input_bus_cfg.format,
new_bridge_state->output_bus_cfg.format);
/*
* The hardware IC designed that VOP must output the RGB10 video
@@ -477,8 +487,17 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
* controller, that's why we need to hardcode the VOP output mode
* to RGA10 here.
*/
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
output_fmt = analogix_dp_get_output_format(new_bridge_state->output_bus_cfg.format);
switch (output_fmt->color_format) {
case DRM_COLOR_FORMAT_YCBCR422:
s->output_mode = ROCKCHIP_OUT_MODE_YUV422;
break;
case DRM_COLOR_FORMAT_RGB444:
case DRM_COLOR_FORMAT_YCBCR444:
default:
s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
break;
}
s->output_type = DRM_MODE_CONNECTOR_eDP;
if (dp->plat_data.split_mode) {
s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
@@ -495,12 +514,16 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
s->output_if |= dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0;
}
s->output_bpc = di->bpc;
s->bus_format = output_fmt->bus_format;
s->output_bpc = output_fmt->bpc;
s->bus_flags = di->bus_flags;
s->tv_state = &conn_state->tv;
s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
s->color_encoding = DRM_COLOR_YCBCR_BT709;
s->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
if (output_fmt->color_format == DRM_COLOR_FORMAT_RGB444)
s->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
else
s->color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
/**
* It's priority to user rate range define in dtsi.
*/
@@ -707,6 +730,7 @@ static int rockchip_dp_probe(struct platform_device *pdev)
dp->adp = ERR_PTR(-ENODEV);
dp->data = &dp_data[id];
dp->plat_data.ssc = dp->data->ssc;
dp->plat_data.max_bpc = dp->data->max_bpc ? dp->data->max_bpc : 8;
dp->plat_data.panel = panel;
dp->plat_data.dev_type = dp->data->chip_type;
dp->plat_data.power_on_start = rockchip_dp_poweron_start;
@@ -863,6 +887,8 @@ static const struct rockchip_dp_chip_data rk3576_edp[] = {
.ssc = true,
.audio = true,
.split_mode = true,
.format_yuv = true,
.max_bpc = 10,
},
{ /* sentinel */ }
};
@@ -876,6 +902,8 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = {
.ssc = true,
.audio = true,
.split_mode = true,
.format_yuv = true,
.max_bpc = 10,
},
{
.chip_type = RK3588_EDP,
@@ -885,6 +913,8 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = {
.ssc = true,
.audio = true,
.split_mode = true,
.format_yuv = true,
.max_bpc = 10,
},
{ /* sentinel */ }
};

View File

@@ -51,6 +51,8 @@ struct analogix_dp_plat_data {
bool dual_connector_split;
bool left_display;
u8 max_bpc;
struct analogix_dp_device *left;
struct analogix_dp_device *right;
@@ -66,6 +68,12 @@ struct analogix_dp_plat_data {
void (*convert_to_origin_mode)(struct drm_display_mode *);
};
struct analogix_dp_output_format {
u32 bus_format;
u32 color_format;
u8 bpc;
};
int analogix_dp_resume(struct analogix_dp_device *dp);
int analogix_dp_suspend(struct analogix_dp_device *dp);
int analogix_dp_runtime_resume(struct analogix_dp_device *dp);
@@ -89,5 +97,6 @@ int analogix_dp_audio_get_eld(struct analogix_dp_device *dp,
u8 *buf, size_t len);
int analogix_dp_loader_protect(struct analogix_dp_device *dp);
void analogix_dp_disable(struct analogix_dp_device *dp);
const struct analogix_dp_output_format *analogix_dp_get_output_format(u32 bus_format);
#endif /* _ANALOGIX_DP_H_ */