1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2025 NXP 4 */ 5 6 #include <linux/bitfield.h> 7 #include <linux/component.h> 8 #include <linux/module.h> 9 #include <linux/of_platform.h> 10 #include <linux/platform_device.h> 11 #include <linux/pm_runtime.h> 12 #include <linux/regmap.h> 13 #include <drm/bridge/dw_hdmi.h> 14 #include <sound/asoundef.h> 15 16 #define HTX_PAI_CTRL 0x00 17 #define ENABLE BIT(0) 18 19 #define HTX_PAI_CTRL_EXT 0x04 20 #define WTMK_HIGH_MASK GENMASK(31, 24) 21 #define WTMK_LOW_MASK GENMASK(23, 16) 22 #define NUM_CH_MASK GENMASK(10, 8) 23 #define WTMK_HIGH(n) FIELD_PREP(WTMK_HIGH_MASK, (n)) 24 #define WTMK_LOW(n) FIELD_PREP(WTMK_LOW_MASK, (n)) 25 #define NUM_CH(n) FIELD_PREP(NUM_CH_MASK, (n) - 1) 26 27 #define HTX_PAI_FIELD_CTRL 0x08 28 #define PRE_SEL GENMASK(28, 24) 29 #define D_SEL GENMASK(23, 20) 30 #define V_SEL GENMASK(19, 15) 31 #define U_SEL GENMASK(14, 10) 32 #define C_SEL GENMASK(9, 5) 33 #define P_SEL GENMASK(4, 0) 34 35 struct imx8mp_hdmi_pai { 36 struct regmap *regmap; 37 struct device *dev; 38 }; 39 40 static void imx8mp_hdmi_pai_enable(struct dw_hdmi *dw_hdmi, int channel, 41 int width, int rate, int non_pcm, 42 int iec958) 43 { 44 const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi); 45 struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio; 46 int val; 47 48 if (pm_runtime_resume_and_get(hdmi_pai->dev) < 0) 49 return; 50 51 /* PAI set control extended */ 52 val = WTMK_HIGH(3) | WTMK_LOW(3); 53 val |= NUM_CH(channel); 54 regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL_EXT, val); 55 56 /* IEC60958 format */ 57 if (iec958) { 58 val = FIELD_PREP_CONST(P_SEL, 59 __bf_shf(IEC958_SUBFRAME_PARITY)); 60 val |= FIELD_PREP_CONST(C_SEL, 61 __bf_shf(IEC958_SUBFRAME_CHANNEL_STATUS)); 62 val |= FIELD_PREP_CONST(U_SEL, 63 __bf_shf(IEC958_SUBFRAME_USER_DATA)); 64 val |= FIELD_PREP_CONST(V_SEL, 65 __bf_shf(IEC958_SUBFRAME_VALIDITY)); 66 val |= FIELD_PREP_CONST(D_SEL, 67 __bf_shf(IEC958_SUBFRAME_SAMPLE_24_MASK)); 68 val |= FIELD_PREP_CONST(PRE_SEL, 69 __bf_shf(IEC958_SUBFRAME_PREAMBLE_MASK)); 70 } else { 71 /* 72 * The allowed PCM widths are 24bit and 32bit, as they are supported 73 * by aud2htx module. 74 * for 24bit, D_SEL = 0, select all the bits. 75 * for 32bit, D_SEL = 8, select 24bit in MSB. 76 */ 77 val = FIELD_PREP(D_SEL, width - 24); 78 } 79 80 regmap_write(hdmi_pai->regmap, HTX_PAI_FIELD_CTRL, val); 81 82 /* PAI start running */ 83 regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, ENABLE); 84 } 85 86 static void imx8mp_hdmi_pai_disable(struct dw_hdmi *dw_hdmi) 87 { 88 const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi); 89 struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio; 90 91 /* Stop PAI */ 92 regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, 0); 93 94 pm_runtime_put_sync(hdmi_pai->dev); 95 } 96 97 static const struct regmap_config imx8mp_hdmi_pai_regmap_config = { 98 .reg_bits = 32, 99 .reg_stride = 4, 100 .val_bits = 32, 101 .max_register = HTX_PAI_FIELD_CTRL, 102 }; 103 104 static int imx8mp_hdmi_pai_bind(struct device *dev, struct device *master, void *data) 105 { 106 struct platform_device *pdev = to_platform_device(dev); 107 struct dw_hdmi_plat_data *plat_data = data; 108 struct imx8mp_hdmi_pai *hdmi_pai; 109 struct resource *res; 110 void __iomem *base; 111 int ret; 112 113 hdmi_pai = devm_kzalloc(dev, sizeof(*hdmi_pai), GFP_KERNEL); 114 if (!hdmi_pai) 115 return -ENOMEM; 116 117 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 118 if (IS_ERR(base)) 119 return PTR_ERR(base); 120 121 hdmi_pai->regmap = devm_regmap_init_mmio_clk(dev, "apb", base, 122 &imx8mp_hdmi_pai_regmap_config); 123 if (IS_ERR(hdmi_pai->regmap)) { 124 dev_err(dev, "regmap init failed\n"); 125 return PTR_ERR(hdmi_pai->regmap); 126 } 127 128 plat_data->enable_audio = imx8mp_hdmi_pai_enable; 129 plat_data->disable_audio = imx8mp_hdmi_pai_disable; 130 plat_data->priv_audio = hdmi_pai; 131 132 hdmi_pai->dev = dev; 133 ret = devm_pm_runtime_enable(dev); 134 if (ret < 0) { 135 dev_err(dev, "failed to enable PM runtime: %d\n", ret); 136 return ret; 137 } 138 139 return 0; 140 } 141 142 static const struct component_ops imx8mp_hdmi_pai_ops = { 143 .bind = imx8mp_hdmi_pai_bind, 144 }; 145 146 static int imx8mp_hdmi_pai_probe(struct platform_device *pdev) 147 { 148 return component_add(&pdev->dev, &imx8mp_hdmi_pai_ops); 149 } 150 151 static void imx8mp_hdmi_pai_remove(struct platform_device *pdev) 152 { 153 component_del(&pdev->dev, &imx8mp_hdmi_pai_ops); 154 } 155 156 static const struct of_device_id imx8mp_hdmi_pai_of_table[] = { 157 { .compatible = "fsl,imx8mp-hdmi-pai" }, 158 { /* Sentinel */ } 159 }; 160 MODULE_DEVICE_TABLE(of, imx8mp_hdmi_pai_of_table); 161 162 static struct platform_driver imx8mp_hdmi_pai_platform_driver = { 163 .probe = imx8mp_hdmi_pai_probe, 164 .remove = imx8mp_hdmi_pai_remove, 165 .driver = { 166 .name = "imx8mp-hdmi-pai", 167 .of_match_table = imx8mp_hdmi_pai_of_table, 168 }, 169 }; 170 module_platform_driver(imx8mp_hdmi_pai_platform_driver); 171 172 MODULE_DESCRIPTION("i.MX8MP HDMI PAI driver"); 173 MODULE_LICENSE("GPL"); 174