inno_hdmi_bridge_mode_valid() validates the pixel clock against
hdmi->refclk, but that clock is optional and is only present on
platforms that describe a "ref" clock. Platforms where the pixel clock
is produced by a separate device - such as the StarFive JH7110, whose
PHY is both the clock and the PHY provider - have no "ref" clock, so
the check is skipped entirely and every mode is reported as valid.

A mode the platform cannot generate is then advertised to userspace.
The subsequent modeset appears to succeed, since the atomic enable path
cannot fail, and the display silently stays blank.

Add a .mode_valid platform operation so platforms can reject modes they
are unable to drive. Platforms that do not implement it are unaffected.

Signed-off-by: Michal Wilczynski <[email protected]>
---
 drivers/gpu/drm/bridge/inno-hdmi.c | 9 +++++++++
 include/drm/bridge/inno_hdmi.h     | 5 ++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c 
b/drivers/gpu/drm/bridge/inno-hdmi.c
index 
952cc55c10f7dc07de6ebe943aae3e54df2edc8d..6da1ef923c99676a2133b4c0e3ba5744195a335c
 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -821,6 +821,7 @@ static enum drm_mode_status 
inno_hdmi_bridge_mode_valid(struct drm_bridge *bridg
                                                        const struct 
drm_display_mode *mode)
 {
        struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+       const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops;
        unsigned long mpixelclk, max_tolerance;
        long rounded_refclk;
 
@@ -836,6 +837,14 @@ static enum drm_mode_status 
inno_hdmi_bridge_mode_valid(struct drm_bridge *bridg
        if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0)
                return MODE_CLOCK_HIGH;
 
+       if (plat_ops && plat_ops->mode_valid) {
+               enum drm_mode_status status;
+
+               status = plat_ops->mode_valid(hdmi->dev, mode);
+               if (status != MODE_OK)
+                       return status;
+       }
+
        if (hdmi->refclk) {
                rounded_refclk = clk_round_rate(hdmi->refclk, mpixelclk);
                if (rounded_refclk < 0)
diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h
index 
5fca651c56b056d814124aa9c7e91aa7fd7ed1cd..1e9974401c39e3773bb339eaec0b45ff1dd9c975
 100644
--- a/include/drm/bridge/inno_hdmi.h
+++ b/include/drm/bridge/inno_hdmi.h
@@ -8,15 +8,18 @@
 
 #include <linux/types.h>
 
+#include <drm/drm_modes.h>
+
 struct device;
 struct drm_encoder;
-struct drm_display_mode;
 struct inno_hdmi;
 struct platform_device;
 
 struct inno_hdmi_plat_ops {
        void (*enable)(struct device *pdev, struct drm_display_mode *mode);
        void (*disable)(struct device *pdev);
+       enum drm_mode_status (*mode_valid)(struct device *pdev,
+                                          const struct drm_display_mode *mode);
 };
 
 struct inno_hdmi_phy_config {

-- 
2.34.1

Reply via email to