drm/atomic-helper: Add HDMI bridge output bus formats helper

The drm_bridge_funcs atomic_get_output_bus_fmts operation should be the
same for likely every HDMI connector bridge, unless such an HDMI
connector bridge has some special hardware restrictions that I cannot
envision yet.

To avoid code duplication and standardize on a set of media bus formats
that the HDMI output color formats translate to, add a common helper
function that implements this operation to the drm bridge helpers.

The function returns a list of output bus formats based on the HDMI
bridge's current output bits-per-component, and its bitmask of supported
color formats.

To guard against future expansion of DRM_OUTPUT_COLOR_FORMAT outgrowing
the hweight8 call, add a BUILD_BUG_ON statement where it's used that
checks for DRM_OUTPUT_COLOR_FORMAT_COUNT. The justification for not
using hweight32 in all cases is that not all ISAs have a popcount
instruction, and will benefit from a smaller/faster software
implementation that doesn't have to operate across all bits.

The justification for not defining an hweight_color depending on the
value of DRM_OUTPUT_COLOR_FORMAT_COUNT is that this count enum value is
only known at compile time, not at preprocessor time.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Daniel Stone <daniel@fooishbar.org>
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Link: https://patch.msgid.link/20260609-color-format-v17-7-35739b5782cc@collabora.com
Signed-off-by: Daniel Stone <daniels@collabora.com>
This commit is contained in:
Nicolas Frattaroli
2026-06-09 14:43:54 +02:00
committed by Daniel Stone
parent f5873533bf
commit 3f0f770ccc
2 changed files with 88 additions and 0 deletions

View File

@@ -28,6 +28,7 @@
#include <linux/export.h>
#include <linux/dma-fence.h>
#include <linux/ktime.h>
#include <linux/media-bus-format.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -4107,3 +4108,83 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge,
return input_fmts;
}
EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
/**
* drm_atomic_helper_bridge_get_hdmi_output_bus_fmts - helper implementing
* atomic_get_output_bus_fmts for HDMI
* @bridge: pointer to &struct drm_bridge
* @bridge_state: pointer to the current bridge state
* @crtc_state: pointer to the current CRTC state
* @conn_state: pointer to the current connector state
* @num_output_fmts: pointer to where the number of entries in the returned array
* will be stored. Set to 0 if unsuccessful.
*
* Common implementation for the &drm_bridge_funcs.atomic_get_output_bus_fmts
* operation that's applicable to HDMI connectors.
*
* Returns: a newly allocated array of u32 values of length \*@num_output_fmts,
* representing all the MEDIA_BUS_FMTS\_ for the current connector state's
* chosen HDMI output bits per compoennt, or %NULL if it fails to allocate one.
*/
u32 *
drm_atomic_helper_bridge_get_hdmi_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)
{
unsigned int num_fmts = 0;
u32 *out_fmts;
/*
* bridge->supported_formats is a bit field of BIT(enum drm_output_color_format)
* values. The smallest hweight that is smaller than or equal to
* %DRM_OUTPUT_COLOR_FORMAT_COUNT will do for counting set bits here.
*/
BUILD_BUG_ON(const_true(DRM_OUTPUT_COLOR_FORMAT_COUNT > 8));
out_fmts = kmalloc_array(hweight8(bridge->supported_formats),
sizeof(u32), GFP_KERNEL);
if (!out_fmts) {
*num_output_fmts = 0;
return NULL;
}
switch (conn_state->hdmi.output_bpc) {
case 12:
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB121212_1X36;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV12_1X36;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY12_1X24;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
break;
case 10:
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB101010_1X30;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV10_1X30;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY10_1X20;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
break;
default:
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB888_1X24;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV8_1X24;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY8_1X16;
if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))
out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
break;
}
*num_output_fmts = num_fmts;
return out_fmts;
}
EXPORT_SYMBOL(drm_atomic_helper_bridge_get_hdmi_output_bus_fmts);

View File

@@ -295,4 +295,11 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge,
u32 output_fmt,
unsigned int *num_input_fmts);
u32 *
drm_atomic_helper_bridge_get_hdmi_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);
#endif /* DRM_ATOMIC_HELPER_H_ */