diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 9a185032a3bd..9d6fcf986022 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1151,6 +1151,47 @@ static int select_bus_fmt_recursive(struct drm_bridge *first_bridge, return ret; } +static bool __pure bus_format_is_color_fmt(u32 bus_fmt, enum drm_connector_color_format fmt) +{ + if (fmt == DRM_CONNECTOR_COLOR_FORMAT_AUTO) + return true; + + switch (bus_fmt) { + case MEDIA_BUS_FMT_FIXED: + return true; + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_RGB444; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + default: + return false; + } +} + /* * This function is called by &drm_atomic_bridge_chain_check() just before * calling &drm_bridge_funcs.atomic_check() on all elements of the chain. @@ -1194,6 +1235,7 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, struct drm_encoder *encoder = bridge->encoder; struct drm_bridge_state *last_bridge_state; unsigned int i, num_out_bus_fmts = 0; + enum drm_connector_color_format fmt; u32 *out_bus_fmts; int ret = 0; @@ -1235,11 +1277,31 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; } + /* + * Instead of directly accessing conn_state.color_format, call into a + * connector function that allows connector implementations (e.g. for + * bridge connectors including HDMI bridges, where the HDMI helpers will + * have already chosen an appropriate output format) to override the + * selected format. + */ + fmt = drm_connector_get_color_format(conn_state); + for (i = 0; i < num_out_bus_fmts; i++) { + if (!bus_format_is_color_fmt(out_bus_fmts[i], fmt)) { + drm_dbg_kms(last_bridge->dev, + "Skipping bus format 0x%04x as it doesn't match format %d\n", + out_bus_fmts[i], fmt); + ret = -ENOTSUPP; + continue; + } ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state, conn_state, out_bus_fmts[i]); - if (ret != -ENOTSUPP) + if (ret != -ENOTSUPP) { + drm_dbg_kms(last_bridge->dev, + "Found bridge chain ending with bus format 0x%04x\n", + out_bus_fmts[i]); break; + } } kfree(out_bus_fmts);