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
