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