On 08/08/2025, Frank Li wrote: > On Fri, Aug 08, 2025 at 04:06:15PM +0800, Shengjiu Wang wrote: >> The HDMI TX Parallel Audio Interface (HTX_PAI) is a digital module that >> acts as the bridge between the Audio Subsystem to the HDMI TX Controller. >> This IP block is found in the HDMI subsystem of the i.MX8MP SoC. >> >> Data received from the audio subsystem can have an arbitrary component >> ordering. The HTX_PAI block has integrated muxing options to select which >> sections of the 32-bit input data word will be mapped to each IEC60958 >> field. The HTX_PAI_FIELD_CTRL register contains mux selects to >> individually select P,C,U,V,Data, and Preamble. >> >> Use component helper so that imx8mp-hdmi-tx will be aggregate driver, >> imx8mp-hdmi-pai will be component driver, then imx8mp-hdmi-pai can use >> bind() ops to get the plat_data from imx8mp-hdmi-tx device. >> >> Signed-off-by: Shengjiu Wang <shengjiu.w...@nxp.com> >> --- >> drivers/gpu/drm/bridge/imx/Kconfig | 11 ++ >> drivers/gpu/drm/bridge/imx/Makefile | 1 + >> drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c | 158 +++++++++++++++++++ >> drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c | 63 +++++++- >> include/drm/bridge/dw_hdmi.h | 6 + >> 5 files changed, 234 insertions(+), 5 deletions(-) >> create mode 100644 drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c >> >> diff --git a/drivers/gpu/drm/bridge/imx/Kconfig >> b/drivers/gpu/drm/bridge/imx/Kconfig >> index 9a480c6abb85..b9028a5e5a06 100644 >> --- a/drivers/gpu/drm/bridge/imx/Kconfig >> +++ b/drivers/gpu/drm/bridge/imx/Kconfig >> @@ -18,12 +18,23 @@ config DRM_IMX8MP_DW_HDMI_BRIDGE >> depends on OF >> depends on COMMON_CLK >> select DRM_DW_HDMI >> + imply DRM_IMX8MP_HDMI_PAI >> imply DRM_IMX8MP_HDMI_PVI >> imply PHY_FSL_SAMSUNG_HDMI_PHY >> help >> Choose this to enable support for the internal HDMI encoder found >> on the i.MX8MP SoC. >> >> +config DRM_IMX8MP_HDMI_PAI >> + tristate "Freescale i.MX8MP HDMI PAI bridge support" >> + depends on OF >> + select DRM_DW_HDMI >> + select REGMAP >> + select REGMAP_MMIO >> + help >> + Choose this to enable support for the internal HDMI TX Parallel >> + Audio Interface found on the Freescale i.MX8MP SoC. >> + >> config DRM_IMX8MP_HDMI_PVI >> tristate "Freescale i.MX8MP HDMI PVI bridge support" >> depends on OF >> diff --git a/drivers/gpu/drm/bridge/imx/Makefile >> b/drivers/gpu/drm/bridge/imx/Makefile >> index dd5d48584806..8d01fda25451 100644 >> --- a/drivers/gpu/drm/bridge/imx/Makefile >> +++ b/drivers/gpu/drm/bridge/imx/Makefile >> @@ -1,6 +1,7 @@ >> obj-$(CONFIG_DRM_IMX_LDB_HELPER) += imx-ldb-helper.o >> obj-$(CONFIG_DRM_IMX_LEGACY_BRIDGE) += imx-legacy-bridge.o >> obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi-tx.o >> +obj-$(CONFIG_DRM_IMX8MP_HDMI_PAI) += imx8mp-hdmi-pai.o >> obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o >> obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o >> obj-$(CONFIG_DRM_IMX8QXP_LDB) += imx8qxp-ldb.o >> diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c >> b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c >> new file mode 100644 >> index 000000000000..8d13a35b206a >> --- /dev/null >> +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c >> @@ -0,0 +1,158 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> +/* >> + * Copyright 2025 NXP >> + */ >> + >> +#include <linux/bitfield.h> >> +#include <linux/component.h> >> +#include <linux/module.h> >> +#include <linux/of_platform.h> >> +#include <linux/platform_device.h> >> +#include <linux/regmap.h> >> +#include <drm/bridge/dw_hdmi.h> >> +#include <sound/asoundef.h> >> + >> +#define HTX_PAI_CTRL 0x00 >> +#define ENABLE BIT(0) >> + >> +#define HTX_PAI_CTRL_EXT 0x04 >> +#define WTMK_HIGH_MASK GENMASK(31, 24) >> +#define WTMK_LOW_MASK GENMASK(23, 16) >> +#define NUM_CH_MASK GENMASK(10, 8) >> +#define WTMK_HIGH(n) FIELD_PREP(WTMK_HIGH_MASK, (n)) >> +#define WTMK_LOW(n) FIELD_PREP(WTMK_LOW_MASK, (n)) >> +#define NUM_CH(n) FIELD_PREP(NUM_CH_MASK, (n) - 1) >> + >> +#define HTX_PAI_FIELD_CTRL 0x08 >> +#define PRE_SEL GENMASK(28, 24) >> +#define D_SEL GENMASK(23, 20) >> +#define V_SEL GENMASK(19, 15) >> +#define U_SEL GENMASK(14, 10) >> +#define C_SEL GENMASK(9, 5) >> +#define P_SEL GENMASK(4, 0) >> + >> +struct imx8mp_hdmi_pai { >> + struct regmap *regmap; >> +}; >> + >> +static void imx8mp_hdmi_pai_enable(struct dw_hdmi *dw_hdmi, int channel, >> + int width, int rate, int non_pcm, >> + int iec958) >> +{ >> + const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi); >> + struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio; >> + int val; >> + >> + /* PAI set control extended */ >> + val = WTMK_HIGH(3) | WTMK_LOW(3); >> + val |= NUM_CH(channel); >> + regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL_EXT, val); >> + >> + /* IEC60958 format */ >> + if (iec958) { >> + val = FIELD_PREP_CONST(P_SEL, >> + __bf_shf(IEC958_SUBFRAME_PARITY)); >> + val |= FIELD_PREP_CONST(C_SEL, >> + >> __bf_shf(IEC958_SUBFRAME_CHANNEL_STATUS)); >> + val |= FIELD_PREP_CONST(U_SEL, >> + __bf_shf(IEC958_SUBFRAME_USER_DATA)); >> + val |= FIELD_PREP_CONST(V_SEL, >> + __bf_shf(IEC958_SUBFRAME_VALIDITY)); >> + val |= FIELD_PREP_CONST(D_SEL, >> + >> __bf_shf(IEC958_SUBFRAME_SAMPLE_24_MASK)); >> + val |= FIELD_PREP_CONST(PRE_SEL, >> + >> __bf_shf(IEC958_SUBFRAME_PREAMBLE_MASK)); >> + } else { >> + /* >> + * The allowed PCM widths are 24bit and 32bit, as they are >> supported >> + * by aud2htx module. >> + * for 24bit, D_SEL = 0, select all the bits. >> + * for 32bit, D_SEL = 8, select 24bit in MSB. >> + */ >> + val = FIELD_PREP(D_SEL, width - 24); >> + } >> + >> + regmap_write(hdmi_pai->regmap, HTX_PAI_FIELD_CTRL, val); >> + >> + /* PAI start running */ >> + regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, ENABLE); >> +} >> + >> +static void imx8mp_hdmi_pai_disable(struct dw_hdmi *dw_hdmi) >> +{ >> + const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi); >> + struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio; >> + >> + /* Stop PAI */ >> + regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, 0); >> +} >> + >> +static const struct regmap_config imx8mp_hdmi_pai_regmap_config = { >> + .reg_bits = 32, >> + .reg_stride = 4, >> + .val_bits = 32, >> + .max_register = HTX_PAI_FIELD_CTRL, >> +}; >> + >> +static int imx8mp_hdmi_pai_bind(struct device *dev, struct device *master, >> void *data) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct dw_hdmi_plat_data *plat_data = data; >> + struct imx8mp_hdmi_pai *hdmi_pai; >> + struct resource *res; >> + void __iomem *base; >> + >> + hdmi_pai = devm_kzalloc(dev, sizeof(*hdmi_pai), GFP_KERNEL); >> + if (!hdmi_pai) >> + return -ENOMEM; > > I am not sure how bind/unbind work here. you use devm_ func here? does > system auto free it at unbind()?
Component helper supports freeing resources allocated by device managed APIs. The allocations are commonly used by bind callbacks in various drivers. > > generally, free() happen and driver's remove if use devm_ func > > Frank >> + >> + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); >> + if (IS_ERR(base)) >> + return PTR_ERR(base); >> + >> + hdmi_pai->regmap = devm_regmap_init_mmio_clk(dev, "apb", base, >> + >> &imx8mp_hdmi_pai_regmap_config); >> + if (IS_ERR(hdmi_pai->regmap)) { >> + dev_err(dev, "regmap init failed\n"); >> + return PTR_ERR(hdmi_pai->regmap); >> + } >> + >> + plat_data->enable_audio = imx8mp_hdmi_pai_enable; >> + plat_data->disable_audio = imx8mp_hdmi_pai_disable; >> + plat_data->priv_audio = hdmi_pai; >> + >> + return 0; >> +} >> + >> +static const struct component_ops imx8mp_hdmi_pai_ops = { >> + .bind = imx8mp_hdmi_pai_bind, >> +}; >> + >> +static int imx8mp_hdmi_pai_probe(struct platform_device *pdev) >> +{ >> + return component_add(&pdev->dev, &imx8mp_hdmi_pai_ops); >> +} >> + >> +static void imx8mp_hdmi_pai_remove(struct platform_device *pdev) >> +{ >> + component_del(&pdev->dev, &imx8mp_hdmi_pai_ops); >> +} >> + >> +static const struct of_device_id imx8mp_hdmi_pai_of_table[] = { >> + { .compatible = "fsl,imx8mp-hdmi-pai" }, >> + { /* Sentinel */ } >> +}; >> +MODULE_DEVICE_TABLE(of, imx8mp_hdmi_pai_of_table); >> + >> +static struct platform_driver imx8mp_hdmi_pai_platform_driver = { >> + .probe = imx8mp_hdmi_pai_probe, >> + .remove = imx8mp_hdmi_pai_remove, >> + .driver = { >> + .name = "imx8mp-hdmi-pai", >> + .of_match_table = imx8mp_hdmi_pai_of_table, >> + }, >> +}; >> +module_platform_driver(imx8mp_hdmi_pai_platform_driver); >> + >> +MODULE_DESCRIPTION("i.MX8MP HDMI PAI driver"); >> +MODULE_LICENSE("GPL"); >> diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c >> b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c >> index 1e7a789ec289..c68896392a2d 100644 >> --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c >> +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c >> @@ -5,11 +5,13 @@ >> */ >> >> #include <linux/clk.h> >> +#include <linux/component.h> >> #include <linux/mod_devicetable.h> >> #include <linux/module.h> >> #include <linux/platform_device.h> >> #include <drm/bridge/dw_hdmi.h> >> #include <drm/drm_modes.h> >> +#include <drm/drm_of.h> >> >> struct imx8mp_hdmi { >> struct dw_hdmi_plat_data plat_data; >> @@ -79,10 +81,43 @@ static const struct dw_hdmi_phy_ops imx8mp_hdmi_phy_ops >> = { >> .update_hpd = dw_hdmi_phy_update_hpd, >> }; >> >> +static int imx8mp_dw_hdmi_bind(struct device *dev) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev); >> + int ret; >> + >> + ret = component_bind_all(dev, &hdmi->plat_data); >> + if (ret) >> + return dev_err_probe(dev, ret, "component_bind_all failed!\n"); >> + >> + hdmi->dw_hdmi = dw_hdmi_probe(pdev, &hdmi->plat_data); >> + if (IS_ERR(hdmi->dw_hdmi)) >> + return PTR_ERR(hdmi->dw_hdmi); >> + >> + return 0; >> +} >> + >> +static void imx8mp_dw_hdmi_unbind(struct device *dev) >> +{ >> + struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev); >> + >> + dw_hdmi_remove(hdmi->dw_hdmi); >> + >> + component_unbind_all(dev, &hdmi->plat_data); >> +} >> + >> +static const struct component_master_ops imx8mp_dw_hdmi_ops = { >> + .bind = imx8mp_dw_hdmi_bind, >> + .unbind = imx8mp_dw_hdmi_unbind, >> +}; >> + >> static int imx8mp_dw_hdmi_probe(struct platform_device *pdev) >> { >> struct device *dev = &pdev->dev; >> struct dw_hdmi_plat_data *plat_data; >> + struct component_match *match = NULL; >> + struct device_node *remote; >> struct imx8mp_hdmi *hdmi; >> >> hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); >> @@ -102,20 +137,38 @@ static int imx8mp_dw_hdmi_probe(struct platform_device >> *pdev) >> plat_data->priv_data = hdmi; >> plat_data->phy_force_vendor = true; >> >> - hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data); >> - if (IS_ERR(hdmi->dw_hdmi)) >> - return PTR_ERR(hdmi->dw_hdmi); >> - >> platform_set_drvdata(pdev, hdmi); >> >> + /* port@2 is for hdmi_pai device */ >> + remote = of_graph_get_remote_node(pdev->dev.of_node, 2, 0); >> + if (!remote) { >> + hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data); >> + if (IS_ERR(hdmi->dw_hdmi)) >> + return PTR_ERR(hdmi->dw_hdmi); >> + } else { >> + drm_of_component_match_add(dev, &match, component_compare_of, >> remote); >> + >> + of_node_put(remote); >> + >> + return component_master_add_with_match(dev, >> &imx8mp_dw_hdmi_ops, match); >> + } >> + >> return 0; >> } >> >> static void imx8mp_dw_hdmi_remove(struct platform_device *pdev) >> { >> struct imx8mp_hdmi *hdmi = platform_get_drvdata(pdev); >> + struct device_node *remote; >> >> - dw_hdmi_remove(hdmi->dw_hdmi); >> + remote = of_graph_get_remote_node(pdev->dev.of_node, 2, 0); >> + if (remote) { >> + of_node_put(remote); >> + >> + component_master_del(&pdev->dev, &imx8mp_dw_hdmi_ops); >> + } else { >> + dw_hdmi_remove(hdmi->dw_hdmi); >> + } >> } >> >> static int imx8mp_dw_hdmi_pm_suspend(struct device *dev) >> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h >> index 095cdd9b7424..336f062e1f9d 100644 >> --- a/include/drm/bridge/dw_hdmi.h >> +++ b/include/drm/bridge/dw_hdmi.h >> @@ -143,6 +143,12 @@ struct dw_hdmi_plat_data { >> const struct drm_display_info *info, >> const struct drm_display_mode *mode); >> >> + /* >> + * priv_audio is specially used for additional audio device to get >> + * driver data through this dw_hdmi_plat_data. >> + */ >> + void *priv_audio; >> + >> /* Platform-specific audio enable/disable (optional) */ >> void (*enable_audio)(struct dw_hdmi *hdmi, int channel, >> int width, int rate, int non_pcm, int iec958); >> -- >> 2.34.1 >> -- Regards, Liu Ying