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; 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, static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *adj_mode); const struct drm_display_mode *adj_mode);
static bool analogix_dp_bandwidth_ok(struct analogix_dp_device *dp, 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) unsigned int rate, unsigned int lanes)
{ {
const struct drm_display_info *info; u32 max_bw, req_bw;
u32 max_bw, req_bw, bpp = 24;
if (dp->plat_data->skip_connector) if (dp->plat_data->skip_connector)
return true; return true;
info = &dp->connector.display_info;
if (info->bpc)
bpp = 3 * info->bpc;
req_bw = mode->crtc_clock * bpp / 8; req_bw = mode->crtc_clock * bpp / 8;
max_bw = lanes * rate; max_bw = lanes * rate;
if (req_bw > max_bw) 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++) { for (i = 0; i < dp->nr_link_rate_table; i++) {
bw_code = drm_dp_link_rate_to_bw_code(dp->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], if (!analogix_dp_bandwidth_ok(dp, &dp->video_info.mode,
dp->link_train.lane_count)) analogix_dp_get_output_bpp(dp->output_fmt),
dp->link_rate_table[i], dp->link_train.lane_count))
continue; continue;
if (dp->link_rate_table[i] <= max_link_rate && 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; 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), drm_dp_bw_code_to_link_rate(dp->link_train.link_rate),
dp->link_train.lane_count)) { dp->link_train.lane_count)) {
dev_err(dp->dev, "bandwidth overflow\n"); 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 analogix_dp_device *dp = bridge->driver_private;
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state; 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); crtc = analogix_dp_get_new_crtc(dp, old_state);
if (!crtc) if (!crtc)
@@ -1967,7 +2003,6 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *adj_mode) const struct drm_display_mode *adj_mode)
{ {
struct analogix_dp_device *dp = bridge->driver_private; 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 video_info *video = &dp->video_info;
struct drm_display_mode *mode = &video->mode; struct drm_display_mode *mode = &video->mode;
struct device_node *dp_node = dp->dev->of_node; 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; video->dynamic_range = VESA;
/* Input vide bpc and color_formats */ /* Input vide bpc and color_formats */
switch (display_info->bpc) { switch (dp->output_fmt->bpc) {
case 12: case 12:
video->color_depth = COLOR_12; video->color_depth = COLOR_12;
break; break;
@@ -2015,10 +2050,10 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
video->color_depth = COLOR_8; video->color_depth = COLOR_8;
break; 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->color_space = COLOR_YCBCR444;
video->ycbcr_coeff = COLOR_YCBCR709; 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->color_space = COLOR_YCBCR422;
video->ycbcr_coeff = COLOR_YCBCR709; video->ycbcr_coeff = COLOR_YCBCR709;
} else { } else {
@@ -2057,18 +2092,26 @@ analogix_dp_bridge_mode_valid(struct drm_bridge *bridge,
struct analogix_dp_device *dp = bridge->driver_private; struct analogix_dp_device *dp = bridge->driver_private;
struct drm_display_mode m; struct drm_display_mode m;
u32 max_link_rate, max_lane_count; u32 max_link_rate, max_lane_count;
u32 min_bpp;
drm_mode_copy(&m, mode); drm_mode_copy(&m, mode);
if (dp->plat_data->split_mode || dp->plat_data->dual_connector_split) if (dp->plat_data->split_mode || dp->plat_data->dual_connector_split)
dp->plat_data->convert_to_origin_mode(&m); 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, max_link_rate = min_t(u32, dp->video_info.max_link_rate,
dp->link_train.link_rate); dp->link_train.link_rate);
max_lane_count = min_t(u32, dp->video_info.max_lane_count, max_lane_count = min_t(u32, dp->video_info.max_lane_count,
dp->link_train.lane_count); dp->link_train.lane_count);
if (analogix_dp_link_config_validate(max_link_rate, max_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), drm_dp_bw_code_to_link_rate(max_link_rate),
max_lane_count)) max_lane_count))
return MODE_BAD; return MODE_BAD;
@@ -2076,10 +2119,67 @@ analogix_dp_bridge_mode_valid(struct drm_bridge *bridge,
return MODE_OK; 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 = { static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset, .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_pre_enable = analogix_dp_bridge_atomic_pre_enable,
.atomic_enable = analogix_dp_bridge_atomic_enable, .atomic_enable = analogix_dp_bridge_atomic_enable,
.atomic_disable = analogix_dp_bridge_atomic_disable, .atomic_disable = analogix_dp_bridge_atomic_disable,

View File

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

View File

@@ -70,6 +70,8 @@ struct rockchip_dp_chip_data {
bool ssc; bool ssc;
bool audio; bool audio;
bool split_mode; bool split_mode;
bool format_yuv;
u8 max_bpc;
}; };
struct rockchip_dp_device { 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, static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
struct drm_connector *connector) struct drm_connector *connector)
{ {
struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
struct drm_display_info *di = &connector->display_info; 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; u32 mask = DRM_COLOR_FORMAT_YCBCR444 | DRM_COLOR_FORMAT_YCBCR422;
if ((di->color_formats & mask)) { if (!dp->data->format_yuv) {
DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n"); if ((di->color_formats & mask)) {
di->color_formats &= ~mask; DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
di->color_formats |= DRM_COLOR_FORMAT_RGB444; di->color_formats &= ~mask;
di->bpc = 8; di->color_formats |= DRM_COLOR_FORMAT_RGB444;
}
} }
return 0; 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_dp_device *dp = encoder_to_dp(encoder);
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct drm_display_info *di = &conn_state->connector->display_info; 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; int refresh_rate;
if (di->num_bus_formats) bridge = drm_bridge_chain_get_first_bridge(encoder);
s->bus_format = di->bus_formats[0]; new_bridge_state = drm_atomic_get_new_bridge_state(conn_state->state, bridge);
else if (!new_bridge_state)
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; 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 * 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 * controller, that's why we need to hardcode the VOP output mode
* to RGA10 here. * to RGA10 here.
*/ */
output_fmt = analogix_dp_get_output_format(new_bridge_state->output_bus_cfg.format);
s->output_mode = ROCKCHIP_OUT_MODE_AAAA; 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; s->output_type = DRM_MODE_CONNECTOR_eDP;
if (dp->plat_data.split_mode) { if (dp->plat_data.split_mode) {
s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_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_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->bus_flags = di->bus_flags;
s->tv_state = &conn_state->tv; s->tv_state = &conn_state->tv;
s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR; s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
s->color_encoding = DRM_COLOR_YCBCR_BT709; 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. * 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->adp = ERR_PTR(-ENODEV);
dp->data = &dp_data[id]; dp->data = &dp_data[id];
dp->plat_data.ssc = dp->data->ssc; 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.panel = panel;
dp->plat_data.dev_type = dp->data->chip_type; dp->plat_data.dev_type = dp->data->chip_type;
dp->plat_data.power_on_start = rockchip_dp_poweron_start; 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, .ssc = true,
.audio = true, .audio = true,
.split_mode = true, .split_mode = true,
.format_yuv = true,
.max_bpc = 10,
}, },
{ /* sentinel */ } { /* sentinel */ }
}; };
@@ -876,6 +902,8 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = {
.ssc = true, .ssc = true,
.audio = true, .audio = true,
.split_mode = true, .split_mode = true,
.format_yuv = true,
.max_bpc = 10,
}, },
{ {
.chip_type = RK3588_EDP, .chip_type = RK3588_EDP,
@@ -885,6 +913,8 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = {
.ssc = true, .ssc = true,
.audio = true, .audio = true,
.split_mode = true, .split_mode = true,
.format_yuv = true,
.max_bpc = 10,
}, },
{ /* sentinel */ } { /* sentinel */ }
}; };

View File

@@ -51,6 +51,8 @@ struct analogix_dp_plat_data {
bool dual_connector_split; bool dual_connector_split;
bool left_display; bool left_display;
u8 max_bpc;
struct analogix_dp_device *left; struct analogix_dp_device *left;
struct analogix_dp_device *right; struct analogix_dp_device *right;
@@ -66,6 +68,12 @@ struct analogix_dp_plat_data {
void (*convert_to_origin_mode)(struct drm_display_mode *); 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_resume(struct analogix_dp_device *dp);
int analogix_dp_suspend(struct analogix_dp_device *dp); int analogix_dp_suspend(struct analogix_dp_device *dp);
int analogix_dp_runtime_resume(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); u8 *buf, size_t len);
int analogix_dp_loader_protect(struct analogix_dp_device *dp); int analogix_dp_loader_protect(struct analogix_dp_device *dp);
void analogix_dp_disable(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_ */ #endif /* _ANALOGIX_DP_H_ */