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.

Signed-off-by: Nicolas Frattaroli <[email protected]>
---
 drivers/gpu/drm/drm_atomic_helper.c | 81 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_atomic_helper.h     |  7 ++++
 2 files changed, 88 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c 
b/drivers/gpu/drm/drm_atomic_helper.c
index b7753454b777..e8613e6df1f4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -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>
@@ -4095,3 +4096,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);
+
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index e154ee4f0696..7256eaca109b 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -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_ */

-- 
2.53.0

Reply via email to