diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 9e39d5090b1c..5831088e2cdd 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -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, diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 09f8319edf36..2b2883029c5b 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -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 */ diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 861c407b2ab4..a48a3b5d2460 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -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 */ } }; diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 3773a093b856..4abaf81c2238 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -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_ */