diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 81569009f88481..1ce3365713cc42 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,7 @@ drm-y := drm_auth.o drm_cache.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ - drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \ + drm_client_modeset.o drm_atomic_uapi.o drm_hdmi.o drm_hdcp.o \ drm_managed.o drm_vblank_work.o drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 4c4fb4b52b7d1c..86847813c68b58 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -801,92 +802,6 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi) } EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable); -static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) -{ - switch (bus_format) { - 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 true; - - default: - return false; - } -} - -static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) -{ - switch (bus_format) { - 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 true; - - default: - return false; - } -} - -static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) -{ - switch (bus_format) { - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_UYVY10_1X20: - case MEDIA_BUS_FMT_UYVY12_1X24: - return true; - - default: - return false; - } -} - -static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format) -{ - switch (bus_format) { - 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 true; - - default: - return false; - } -} - -static int hdmi_bus_fmt_color_depth(unsigned int bus_format) -{ - switch (bus_format) { - case MEDIA_BUS_FMT_RGB888_1X24: - case MEDIA_BUS_FMT_YUV8_1X24: - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_UYYVYY8_0_5X24: - return 8; - - case MEDIA_BUS_FMT_RGB101010_1X30: - case MEDIA_BUS_FMT_YUV10_1X30: - case MEDIA_BUS_FMT_UYVY10_1X20: - case MEDIA_BUS_FMT_UYYVYY10_0_5X30: - return 10; - - case MEDIA_BUS_FMT_RGB121212_1X36: - case MEDIA_BUS_FMT_YUV12_1X36: - case MEDIA_BUS_FMT_UYVY12_1X24: - case MEDIA_BUS_FMT_UYYVYY12_0_5X36: - return 12; - - case MEDIA_BUS_FMT_RGB161616_1X48: - case MEDIA_BUS_FMT_YUV16_1X48: - case MEDIA_BUS_FMT_UYYVYY16_0_5X48: - return 16; - - default: - return 0; - } -} - /* * this submodule is responsible for the video data synchronization. * for example, for RGB 4:4:4 input, the data map is defined as @@ -967,8 +882,8 @@ static int is_color_space_conversion(struct dw_hdmi *hdmi) struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; bool is_input_rgb, is_output_rgb; - is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_in_bus_format); - is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi_data->enc_out_bus_format); + is_input_rgb = drm_hdmi_bus_fmt_is_rgb(hdmi_data->enc_in_bus_format); + is_output_rgb = drm_hdmi_bus_fmt_is_rgb(hdmi_data->enc_out_bus_format); return (is_input_rgb != is_output_rgb) || (is_input_rgb && is_output_rgb && hdmi_data->rgb_limited_range); @@ -976,11 +891,11 @@ static int is_color_space_conversion(struct dw_hdmi *hdmi) static int is_color_space_decimation(struct dw_hdmi *hdmi) { - if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) + if (!drm_hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) return 0; - if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) || - hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format)) + if (drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) || + drm_hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format)) return 1; return 0; @@ -988,11 +903,11 @@ static int is_color_space_decimation(struct dw_hdmi *hdmi) static int is_color_space_interpolation(struct dw_hdmi *hdmi) { - if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format)) + if (!drm_hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format)) return 0; - if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || - hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) + if (drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || + drm_hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) return 1; return 0; @@ -1012,8 +927,8 @@ static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) unsigned i; u32 csc_scale = 1; - is_input_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format); - is_output_rgb = hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format); + is_input_rgb = drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format); + is_output_rgb = drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format); if (!is_input_rgb && is_output_rgb) { if (hdmi->hdmi_data.enc_out_encoding == V4L2_YCBCR_ENC_601) @@ -1061,7 +976,7 @@ static void hdmi_video_csc(struct dw_hdmi *hdmi) else if (is_color_space_decimation(hdmi)) decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; - switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) { + switch (drm_hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) { case 8: color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; break; @@ -1100,10 +1015,10 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; u8 val, vp_conf; - if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || - hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || - hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { - switch (hdmi_bus_fmt_color_depth( + if (drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || + drm_hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format) || + drm_hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { + switch (drm_hdmi_bus_fmt_color_depth( hdmi->hdmi_data.enc_out_bus_format)) { case 8: color_depth = 4; @@ -1121,8 +1036,8 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) default: output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; } - } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { - switch (hdmi_bus_fmt_color_depth( + } else if (drm_hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { + switch (drm_hdmi_bus_fmt_color_depth( hdmi->hdmi_data.enc_out_bus_format)) { case 0: case 8: @@ -1641,7 +1556,7 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, /* Initialise info frame from DRM mode */ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode); - if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { + if (drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode, hdmi->hdmi_data.rgb_limited_range ? HDMI_QUANTIZATION_RANGE_LIMITED : @@ -1652,17 +1567,17 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, HDMI_YCC_QUANTIZATION_RANGE_LIMITED; } - if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) + if (drm_hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) frame.colorspace = HDMI_COLORSPACE_YUV444; - else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) + else if (drm_hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) frame.colorspace = HDMI_COLORSPACE_YUV422; - else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) + else if (drm_hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) frame.colorspace = HDMI_COLORSPACE_YUV420; else frame.colorspace = HDMI_COLORSPACE_RGB; /* Set up colorimetry */ - if (!hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { + if (!drm_hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { switch (hdmi->hdmi_data.enc_out_encoding) { case V4L2_YCBCR_ENC_601: if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) @@ -1864,8 +1779,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, vmode->mtmdsclock = vmode->mpixelclock; - if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { - switch (hdmi_bus_fmt_color_depth( + if (!drm_hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { + switch (drm_hdmi_bus_fmt_color_depth( hdmi->hdmi_data.enc_out_bus_format)) { case 16: vmode->mtmdsclock = vmode->mpixelclock * 2; @@ -1879,7 +1794,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, } } - if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) + if (drm_hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) vmode->mtmdsclock /= 2; dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock); @@ -1930,7 +1845,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, * When we're setting a YCbCr420 mode, we need * to adjust the horizontal timing to suit. */ - if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { + if (drm_hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format)) { hdisplay /= 2; hblank /= 2; h_de_hs /= 2; @@ -2498,133 +2413,6 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi) * DRM Bridge Operations */ -/* - * Possible output formats : - * - MEDIA_BUS_FMT_UYYVYY16_0_5X48, - * - MEDIA_BUS_FMT_UYYVYY12_0_5X36, - * - MEDIA_BUS_FMT_UYYVYY10_0_5X30, - * - MEDIA_BUS_FMT_UYYVYY8_0_5X24, - * - MEDIA_BUS_FMT_YUV16_1X48, - * - MEDIA_BUS_FMT_RGB161616_1X48, - * - MEDIA_BUS_FMT_UYVY12_1X24, - * - MEDIA_BUS_FMT_YUV12_1X36, - * - MEDIA_BUS_FMT_RGB121212_1X36, - * - MEDIA_BUS_FMT_UYVY10_1X20, - * - MEDIA_BUS_FMT_YUV10_1X30, - * - MEDIA_BUS_FMT_RGB101010_1X30, - * - MEDIA_BUS_FMT_UYVY8_1X16, - * - MEDIA_BUS_FMT_YUV8_1X24, - * - MEDIA_BUS_FMT_RGB888_1X24, - */ - -/* Can return a maximum of 11 possible output formats for a mode/connector */ -#define MAX_OUTPUT_SEL_FORMATS 11 - -static u32 *dw_hdmi_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 drm_connector *conn = conn_state->connector; - struct drm_display_info *info = &conn->display_info; - struct drm_display_mode *mode = &crtc_state->mode; - u8 max_bpc = conn_state->max_requested_bpc; - bool is_hdmi2_sink = info->hdmi.scdc.supported || - (info->color_formats & DRM_COLOR_FORMAT_YCRCB420); - u32 *output_fmts; - unsigned int i = 0; - - *num_output_fmts = 0; - - output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), - GFP_KERNEL); - if (!output_fmts) - return NULL; - - /* If dw-hdmi is the only bridge, avoid negociating with ourselves */ - if (list_is_singular(&bridge->encoder->bridge_chain)) { - *num_output_fmts = 1; - output_fmts[0] = MEDIA_BUS_FMT_FIXED; - - return output_fmts; - } - - /* - * If the current mode enforces 4:2:0, force the output but format - * to 4:2:0 and do not add the YUV422/444/RGB formats - */ - if (conn->ycbcr_420_allowed && - (drm_mode_is_420_only(info, mode) || - (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) { - - /* Order bus formats from 16bit to 8bit if supported */ - if (max_bpc >= 16 && info->bpc == 16 && - (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)) - output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48; - - if (max_bpc >= 12 && info->bpc >= 12 && - (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)) - output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36; - - if (max_bpc >= 10 && info->bpc >= 10 && - (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)) - output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; - - /* Default 8bit fallback */ - output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; - - *num_output_fmts = i; - - return output_fmts; - } - - /* - * Order bus formats from 16bit to 8bit and from YUV422 to RGB - * if supported. In any case the default RGB888 format is added - */ - - if (max_bpc >= 16 && info->bpc == 16) { - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; - - output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; - } - - if (max_bpc >= 12 && info->bpc >= 12) { - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; - - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; - - output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; - } - - if (max_bpc >= 10 && info->bpc >= 10) { - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; - - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; - - output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; - } - - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) - output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; - - if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) - output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; - - /* Default 8bit RGB fallback */ - output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; - - *num_output_fmts = i; - - return output_fmts; -} - /* * Possible input formats : * - MEDIA_BUS_FMT_RGB888_1X24 @@ -2877,7 +2665,7 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { .attach = dw_hdmi_bridge_attach, .detach = dw_hdmi_bridge_detach, .atomic_check = dw_hdmi_bridge_atomic_check, - .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts, + .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_hdmi_get_output_bus_fmts, .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts, .atomic_enable = dw_hdmi_bridge_atomic_enable, .atomic_disable = dw_hdmi_bridge_atomic_disable, diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 64f0effb52ac12..253cbca1c19e05 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1035,6 +1035,124 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_check); +/* Can return a maximum of 11 possible output formats for a mode/connector */ +#define MAX_OUTPUT_SEL_FORMATS 11 + +/** + * drm_bridge_helper_hdmi_atomic_get_output_bus_fmts() - Lists the output formats for an HDMI sink + * @bridge: bridge control structure + * @bridge_state: bridge state + * @crtc_state: CRTC state + * @conn_state: connector state + * @num_output_fmts: number of formats returned + * + * Returns the supported bus formats on the output end of an HDMI + * bridge. The returned array is allocated with kmalloc and will thus + * need to be freed. Formats will be listed in decreasing preference + * order, the framework eventually picking the highest preference + * available across all the bridges. + * + * RETURNS: + * an array of MEDIA_FMT_* on success, NULL on failure + */ +u32 *drm_atomic_helper_bridge_hdmi_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 drm_connector *conn = conn_state->connector; + struct drm_display_info *info = &conn->display_info; + struct drm_display_mode *mode = &crtc_state->mode; + u8 max_bpc = conn_state->max_requested_bpc; + bool is_hdmi2_sink = info->hdmi.scdc.supported || + (info->color_formats & DRM_COLOR_FORMAT_YCRCB420); + u32 *output_fmts; + unsigned int i = 0; + + *num_output_fmts = 0; + + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; + + /* + * If the current mode enforces 4:2:0, force the output but format + * to 4:2:0 and do not add the YUV422/444/RGB formats + */ + if (conn->ycbcr_420_allowed && + (drm_mode_is_420_only(info, mode) || + (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) { + + /* Order bus formats from 16bit to 8bit if supported */ + if (max_bpc >= 16 && info->bpc == 16 && + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)) + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48; + + if (max_bpc >= 12 && info->bpc >= 12 && + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)) + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36; + + if (max_bpc >= 10 && info->bpc >= 10 && + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)) + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; + + /* Default 8bit fallback */ + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + + *num_output_fmts = i; + + return output_fmts; + } + + /* + * Order bus formats from 16bit to 8bit and from YUV422 to RGB + * if supported. In any case the default RGB888 format is added + */ + + if (max_bpc >= 16 && info->bpc == 16) { + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48; + } + + if (max_bpc >= 12 && info->bpc >= 12) { + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24; + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36; + } + + if (max_bpc >= 10 && info->bpc >= 10) { + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20; + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30; + + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30; + } + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422) + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16; + + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444) + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24; + + /* Default 8bit RGB fallback */ + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24; + + *num_output_fmts = i; + + return output_fmts; +} +EXPORT_SYMBOL_GPL(drm_atomic_helper_bridge_hdmi_get_output_bus_fmts); + /** * drm_bridge_detect - check if anything is attached to the bridge output * @bridge: bridge control structure diff --git a/drivers/gpu/drm/drm_hdmi.c b/drivers/gpu/drm/drm_hdmi.c new file mode 100644 index 00000000000000..3834d5dd6d88f2 --- /dev/null +++ b/drivers/gpu/drm/drm_hdmi.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include + +#include +#include + +/** + * drm_hdmi_bus_fmt_is_rgb() - Is the media bus format an RGB format? + * @bus_format: MEDIA_BUS_FMT* to test + * + * Checks if the media bus format is an RGB one + * + * RETURNS: + * True if the format is an RGB one, false otherwise + */ +bool drm_hdmi_bus_fmt_is_rgb(u32 bus_format) +{ + switch (bus_format) { + 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 true; + + default: + return false; + } +} +EXPORT_SYMBOL(drm_hdmi_bus_fmt_is_rgb); + +/** + * drm_hdmi_bus_fmt_is_yuv444() - Is the media bus format an YUV444 format? + * @bus_format: MEDIA_BUS_FMT* to test + * + * Checks if the media bus format is an YUV444 one + * + * RETURNS: + * True if the format is an YUV444 one, false otherwise + */ +bool drm_hdmi_bus_fmt_is_yuv444(u32 bus_format) +{ + switch (bus_format) { + 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 true; + + default: + return false; + } +} +EXPORT_SYMBOL(drm_hdmi_bus_fmt_is_yuv444); + +/** + * drm_hdmi_bus_fmt_is_yuv422() - Is the media bus format an YUV422 format? + * @bus_format: MEDIA_BUS_FMT* to test + * + * Checks if the media bus format is an YUV422 one + * + * RETURNS: + * True if the format is an YUV422 one, false otherwise + */ +bool drm_hdmi_bus_fmt_is_yuv422(u32 bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + return true; + + default: + return false; + } +} +EXPORT_SYMBOL(drm_hdmi_bus_fmt_is_yuv422); + +/** + * drm_hdmi_bus_fmt_is_yuv420() - Is the media bus format an YUV420 format? + * @bus_format: MEDIA_BUS_FMT* to test + * + * Checks if the media bus format is an YUV420 one + * + * RETURNS: + * True if the format is an YUV420 one, false otherwise + */ +bool drm_hdmi_bus_fmt_is_yuv420(u32 bus_format) +{ + switch (bus_format) { + 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 true; + + default: + return false; + } +} +EXPORT_SYMBOL(drm_hdmi_bus_fmt_is_yuv420); + +/** + * drm_hdmi_bus_fmt_color_depth() - Returns the color depth in bits + * @bus_format: MEDIA_BUS_FMT* to test + * + * Computes the number of bits per color for a given media bus format + * + * RETURNS: + * The number of bits per color + */ +int drm_hdmi_bus_fmt_color_depth(u32 bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + return 8; + + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + return 10; + + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + return 12; + + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return 16; + + default: + return 0; + } +} +EXPORT_SYMBOL(drm_hdmi_bus_fmt_color_depth); + +/** + * drm_hdmi_bus_fmt_color_depth() - Returns the color depth in bits + * @bus_format: MEDIA_BUS_FMT* to test + * + * Computes the number of bits per color for a given media bus format + * + * RETURNS: + * The number of bits per color + */ +int drm_hdmi_avi_infoframe_output_colorspace(struct hdmi_avi_infoframe *frame, + struct drm_bus_cfg *out_bus_cfg) +{ + if (drm_hdmi_bus_fmt_is_yuv444(out_bus_cfg->format)) + frame->colorspace = HDMI_COLORSPACE_YUV444; + else if (drm_hdmi_bus_fmt_is_yuv422(out_bus_cfg->format)) + frame->colorspace = HDMI_COLORSPACE_YUV422; + else if (drm_hdmi_bus_fmt_is_yuv420(out_bus_cfg->format)) + frame->colorspace = HDMI_COLORSPACE_YUV420; + else if (drm_hdmi_bus_fmt_is_rgb(out_bus_cfg->format)) + frame->colorspace = HDMI_COLORSPACE_RGB; + else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(drm_hdmi_avi_infoframe_output_colorspace); diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 6d71c7e6979a7d..ad053e22a8dd34 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -263,6 +263,19 @@ static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc, PV_CONTROL_FIFO_LEVEL); } +struct drm_encoder *vc4_get_connector_encoder(struct drm_connector *connector) +{ + struct drm_encoder *encoder; + + if (WARN_ON(hweight32(connector->possible_encoders) != 1)) + return NULL; + + drm_connector_for_each_possible_encoder(connector, encoder) + return encoder; + + return NULL; +} + /* * Returns the encoder attached to the CRTC. * @@ -277,9 +290,22 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) drm_connector_list_iter_begin(crtc->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->state->crtc == crtc) { + struct drm_encoder *encoder; + struct drm_bridge *bridge; + struct vc4_bridge *vc4_bridge; + + encoder = vc4_get_connector_encoder(connector); + if (!encoder) + continue; + + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (!bridge) + continue; + + vc4_bridge = to_vc4_bridge(bridge); + if (vc4_bridge->crtc == crtc) { drm_connector_list_iter_end(&conn_iter); - return connector->encoder; + return encoder; } } drm_connector_list_iter_end(&conn_iter); @@ -416,7 +442,8 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, unsigned int channel) { struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); + struct vc4_bridge *vc4_bridge = to_vc4_bridge(bridge); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_device *dev = crtc->dev; int ret; @@ -444,14 +471,14 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, */ mdelay(20); - if (vc4_encoder && vc4_encoder->post_crtc_disable) - vc4_encoder->post_crtc_disable(encoder, state); + if (vc4_bridge && vc4_bridge->post_crtc_disable) + vc4_bridge->post_crtc_disable(bridge, state); vc4_crtc_pixelvalve_reset(crtc); vc4_hvs_stop_channel(dev, channel); - if (vc4_encoder && vc4_encoder->post_crtc_powerdown) - vc4_encoder->post_crtc_powerdown(encoder, state); + if (vc4_bridge && vc4_bridge->post_crtc_powerdown) + vc4_bridge->post_crtc_powerdown(bridge, state); return 0; } @@ -516,7 +543,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder); + struct vc4_bridge *vc4_bridge = to_vc4_bridge(bridge); require_hvs_enabled(dev); @@ -527,15 +555,15 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, vc4_hvs_atomic_enable(crtc, state); - if (vc4_encoder->pre_crtc_configure) - vc4_encoder->pre_crtc_configure(encoder, state); + if (vc4_bridge->pre_crtc_configure) + vc4_bridge->pre_crtc_configure(bridge, state); vc4_crtc_config_pv(crtc); CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN); - if (vc4_encoder->pre_crtc_enable) - vc4_encoder->pre_crtc_enable(encoder, state); + if (vc4_bridge->pre_crtc_enable) + vc4_bridge->pre_crtc_enable(bridge, state); /* When feeding the transposer block the pixelvalve is unneeded and * should not be enabled. @@ -543,8 +571,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, CRTC_WRITE(PV_V_CONTROL, CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); - if (vc4_encoder->post_crtc_enable) - vc4_encoder->post_crtc_enable(encoder, state); + if (vc4_bridge->post_crtc_enable) + vc4_bridge->post_crtc_enable(bridge, state); } static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc, @@ -1040,6 +1068,9 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct vc4_encoder *vc4_encoder; int i; + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) + continue; + vc4_encoder = to_vc4_encoder(encoder); for (i = 0; i < ARRAY_SIZE(pv_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) { diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 617c113b033fba..9ea1f787330cf4 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -226,6 +226,46 @@ static int compare_dev(struct device *dev, void *data) return dev == data; } +static struct drm_crtc *vc4_drv_find_crtc(struct drm_device *drm, + struct drm_encoder *encoder) +{ + struct drm_crtc *crtc; + + if (WARN_ON(hweight32(encoder->possible_crtcs) != 1)) + return NULL; + + drm_for_each_crtc(crtc, drm) { + if (!drm_encoder_crtc_ok(encoder, crtc)) + continue; + + return crtc; + } + + return NULL; +} + +static void vc4_drv_set_bridge_data(struct drm_device *drm) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, drm) { + struct vc4_bridge *vc4_bridge; + struct drm_bridge *bridge; + struct drm_crtc *crtc; + + crtc = vc4_drv_find_crtc(drm, encoder); + if (WARN_ON(!crtc)) + return; + + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (!bridge) + continue; + + vc4_bridge = to_vc4_bridge(bridge); + vc4_bridge->crtc = crtc; + } +} + static void vc4_match_add_drivers(struct device *dev, struct component_match **match, struct platform_driver *const *drivers, @@ -308,6 +348,7 @@ static int vc4_drm_bind(struct device *dev) ret = component_bind_all(dev, drm); if (ret) return ret; + vc4_drv_set_bridge_data(drm); if (!vc4->firmware_kms) { ret = vc4_plane_create_additional_planes(drm); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 52c3ee43d004e1..23a4c2bde9c3a1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -449,13 +450,6 @@ struct vc4_encoder { struct drm_encoder base; enum vc4_encoder_type type; u32 clock_select; - - void (*pre_crtc_configure)(struct drm_encoder *encoder, struct drm_atomic_state *state); - void (*pre_crtc_enable)(struct drm_encoder *encoder, struct drm_atomic_state *state); - void (*post_crtc_enable)(struct drm_encoder *encoder, struct drm_atomic_state *state); - - void (*post_crtc_disable)(struct drm_encoder *encoder, struct drm_atomic_state *state); - void (*post_crtc_powerdown)(struct drm_encoder *encoder, struct drm_atomic_state *state); }; static inline struct vc4_encoder * @@ -464,6 +458,24 @@ to_vc4_encoder(struct drm_encoder *encoder) return container_of(encoder, struct vc4_encoder, base); } +struct vc4_bridge { + struct drm_bridge base; + struct drm_crtc *crtc; + + void (*pre_crtc_configure)(struct drm_bridge *bridge, struct drm_atomic_state *state); + void (*pre_crtc_enable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + void (*post_crtc_enable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + + void (*post_crtc_disable)(struct drm_bridge *bridge, struct drm_atomic_state *state); + void (*post_crtc_powerdown)(struct drm_bridge *bridge, struct drm_atomic_state *state); +}; + +static inline struct vc4_bridge * +to_vc4_bridge(struct drm_bridge *bridge) +{ + return container_of(bridge, struct vc4_bridge, base); +} + struct vc4_crtc_data { /* Bitmask of channels (FIFOs) of the HVS that the output can source from */ unsigned int hvs_available_channels; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 2816a1d3ee2451..e752b89b2cad9a 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -402,6 +403,15 @@ static void hdmi_codec_eld_chmap(struct vc4_hdmi *vc4_hdmi) #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) +static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi, + const struct drm_display_mode *mode) +{ + struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder; + + return !vc4_encoder->hdmi_monitor || + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL; +} + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; @@ -666,10 +676,10 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, BIT(packet_id)), 100); } -static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, +static void vc4_hdmi_write_infoframe(struct vc4_hdmi *vc4_hdmi, union hdmi_infoframe *frame) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; u32 packet_id = frame->any.type - 0x80; const struct vc4_hdmi_register *ram_packet_start = &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START]; @@ -726,12 +736,15 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); } -static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +static void vc4_hdmi_set_avi_infoframe(struct vc4_hdmi *vc4_hdmi, + struct drm_atomic_state *state) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; struct drm_connector *connector = &vc4_hdmi->connector; - struct drm_connector_state *cstate = connector->state; + struct drm_connector_state *cstate = + drm_atomic_get_new_connector_state(state, connector); + struct drm_bridge_state *bstate = + drm_atomic_get_new_bridge_state(state, &vc4_hdmi->bridge.base); struct drm_crtc *crtc = encoder->crtc; const struct drm_display_mode *mode = &crtc->state->adjusted_mode; union hdmi_infoframe frame; @@ -746,16 +759,17 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) drm_hdmi_avi_infoframe_quant_range(&frame.avi, connector, mode, - vc4_encoder->limited_rgb_range ? - HDMI_QUANTIZATION_RANGE_LIMITED : - HDMI_QUANTIZATION_RANGE_FULL); + vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode) ? + HDMI_QUANTIZATION_RANGE_FULL : + HDMI_QUANTIZATION_RANGE_LIMITED); drm_hdmi_avi_infoframe_colorspace(&frame.avi, cstate); drm_hdmi_avi_infoframe_bars(&frame.avi, cstate); + drm_hdmi_avi_infoframe_output_colorspace(&frame.avi, &bstate->output_bus_cfg); - vc4_hdmi_write_infoframe(encoder, &frame); + vc4_hdmi_write_infoframe(vc4_hdmi, &frame); } -static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +static void vc4_hdmi_set_spd_infoframe(struct vc4_hdmi *vc4_hdmi) { union hdmi_infoframe frame; int ret; @@ -768,12 +782,11 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) frame.spd.sdi = HDMI_SPD_SDI_PC; - vc4_hdmi_write_infoframe(encoder, &frame); + vc4_hdmi_write_infoframe(vc4_hdmi, &frame); } -static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) +static void vc4_hdmi_set_audio_infoframe(struct vc4_hdmi *vc4_hdmi) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); union hdmi_infoframe frame; int ret; @@ -787,12 +800,11 @@ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) /* Select a channel allocation that matches with ELD and pcm channels */ frame.audio.channel_allocation = vc4_hdmi->audio.chmap_idx; - vc4_hdmi_write_infoframe(encoder, &frame); + vc4_hdmi_write_infoframe(vc4_hdmi, &frame); } -static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder) +static void vc4_hdmi_set_hdr_infoframe(struct vc4_hdmi *vc4_hdmi) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector_state *conn_state = connector->state; union hdmi_infoframe frame; @@ -806,29 +818,27 @@ static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder) if (drm_hdmi_infoframe_set_hdr_metadata(&frame.drm, conn_state)) return; - vc4_hdmi_write_infoframe(encoder, &frame); + vc4_hdmi_write_infoframe(vc4_hdmi, &frame); } -static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) +static void vc4_hdmi_set_infoframes(struct vc4_hdmi *vc4_hdmi, struct drm_atomic_state *state) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - - vc4_hdmi_set_avi_infoframe(encoder); - vc4_hdmi_set_spd_infoframe(encoder); + vc4_hdmi_set_avi_infoframe(vc4_hdmi, state); + vc4_hdmi_set_spd_infoframe(vc4_hdmi); /* * If audio was streaming, then we need to reenabled the audio * infoframe here during encoder_enable. */ if (vc4_hdmi->audio.streaming) - vc4_hdmi_set_audio_infoframe(encoder); + vc4_hdmi_set_audio_infoframe(vc4_hdmi); - vc4_hdmi_set_hdr_infoframe(encoder); + vc4_hdmi_set_hdr_infoframe(vc4_hdmi); } -static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_post_crtc_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); @@ -839,10 +849,10 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX); } -static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_post_crtc_powerdown(struct drm_bridge *bridge, + struct drm_atomic_state *state) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); int ret; if (vc4_hdmi->variant->phy_disable) @@ -860,18 +870,16 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, DRM_ERROR("Failed to release power domain: %d\n", ret); } -static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) -{ -} - -static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) +static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, + struct drm_atomic_state *state, + const struct drm_display_mode *mode) { u32 csc_ctl; csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, VC4_HD_CSC_CTL_ORDER); - if (enable) { + if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) { /* CEA VICs other than #1 requre limited range RGB * output unless overridden by an AVI infoframe. * Apply a colorspace conversion to squash 0-255 down @@ -899,42 +907,122 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); } -static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable) -{ - u32 csc_ctl; - csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */ +/* + * If we need to output Full Range RGB, then use the unity matrix + * + * [ 1 0 0 0] + * [ 0 1 0 0] + * [ 0 0 1 0] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_unity[3][4] = { + { 0x2000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x2000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x2000, 0x0000 }, +}; - if (enable) { - /* CEA VICs other than #1 requre limited range RGB - * output unless overridden by an AVI infoframe. - * Apply a colorspace conversion to squash 0-255 down - * to 16-235. The matrix here is: - * - * [ 0.8594 0 0 16] - * [ 0 0.8594 0 16] - * [ 0 0 0.8594 16] - * [ 0 0 0 1] - * Matrix is signed 2p13 fixed point, with signed 9p6 offsets - */ - HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x1b80); - HDMI_WRITE(HDMI_CSC_14_13, (0x0400 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_22_21, (0x1b80 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_24_23, (0x0400 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_34_33, (0x0400 << 16) | 0x1b80); - } else { - /* Still use the matrix for full range, but make it unity. - * Matrix is signed 2p13 fixed point, with signed 9p6 offsets - */ - HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x2000); - HDMI_WRITE(HDMI_CSC_14_13, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_22_21, (0x2000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_24_23, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000); - HDMI_WRITE(HDMI_CSC_34_33, (0x0000 << 16) | 0x2000); +/* + * CEA VICs other than #1 require limited range RGB output unless + * overridden by an AVI infoframe. Apply a colorspace conversion to + * squash 0-255 down to 16-235. The matrix here is: + * + * [ 0.8594 0 0 16] + * [ 0 0.8594 0 16] + * [ 0 0 0.8594 16] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_limited_rgb[3][4] = { + { 0x1b80, 0x0000, 0x0000, 0x0400 }, + { 0x0000, 0x1b80, 0x0000, 0x0400 }, + { 0x0000, 0x0000, 0x1b80, 0x0400 }, +}; + +/* + * Conversion between Full Range RGB and Full Range YUV422 using the + * BT.709 Colorspace + * + * [ 0.212639 0.715169 0.072192 0 ] + * [ -0.117208 -0.394207 0.511416 128 ] + * [ 0.511416 -0.464524 -0.046891 128 ] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_full_yuv422_bt709[3][4] = { + { 0x06ce, 0x16e3, 0x024f, 0x0000 }, + { 0xfc41, 0xf364, 0x105e, 0x2000 }, + { 0x105e, 0xf124, 0xfe81, 0x2000 }, +}; + +/* + * Conversion between Full Range RGB and Full Range YUV444 using the + * BT.709 Colorspace + * + * [ -0.117208 -0.394207 0.511416 128 ] + * [ 0.511416 -0.464524 -0.046891 128 ] + * [ 0.212639 0.715169 0.072192 0 ] + * + * Matrix is signed 2p13 fixed point, with signed 9p6 offsets + */ +static const u16 vc5_hdmi_csc_full_rgb_to_full_yuv444_bt709[3][4] = { + { 0xfc41, 0xf364, 0x105e, 0x2000 }, + { 0x105e, 0xf124, 0xfe81, 0x2000 }, + { 0x06ce, 0x16e3, 0x024f, 0x0000 }, +}; + +static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi, + const u16 coeffs[3][4]) +{ + HDMI_WRITE(HDMI_CSC_12_11, (coeffs[0][1] << 16) | coeffs[0][0]); + HDMI_WRITE(HDMI_CSC_14_13, (coeffs[0][3] << 16) | coeffs[0][2]); + HDMI_WRITE(HDMI_CSC_22_21, (coeffs[1][1] << 16) | coeffs[1][0]); + HDMI_WRITE(HDMI_CSC_24_23, (coeffs[1][3] << 16) | coeffs[1][2]); + HDMI_WRITE(HDMI_CSC_32_31, (coeffs[2][1] << 16) | coeffs[2][0]); + HDMI_WRITE(HDMI_CSC_34_33, (coeffs[2][3] << 16) | coeffs[2][2]); +} + +static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, + struct drm_atomic_state *state, + const struct drm_display_mode *mode) +{ + struct drm_bridge *bridge = &vc4_hdmi->bridge.base; + struct drm_bridge_state *bridge_state = + drm_atomic_get_new_bridge_state(state, bridge); + u32 if_cfg = 0; + u32 if_xbar = 0x543210; + u32 csc_chan_ctl = 0; + u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, + VC5_MT_CP_CSC_CTL_MODE); + + if (drm_hdmi_bus_fmt_is_yuv422(bridge_state->output_bus_cfg.format)) { + csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD, + VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) | + VC5_MT_CP_CSC_CTL_USE_444_TO_422 | + VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION; + + csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE, + VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP); + + if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY, + VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422); + + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_full_yuv422_bt709); + } else if (drm_hdmi_bus_fmt_is_yuv444(bridge_state->output_bus_cfg.format)) { + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_full_yuv444_bt709); + } else if (drm_hdmi_bus_fmt_is_rgb(bridge_state->output_bus_cfg.format)) { + if_xbar = 0x354021; + + if (vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode)) + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity); + else + vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb); } + HDMI_WRITE(HDMI_VEC_INTERFACE_CFG, if_cfg); + HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, if_xbar); + HDMI_WRITE(HDMI_CSC_CHANNEL_CTL, csc_chan_ctl); HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); } @@ -1009,7 +1097,6 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, bool gcp_en; u32 reg; - HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021); HDMI_WRITE(HDMI_HORZA, (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) | (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) | @@ -1109,9 +1196,10 @@ vc4_hdmi_encoder_get_connector_state(struct drm_encoder *encoder, return NULL; } -static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_pre_crtc_configure(struct drm_bridge *bridge, + struct drm_atomic_state *state) { + struct drm_encoder *encoder = bridge->encoder; struct drm_connector_state *conn_state = vc4_hdmi_encoder_get_connector_state(encoder, state); struct vc4_hdmi_connector_state *vc4_conn_state = @@ -1205,32 +1293,23 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode); } -static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_pre_crtc_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { + struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; - struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - if (vc4_encoder->hdmi_monitor && - drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) { - if (vc4_hdmi->variant->csc_setup) - vc4_hdmi->variant->csc_setup(vc4_hdmi, true); - - vc4_encoder->limited_rgb_range = true; - } else { - if (vc4_hdmi->variant->csc_setup) - vc4_hdmi->variant->csc_setup(vc4_hdmi, false); - - vc4_encoder->limited_rgb_range = false; - } + if (vc4_hdmi->variant->csc_setup) + vc4_hdmi->variant->csc_setup(vc4_hdmi, state, mode); HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); } -static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void vc4_hdmi_bridge_post_crtc_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { + struct drm_encoder *encoder = bridge->encoder; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); @@ -1281,34 +1360,25 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, VC4_HDMI_RAM_PACKET_ENABLE); - vc4_hdmi_set_infoframes(encoder); + vc4_hdmi_set_infoframes(vc4_hdmi, state); } vc4_hdmi_recenter_fifo(vc4_hdmi); } -static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) -{ -} - #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL -static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static unsigned long vc4_hdmi_calc_pixel_rate(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { - struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &crtc_state->adjusted_mode; - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); unsigned long long pixel_rate = mode->clock * 1000; + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); unsigned long long tmds_rate; - if (vc4_hdmi->variant->unsupported_odd_h_timings && - ((mode->hdisplay % 2) || (mode->hsync_start % 2) || - (mode->hsync_end % 2) || (mode->htotal % 2))) - return -EINVAL; - /* * The 1440p@60 pixel rate is in the same range than the first * WiFi channel (between 2.4GHz and 2.422GHz with 22MHz @@ -1334,6 +1404,61 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, if (mode->flags & DRM_MODE_FLAG_DBLCLK) pixel_rate = pixel_rate * 2; + return pixel_rate; +} + +static u32 *vc4_hdmi_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 vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); + unsigned long long pixel_rate = vc4_hdmi_calc_pixel_rate(bridge, + bridge_state, + crtc_state, + conn_state); + + /* + * If our pixel rate is too fast, force YUV422 and hope it works + */ + if (pixel_rate > vc4_hdmi->variant->max_pixel_clock) { + u32 *output_fmts; + + output_fmts = kzalloc(sizeof(*output_fmts), GFP_KERNEL); + if (!output_fmts) + return NULL; + + *output_fmts = MEDIA_BUS_FMT_UYVY8_1X16; + *num_output_fmts = 1; + + return output_fmts; + } + + return drm_atomic_helper_bridge_hdmi_get_output_bus_fmts(bridge, + bridge_state, + crtc_state, + conn_state, + num_output_fmts); +} + +static int vc4_hdmi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); + struct vc4_hdmi_connector_state *vc4_state = + conn_state_to_vc4_hdmi_conn_state(conn_state); + struct drm_display_mode *mode = &crtc_state->adjusted_mode; + unsigned long long pixel_rate; + + if (vc4_hdmi->variant->unsupported_odd_h_timings && + ((mode->hdisplay % 2) || (mode->hsync_start % 2) || + (mode->hsync_end % 2) || (mode->htotal % 2))) + return -EINVAL; + + pixel_rate = vc4_hdmi_calc_pixel_rate(bridge, bridge_state, crtc_state, conn_state); if (pixel_rate > vc4_hdmi->variant->max_pixel_clock) return -EINVAL; @@ -1343,10 +1468,11 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, } static enum drm_mode_status -vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, - const struct drm_display_mode *mode) +vc4_hdmi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { - struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); if (vc4_hdmi->variant->unsupported_odd_h_timings && ((mode->hdisplay % 2) || (mode->hsync_start % 2) || @@ -1359,13 +1485,50 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, return MODE_OK; } -static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { - .atomic_check = vc4_hdmi_encoder_atomic_check, - .mode_valid = vc4_hdmi_encoder_mode_valid, - .disable = vc4_hdmi_encoder_disable, - .enable = vc4_hdmi_encoder_enable, +static int vc4_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct vc4_hdmi *vc4_hdmi = bridge_to_vc4_hdmi(bridge); + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + return vc4_hdmi_connector_init(bridge->dev, vc4_hdmi); +} + +static const struct drm_bridge_funcs vc4_hdmi_bridge_funcs = { + .attach = vc4_hdmi_bridge_attach, + .atomic_check = vc4_hdmi_bridge_atomic_check, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_get_output_bus_fmts = vc4_hdmi_bridge_atomic_get_output_bus_fmts, + .atomic_reset = drm_atomic_helper_bridge_reset, + .mode_valid = vc4_hdmi_bridge_mode_valid, }; +static int vc4_hdmi_bridge_init(struct drm_device *drm, + struct vc4_hdmi *vc4_hdmi) +{ + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; + struct drm_bridge *bridge = &vc4_hdmi->bridge.base; + struct device *dev = &vc4_hdmi->pdev->dev; + int ret; + + bridge->funcs = &vc4_hdmi_bridge_funcs; + bridge->of_node = dev->of_node; + bridge->type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(bridge); + + ret = drm_bridge_attach(encoder, bridge, NULL, 0); + if (ret) { + drm_bridge_remove(bridge); + return ret; + } + + return 0; +} + static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) { int i; @@ -1551,7 +1714,6 @@ static int vc4_hdmi_audio_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); - struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; struct device *dev = &vc4_hdmi->pdev->dev; u32 audio_packet_config, channel_mask; u32 channel_map; @@ -1628,7 +1790,7 @@ static int vc4_hdmi_audio_prepare(struct snd_pcm_substream *substream, vc4_hdmi->audio.chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; } - vc4_hdmi_set_audio_infoframe(encoder); + vc4_hdmi_set_audio_infoframe(vc4_hdmi); return 0; } @@ -2573,14 +2735,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, vc4_hdmi); encoder = &vc4_hdmi->encoder.base.base; vc4_hdmi->encoder.base.type = variant->encoder_type; - vc4_hdmi->encoder.base.pre_crtc_configure = vc4_hdmi_encoder_pre_crtc_configure; - vc4_hdmi->encoder.base.pre_crtc_enable = vc4_hdmi_encoder_pre_crtc_enable; - vc4_hdmi->encoder.base.post_crtc_enable = vc4_hdmi_encoder_post_crtc_enable; - vc4_hdmi->encoder.base.post_crtc_disable = vc4_hdmi_encoder_post_crtc_disable; - vc4_hdmi->encoder.base.post_crtc_powerdown = vc4_hdmi_encoder_post_crtc_powerdown; vc4_hdmi->pdev = pdev; vc4_hdmi->variant = variant; + vc4_hdmi->bridge.pre_crtc_configure = vc4_hdmi_bridge_pre_crtc_configure; + vc4_hdmi->bridge.pre_crtc_enable = vc4_hdmi_bridge_pre_crtc_enable; + vc4_hdmi->bridge.post_crtc_enable = vc4_hdmi_bridge_post_crtc_enable; + vc4_hdmi->bridge.post_crtc_disable = vc4_hdmi_bridge_post_crtc_disable; + vc4_hdmi->bridge.post_crtc_powerdown = vc4_hdmi_bridge_post_crtc_powerdown; + ret = variant->init_resources(vc4_hdmi); if (ret) return ret; @@ -2621,12 +2784,19 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (vc4_hdmi->variant->reset) vc4_hdmi->variant->reset(vc4_hdmi); + if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") || + of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) && + HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) { + clk_prepare_enable(vc4_hdmi->pixel_clock); + clk_prepare_enable(vc4_hdmi->hsm_clock); + clk_prepare_enable(vc4_hdmi->pixel_bvb_clock); + } + pm_runtime_enable(dev); drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs); - ret = vc4_hdmi_connector_init(drm, vc4_hdmi); + ret = vc4_hdmi_bridge_init(drm, vc4_hdmi); if (ret) goto err_destroy_encoder; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 33b5f120bbebe2..f2b96c747dd47d 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -1,6 +1,7 @@ #ifndef _VC4_HDMI_H_ #define _VC4_HDMI_H_ +#include #include #include #include @@ -12,7 +13,6 @@ struct vc4_hdmi_encoder { struct vc4_encoder base; bool hdmi_monitor; - bool limited_rgb_range; }; static inline struct vc4_hdmi_encoder * @@ -77,7 +77,9 @@ struct vc4_hdmi_variant { void (*reset)(struct vc4_hdmi *vc4_hdmi); /* Callback to enable / disable the CSC */ - void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable); + void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, + struct drm_atomic_state *state, + const struct drm_display_mode *mode); /* Callback to configure the video timings in the HDMI block */ void (*set_timings)(struct vc4_hdmi *vc4_hdmi, @@ -133,6 +135,7 @@ struct vc4_hdmi { struct vc4_hdmi_encoder encoder; struct drm_connector connector; + struct vc4_bridge bridge; struct i2c_adapter *ddc; void __iomem *hdmicore_regs; @@ -187,6 +190,12 @@ struct vc4_hdmi { struct debugfs_regset32 rm_regset; }; +static inline struct vc4_hdmi * +bridge_to_vc4_hdmi(struct drm_bridge *bridge) +{ + return container_of(bridge, struct vc4_hdmi, bridge.base); +} + static inline struct vc4_hdmi * connector_to_vc4_hdmi(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index 20a1438a72cb73..3c7ed795673217 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -52,6 +52,7 @@ enum vc4_hdmi_field { HDMI_CSC_24_23, HDMI_CSC_32_31, HDMI_CSC_34_33, + HDMI_CSC_CHANNEL_CTL, HDMI_CSC_CTL, /* @@ -116,6 +117,7 @@ enum vc4_hdmi_field { HDMI_TX_PHY_POWERDOWN_CTL, HDMI_TX_PHY_RESET_CTL, HDMI_TX_PHY_TMDS_CLK_WORD_SEL, + HDMI_VEC_INTERFACE_CFG, HDMI_VEC_INTERFACE_XBAR, HDMI_VERTA0, HDMI_VERTA1, @@ -240,6 +242,7 @@ static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = { VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), + VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), @@ -285,6 +288,7 @@ static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = { VC5_CSC_REG(HDMI_CSC_24_23, 0x010), VC5_CSC_REG(HDMI_CSC_32_31, 0x014), VC5_CSC_REG(HDMI_CSC_34_33, 0x018), + VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c), }; static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = { @@ -319,6 +323,7 @@ static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = { VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), + VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000), @@ -364,6 +369,7 @@ static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = { VC5_CSC_REG(HDMI_CSC_24_23, 0x010), VC5_CSC_REG(HDMI_CSC_32_31, 0x014), VC5_CSC_REG(HDMI_CSC_34_33, 0x018), + VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c), }; static inline diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 78fd28599aebcf..c055631d5c686c 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -774,8 +774,27 @@ enum { # define VC4_HD_CSC_CTL_RGB2YCC BIT(1) # define VC4_HD_CSC_CTL_ENABLE BIT(0) +# define VC5_MT_CP_CSC_CTL_USE_444_TO_422 BIT(6) +# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_MASK \ + VC4_MASK(5, 4) +# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD \ + 3 +# define VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION BIT(3) +# define VC5_MT_CP_CSC_CTL_ENABLE BIT(2) +# define VC5_MT_CP_CSC_CTL_MODE_MASK VC4_MASK(1, 0) + +# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_MASK \ + VC4_MASK(7, 6) +# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE \ + 2 + # define VC4_DVP_HT_CLOCK_STOP_PIXEL BIT(1) +# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_MASK \ + VC4_MASK(3, 2) +# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY \ + 2 + /* HVS display list information. */ #define HVS_BOOTLOADER_DLIST_END 32 diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 4a26750b5e933c..22430640786fa0 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -506,7 +506,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) return ret; encoder = &txp->connector.encoder; - encoder->possible_crtcs |= drm_crtc_mask(crtc); + encoder->possible_crtcs = drm_crtc_mask(crtc); ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, dev_name(dev), txp); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 2195daa289d270..1d801d77e90a48 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -868,6 +868,12 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, u32 output_fmt, unsigned int *num_input_fmts); +u32 *drm_atomic_helper_bridge_hdmi_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); + enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge); int drm_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector); diff --git a/include/drm/drm_hdmi.h b/include/drm/drm_hdmi.h new file mode 100644 index 00000000000000..8cd281699ea081 --- /dev/null +++ b/include/drm/drm_hdmi.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2013-2015 Mentor Graphics Inc. + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. + * Copyright (C) 2010, Guennadi Liakhovetski + */ + +#ifndef __DRM_HDMI_H_ +#define __DRM_HDMI_H_ + +#include + +struct drm_bus_cfg; +struct hdmi_avi_infoframe; + +bool drm_hdmi_bus_fmt_is_rgb(u32 bus_format); +bool drm_hdmi_bus_fmt_is_yuv444(u32 bus_format); +bool drm_hdmi_bus_fmt_is_yuv422(u32 bus_format); +bool drm_hdmi_bus_fmt_is_yuv420(u32 bus_format); +int drm_hdmi_bus_fmt_color_depth(u32 bus_format); +int drm_hdmi_avi_infoframe_output_colorspace(struct hdmi_avi_infoframe *frame, + struct drm_bus_cfg *out_bus_cfg); + +#endif // __DRM_HDMI_H_