Refactor the Innosilicon HDMI bridge driver into a library
to support being called by MFD (Multi-Function Device) drivers.
This is necessary for platforms like the StarFive JH7110, where
the HDMI controller and PHY are part of a monolithic MFD block.

This patch makes the following changes:
- The core probing logic is moved into a new exported function,
  inno_hdmi_probe().
- A corresponding exported inno_hdmi_remove() is added.
- The existing inno_hdmi_bind() function is updated to use the
  new inno_hdmi_probe() helper.
- The driver now supports retrieving a shared regmap from a parent
  device, falling back to ioremap if one is not found.
- The struct inno_hdmi definition is moved to a public header
  (include/drm/bridge/inno_hdmi.h) to be accessible by other
  drivers.

Signed-off-by: Michal Wilczynski <[email protected]>
---
 drivers/gpu/drm/bridge/inno-hdmi.c | 99 +++++++++++++++++++++++++++-----------
 include/drm/bridge/inno_hdmi.h     | 25 +++++++++-
 2 files changed, 96 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c 
b/drivers/gpu/drm/bridge/inno-hdmi.c
index 
e46ee4d85044f18407aaa624b4e3dd1a6c5af5cb..9a2370ed2f208caf3dafb4a4d8884516d489263c
 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -395,12 +395,6 @@ enum inno_hdmi_dev_type {
        RK3128_HDMI,
 };
 
-struct inno_hdmi_phy_config {
-       unsigned long pixelclock;
-       u8 pre_emphasis;
-       u8 voltage_level_control;
-};
-
 struct inno_hdmi_variant {
        enum inno_hdmi_dev_type dev_type;
        struct inno_hdmi_phy_config *phy_configs;
@@ -417,19 +411,6 @@ struct inno_hdmi_i2c {
        struct completion cmp;
 };
 
-struct inno_hdmi {
-       struct device *dev;
-       struct drm_bridge bridge;
-       struct clk *pclk;
-       struct clk *refclk;
-       void __iomem *regs;
-       struct regmap *grf;
-
-       struct inno_hdmi_i2c *i2c;
-       struct i2c_adapter *ddc;
-       const struct inno_hdmi_plat_data *plat_data;
-};
-
 enum {
        CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
        CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
@@ -496,11 +477,23 @@ static int inno_hdmi_find_phy_config(struct inno_hdmi 
*hdmi,
 
 static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
 {
+       u32 val;
+
+       if (hdmi->regmap) {
+               regmap_read(hdmi->regmap, offset * 4, &val);
+               return val;
+       }
+
        return readl_relaxed(hdmi->regs + (offset) * 0x04);
 }
 
 static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
 {
+       if (hdmi->regmap) {
+               regmap_write(hdmi->regmap, offset * 4, val);
+               return;
+       }
+
        writel_relaxed(val, hdmi->regs + (offset) * 0x04);
 }
 
@@ -1082,11 +1075,24 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct 
inno_hdmi *hdmi)
        return adap;
 }
 
-struct inno_hdmi *inno_hdmi_bind(struct device *dev,
-                                struct drm_encoder *encoder,
-                                const struct inno_hdmi_plat_data *plat_data)
+/**
+ * inno_hdmi_probe - Internal helper to perform common setup
+ * @pdev: platform device
+ * @plat_data: SoC-specific platform data
+ *
+ * This function handles all the common hardware setup: allocating the main
+ * struct, mapping registers, getting clocks, initializing the hardware,
+ * setting up the IRQ, and initializing the DDC adapter and bridge struct.
+ * It returns a pointer to the inno_hdmi struct on success, or an ERR_PTR
+ * on failure.
+ *
+ * This function is used by modern, decoupled MFD/glue drivers. It registers
+ * the bridge but does not attach it.
+ */
+struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
+                                        const struct inno_hdmi_plat_data 
*plat_data)
 {
-       struct platform_device *pdev = to_platform_device(dev);
+       struct device *dev = &pdev->dev;
        struct inno_hdmi *hdmi;
        int irq;
        int ret;
@@ -1103,9 +1109,21 @@ struct inno_hdmi *inno_hdmi_bind(struct device *dev,
        hdmi->dev = dev;
        hdmi->plat_data = plat_data;
 
-       hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(hdmi->regs))
-               return ERR_CAST(hdmi->regs);
+       /*
+        * MFD Support: Check if parent provides a regmap.
+        * If so, use it. Otherwise, fall back to ioremap.
+        */
+       if (dev->parent)
+               hdmi->regmap = dev_get_regmap(dev->parent, NULL);
+
+       if (hdmi->regmap) {
+               dev_info(dev, "Using MFD regmap for registers\n");
+       } else {
+               dev_info(dev, "Falling back to ioremap for registers\n");
+               hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
+               if (IS_ERR(hdmi->regs))
+                       return ERR_CAST(hdmi->regs);
+       }
 
        hdmi->pclk = devm_clk_get_enabled(hdmi->dev, "pclk");
        if (IS_ERR(hdmi->pclk)) {
@@ -1149,7 +1167,34 @@ struct inno_hdmi *inno_hdmi_bind(struct device *dev,
        if (ret)
                return ERR_PTR(ret);
 
-       ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+       return hdmi;
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_probe);
+
+/**
+ * inno_hdmi_remove - Remove a bridge created by inno_hdmi_probe
+ * @hdmi: The inno_hdmi instance to remove
+ */
+void inno_hdmi_remove(struct inno_hdmi *hdmi)
+{
+       drm_bridge_remove(&hdmi->bridge);
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_remove);
+
+struct inno_hdmi *inno_hdmi_bind(struct device *dev,
+                                struct drm_encoder *encoder,
+                                const struct inno_hdmi_plat_data *plat_data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct inno_hdmi *hdmi;
+       int ret;
+
+       hdmi = inno_hdmi_probe(pdev, plat_data);
+       if (IS_ERR(hdmi))
+               return hdmi;
+
+       ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL,
+                               DRM_BRIDGE_ATTACH_NO_CONNECTOR);
        if (ret)
                return ERR_PTR(ret);
 
diff --git a/include/drm/bridge/inno_hdmi.h b/include/drm/bridge/inno_hdmi.h
index 
8b39655212e247d9ca7b1f220f970df1fb6afe13..019680622324197e046a1c606ec25aabe95537b4
 100644
--- a/include/drm/bridge/inno_hdmi.h
+++ b/include/drm/bridge/inno_hdmi.h
@@ -6,10 +6,13 @@
 #ifndef __INNO_HDMI__
 #define __INNO_HDMI__
 
+#include <drm/drm_bridge.h>
+
 struct device;
 struct drm_encoder;
 struct drm_display_mode;
-struct inno_hdmi;
+struct i2c_adapter;
+struct inno_hdmi_i2c;
 
 struct inno_hdmi_plat_ops {
        void (*enable)(struct device *pdev, struct drm_display_mode *mode);
@@ -27,7 +30,27 @@ struct inno_hdmi_plat_data {
        struct inno_hdmi_phy_config *default_phy_config;
 };
 
+struct inno_hdmi {
+       struct device *dev;
+       struct drm_bridge bridge;
+       struct clk *pclk;
+       struct clk *refclk;
+       void __iomem *regs;
+       struct regmap *regmap;
+       struct regmap *grf;
+
+       struct i2c_adapter *ddc;
+       struct inno_hdmi_i2c *i2c;
+       const struct inno_hdmi_plat_data *plat_data;
+};
+
 struct inno_hdmi *inno_hdmi_bind(struct device *pdev,
                                 struct drm_encoder *encoder,
                                 const struct inno_hdmi_plat_data *plat_data);
+
+struct inno_hdmi *inno_hdmi_probe(struct platform_device *pdev,
+                                const struct inno_hdmi_plat_data *plat_data);
+
+void inno_hdmi_remove(struct inno_hdmi *hdmi);
+
 #endif /* __INNO_HDMI__ */

-- 
2.34.1

Reply via email to