xref: /linux/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c (revision 8185461e531c39d67aa4705d7f94873feb87adfd)
10205fae6SShengjiu Wang // SPDX-License-Identifier: GPL-2.0+
20205fae6SShengjiu Wang /*
30205fae6SShengjiu Wang  * Copyright 2025 NXP
40205fae6SShengjiu Wang  */
50205fae6SShengjiu Wang 
60205fae6SShengjiu Wang #include <linux/bitfield.h>
70205fae6SShengjiu Wang #include <linux/component.h>
80205fae6SShengjiu Wang #include <linux/module.h>
90205fae6SShengjiu Wang #include <linux/of_platform.h>
100205fae6SShengjiu Wang #include <linux/platform_device.h>
11*40b24d9cSShengjiu Wang #include <linux/pm_runtime.h>
120205fae6SShengjiu Wang #include <linux/regmap.h>
130205fae6SShengjiu Wang #include <drm/bridge/dw_hdmi.h>
140205fae6SShengjiu Wang #include <sound/asoundef.h>
150205fae6SShengjiu Wang 
160205fae6SShengjiu Wang #define HTX_PAI_CTRL			0x00
170205fae6SShengjiu Wang #define   ENABLE			BIT(0)
180205fae6SShengjiu Wang 
190205fae6SShengjiu Wang #define HTX_PAI_CTRL_EXT		0x04
200205fae6SShengjiu Wang #define   WTMK_HIGH_MASK		GENMASK(31, 24)
210205fae6SShengjiu Wang #define   WTMK_LOW_MASK			GENMASK(23, 16)
220205fae6SShengjiu Wang #define   NUM_CH_MASK			GENMASK(10, 8)
230205fae6SShengjiu Wang #define   WTMK_HIGH(n)			FIELD_PREP(WTMK_HIGH_MASK, (n))
240205fae6SShengjiu Wang #define   WTMK_LOW(n)			FIELD_PREP(WTMK_LOW_MASK, (n))
250205fae6SShengjiu Wang #define   NUM_CH(n)			FIELD_PREP(NUM_CH_MASK, (n) - 1)
260205fae6SShengjiu Wang 
270205fae6SShengjiu Wang #define HTX_PAI_FIELD_CTRL		0x08
280205fae6SShengjiu Wang #define   PRE_SEL			GENMASK(28, 24)
290205fae6SShengjiu Wang #define   D_SEL				GENMASK(23, 20)
300205fae6SShengjiu Wang #define   V_SEL				GENMASK(19, 15)
310205fae6SShengjiu Wang #define   U_SEL				GENMASK(14, 10)
320205fae6SShengjiu Wang #define   C_SEL				GENMASK(9, 5)
330205fae6SShengjiu Wang #define   P_SEL				GENMASK(4, 0)
340205fae6SShengjiu Wang 
350205fae6SShengjiu Wang struct imx8mp_hdmi_pai {
360205fae6SShengjiu Wang 	struct regmap	*regmap;
37*40b24d9cSShengjiu Wang 	struct device	*dev;
380205fae6SShengjiu Wang };
390205fae6SShengjiu Wang 
imx8mp_hdmi_pai_enable(struct dw_hdmi * dw_hdmi,int channel,int width,int rate,int non_pcm,int iec958)400205fae6SShengjiu Wang static void imx8mp_hdmi_pai_enable(struct dw_hdmi *dw_hdmi, int channel,
410205fae6SShengjiu Wang 				   int width, int rate, int non_pcm,
420205fae6SShengjiu Wang 				   int iec958)
430205fae6SShengjiu Wang {
440205fae6SShengjiu Wang 	const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi);
450205fae6SShengjiu Wang 	struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio;
460205fae6SShengjiu Wang 	int val;
470205fae6SShengjiu Wang 
48*40b24d9cSShengjiu Wang 	if (pm_runtime_resume_and_get(hdmi_pai->dev) < 0)
49*40b24d9cSShengjiu Wang 		return;
50*40b24d9cSShengjiu Wang 
510205fae6SShengjiu Wang 	/* PAI set control extended */
520205fae6SShengjiu Wang 	val =  WTMK_HIGH(3) | WTMK_LOW(3);
530205fae6SShengjiu Wang 	val |= NUM_CH(channel);
540205fae6SShengjiu Wang 	regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL_EXT, val);
550205fae6SShengjiu Wang 
560205fae6SShengjiu Wang 	/* IEC60958 format */
570205fae6SShengjiu Wang 	if (iec958) {
580205fae6SShengjiu Wang 		val = FIELD_PREP_CONST(P_SEL,
590205fae6SShengjiu Wang 				       __bf_shf(IEC958_SUBFRAME_PARITY));
600205fae6SShengjiu Wang 		val |= FIELD_PREP_CONST(C_SEL,
610205fae6SShengjiu Wang 					__bf_shf(IEC958_SUBFRAME_CHANNEL_STATUS));
620205fae6SShengjiu Wang 		val |= FIELD_PREP_CONST(U_SEL,
630205fae6SShengjiu Wang 					__bf_shf(IEC958_SUBFRAME_USER_DATA));
640205fae6SShengjiu Wang 		val |= FIELD_PREP_CONST(V_SEL,
650205fae6SShengjiu Wang 					__bf_shf(IEC958_SUBFRAME_VALIDITY));
660205fae6SShengjiu Wang 		val |= FIELD_PREP_CONST(D_SEL,
670205fae6SShengjiu Wang 					__bf_shf(IEC958_SUBFRAME_SAMPLE_24_MASK));
680205fae6SShengjiu Wang 		val |= FIELD_PREP_CONST(PRE_SEL,
690205fae6SShengjiu Wang 					__bf_shf(IEC958_SUBFRAME_PREAMBLE_MASK));
700205fae6SShengjiu Wang 	} else {
710205fae6SShengjiu Wang 		/*
720205fae6SShengjiu Wang 		 * The allowed PCM widths are 24bit and 32bit, as they are supported
730205fae6SShengjiu Wang 		 * by aud2htx module.
740205fae6SShengjiu Wang 		 * for 24bit, D_SEL = 0, select all the bits.
750205fae6SShengjiu Wang 		 * for 32bit, D_SEL = 8, select 24bit in MSB.
760205fae6SShengjiu Wang 		 */
770205fae6SShengjiu Wang 		val = FIELD_PREP(D_SEL, width - 24);
780205fae6SShengjiu Wang 	}
790205fae6SShengjiu Wang 
800205fae6SShengjiu Wang 	regmap_write(hdmi_pai->regmap, HTX_PAI_FIELD_CTRL, val);
810205fae6SShengjiu Wang 
820205fae6SShengjiu Wang 	/* PAI start running */
830205fae6SShengjiu Wang 	regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, ENABLE);
840205fae6SShengjiu Wang }
850205fae6SShengjiu Wang 
imx8mp_hdmi_pai_disable(struct dw_hdmi * dw_hdmi)860205fae6SShengjiu Wang static void imx8mp_hdmi_pai_disable(struct dw_hdmi *dw_hdmi)
870205fae6SShengjiu Wang {
880205fae6SShengjiu Wang 	const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi);
890205fae6SShengjiu Wang 	struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio;
900205fae6SShengjiu Wang 
910205fae6SShengjiu Wang 	/* Stop PAI */
920205fae6SShengjiu Wang 	regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, 0);
93*40b24d9cSShengjiu Wang 
94*40b24d9cSShengjiu Wang 	pm_runtime_put_sync(hdmi_pai->dev);
950205fae6SShengjiu Wang }
960205fae6SShengjiu Wang 
970205fae6SShengjiu Wang static const struct regmap_config imx8mp_hdmi_pai_regmap_config = {
980205fae6SShengjiu Wang 	.reg_bits = 32,
990205fae6SShengjiu Wang 	.reg_stride = 4,
1000205fae6SShengjiu Wang 	.val_bits = 32,
1010205fae6SShengjiu Wang 	.max_register = HTX_PAI_FIELD_CTRL,
1020205fae6SShengjiu Wang };
1030205fae6SShengjiu Wang 
imx8mp_hdmi_pai_bind(struct device * dev,struct device * master,void * data)1040205fae6SShengjiu Wang static int imx8mp_hdmi_pai_bind(struct device *dev, struct device *master, void *data)
1050205fae6SShengjiu Wang {
1060205fae6SShengjiu Wang 	struct platform_device *pdev = to_platform_device(dev);
1070205fae6SShengjiu Wang 	struct dw_hdmi_plat_data *plat_data = data;
1080205fae6SShengjiu Wang 	struct imx8mp_hdmi_pai *hdmi_pai;
1090205fae6SShengjiu Wang 	struct resource *res;
1100205fae6SShengjiu Wang 	void __iomem *base;
111*40b24d9cSShengjiu Wang 	int ret;
1120205fae6SShengjiu Wang 
1130205fae6SShengjiu Wang 	hdmi_pai = devm_kzalloc(dev, sizeof(*hdmi_pai), GFP_KERNEL);
1140205fae6SShengjiu Wang 	if (!hdmi_pai)
1150205fae6SShengjiu Wang 		return -ENOMEM;
1160205fae6SShengjiu Wang 
1170205fae6SShengjiu Wang 	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
1180205fae6SShengjiu Wang 	if (IS_ERR(base))
1190205fae6SShengjiu Wang 		return PTR_ERR(base);
1200205fae6SShengjiu Wang 
1210205fae6SShengjiu Wang 	hdmi_pai->regmap = devm_regmap_init_mmio_clk(dev, "apb", base,
1220205fae6SShengjiu Wang 						     &imx8mp_hdmi_pai_regmap_config);
1230205fae6SShengjiu Wang 	if (IS_ERR(hdmi_pai->regmap)) {
1240205fae6SShengjiu Wang 		dev_err(dev, "regmap init failed\n");
1250205fae6SShengjiu Wang 		return PTR_ERR(hdmi_pai->regmap);
1260205fae6SShengjiu Wang 	}
1270205fae6SShengjiu Wang 
1280205fae6SShengjiu Wang 	plat_data->enable_audio = imx8mp_hdmi_pai_enable;
1290205fae6SShengjiu Wang 	plat_data->disable_audio = imx8mp_hdmi_pai_disable;
1300205fae6SShengjiu Wang 	plat_data->priv_audio = hdmi_pai;
1310205fae6SShengjiu Wang 
132*40b24d9cSShengjiu Wang 	hdmi_pai->dev = dev;
133*40b24d9cSShengjiu Wang 	ret = devm_pm_runtime_enable(dev);
134*40b24d9cSShengjiu Wang 	if (ret < 0) {
135*40b24d9cSShengjiu Wang 		dev_err(dev, "failed to enable PM runtime: %d\n", ret);
136*40b24d9cSShengjiu Wang 		return ret;
137*40b24d9cSShengjiu Wang 	}
138*40b24d9cSShengjiu Wang 
1390205fae6SShengjiu Wang 	return 0;
1400205fae6SShengjiu Wang }
1410205fae6SShengjiu Wang 
1420205fae6SShengjiu Wang static const struct component_ops imx8mp_hdmi_pai_ops = {
1430205fae6SShengjiu Wang 	.bind   = imx8mp_hdmi_pai_bind,
1440205fae6SShengjiu Wang };
1450205fae6SShengjiu Wang 
imx8mp_hdmi_pai_probe(struct platform_device * pdev)1460205fae6SShengjiu Wang static int imx8mp_hdmi_pai_probe(struct platform_device *pdev)
1470205fae6SShengjiu Wang {
1480205fae6SShengjiu Wang 	return component_add(&pdev->dev, &imx8mp_hdmi_pai_ops);
1490205fae6SShengjiu Wang }
1500205fae6SShengjiu Wang 
imx8mp_hdmi_pai_remove(struct platform_device * pdev)1510205fae6SShengjiu Wang static void imx8mp_hdmi_pai_remove(struct platform_device *pdev)
1520205fae6SShengjiu Wang {
1530205fae6SShengjiu Wang 	component_del(&pdev->dev, &imx8mp_hdmi_pai_ops);
1540205fae6SShengjiu Wang }
1550205fae6SShengjiu Wang 
1560205fae6SShengjiu Wang static const struct of_device_id imx8mp_hdmi_pai_of_table[] = {
1570205fae6SShengjiu Wang 	{ .compatible = "fsl,imx8mp-hdmi-pai" },
1580205fae6SShengjiu Wang 	{ /* Sentinel */ }
1590205fae6SShengjiu Wang };
1600205fae6SShengjiu Wang MODULE_DEVICE_TABLE(of, imx8mp_hdmi_pai_of_table);
1610205fae6SShengjiu Wang 
1620205fae6SShengjiu Wang static struct platform_driver imx8mp_hdmi_pai_platform_driver = {
1630205fae6SShengjiu Wang 	.probe		= imx8mp_hdmi_pai_probe,
1640205fae6SShengjiu Wang 	.remove		= imx8mp_hdmi_pai_remove,
1650205fae6SShengjiu Wang 	.driver		= {
1660205fae6SShengjiu Wang 		.name	= "imx8mp-hdmi-pai",
1670205fae6SShengjiu Wang 		.of_match_table = imx8mp_hdmi_pai_of_table,
1680205fae6SShengjiu Wang 	},
1690205fae6SShengjiu Wang };
1700205fae6SShengjiu Wang module_platform_driver(imx8mp_hdmi_pai_platform_driver);
1710205fae6SShengjiu Wang 
1720205fae6SShengjiu Wang MODULE_DESCRIPTION("i.MX8MP HDMI PAI driver");
1730205fae6SShengjiu Wang MODULE_LICENSE("GPL");
174