xref: /openbmc/linux/sound/soc/fsl/imx-es8328.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
10fe61fc0SAndra Danciu // SPDX-License-Identifier: GPL-2.0+
20fe61fc0SAndra Danciu //
30fe61fc0SAndra Danciu // Copyright 2012 Freescale Semiconductor, Inc.
40fe61fc0SAndra Danciu // Copyright 2012 Linaro Ltd.
57e7292dbSSean Cross 
67e7292dbSSean Cross #include <linux/gpio.h>
77e7292dbSSean Cross #include <linux/module.h>
87e7292dbSSean Cross #include <linux/of.h>
97e7292dbSSean Cross #include <linux/of_platform.h>
107e7292dbSSean Cross #include <linux/i2c.h>
117e7292dbSSean Cross #include <linux/of_gpio.h>
127e7292dbSSean Cross #include <sound/soc.h>
137e7292dbSSean Cross #include <sound/jack.h>
147e7292dbSSean Cross 
157e7292dbSSean Cross #include "imx-audmux.h"
167e7292dbSSean Cross 
177e7292dbSSean Cross #define DAI_NAME_SIZE	32
187e7292dbSSean Cross #define MUX_PORT_MAX	7
197e7292dbSSean Cross 
207e7292dbSSean Cross struct imx_es8328_data {
217e7292dbSSean Cross 	struct device *dev;
227e7292dbSSean Cross 	struct snd_soc_dai_link dai;
237e7292dbSSean Cross 	struct snd_soc_card card;
247e7292dbSSean Cross 	char codec_dai_name[DAI_NAME_SIZE];
257e7292dbSSean Cross 	char platform_name[DAI_NAME_SIZE];
267e7292dbSSean Cross 	int jack_gpio;
277e7292dbSSean Cross };
287e7292dbSSean Cross 
297e7292dbSSean Cross static struct snd_soc_jack_gpio headset_jack_gpios[] = {
307e7292dbSSean Cross 	{
317e7292dbSSean Cross 		.gpio = -1,
327e7292dbSSean Cross 		.name = "headset-gpio",
337e7292dbSSean Cross 		.report = SND_JACK_HEADSET,
347e7292dbSSean Cross 		.invert = 0,
357e7292dbSSean Cross 		.debounce_time = 200,
367e7292dbSSean Cross 	},
377e7292dbSSean Cross };
387e7292dbSSean Cross 
397e7292dbSSean Cross static struct snd_soc_jack headset_jack;
40*105e8458SAlper Nebi Yasak static struct snd_soc_jack_pin headset_jack_pins[] = {
41*105e8458SAlper Nebi Yasak 	{
42*105e8458SAlper Nebi Yasak 		.pin = "Headphone",
43*105e8458SAlper Nebi Yasak 		.mask = SND_JACK_HEADPHONE,
44*105e8458SAlper Nebi Yasak 	},
45*105e8458SAlper Nebi Yasak 	{
46*105e8458SAlper Nebi Yasak 		.pin = "Mic Jack",
47*105e8458SAlper Nebi Yasak 		.mask = SND_JACK_MICROPHONE,
48*105e8458SAlper Nebi Yasak 	},
49*105e8458SAlper Nebi Yasak };
507e7292dbSSean Cross 
imx_es8328_dai_init(struct snd_soc_pcm_runtime * rtd)517e7292dbSSean Cross static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
527e7292dbSSean Cross {
537e7292dbSSean Cross 	struct imx_es8328_data *data = container_of(rtd->card,
547e7292dbSSean Cross 					struct imx_es8328_data, card);
557e7292dbSSean Cross 	int ret = 0;
567e7292dbSSean Cross 
577e7292dbSSean Cross 	/* Headphone jack detection */
587e7292dbSSean Cross 	if (gpio_is_valid(data->jack_gpio)) {
59*105e8458SAlper Nebi Yasak 		ret = snd_soc_card_jack_new_pins(rtd->card, "Headphone",
60*105e8458SAlper Nebi Yasak 						 SND_JACK_HEADSET | SND_JACK_BTN_0,
61*105e8458SAlper Nebi Yasak 						 &headset_jack,
62*105e8458SAlper Nebi Yasak 						 headset_jack_pins,
63*105e8458SAlper Nebi Yasak 						 ARRAY_SIZE(headset_jack_pins));
647e7292dbSSean Cross 		if (ret)
657e7292dbSSean Cross 			return ret;
667e7292dbSSean Cross 
677e7292dbSSean Cross 		headset_jack_gpios[0].gpio = data->jack_gpio;
687e7292dbSSean Cross 		ret = snd_soc_jack_add_gpios(&headset_jack,
697e7292dbSSean Cross 					     ARRAY_SIZE(headset_jack_gpios),
707e7292dbSSean Cross 					     headset_jack_gpios);
717e7292dbSSean Cross 	}
727e7292dbSSean Cross 
737e7292dbSSean Cross 	return ret;
747e7292dbSSean Cross }
757e7292dbSSean Cross 
767e7292dbSSean Cross static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
777e7292dbSSean Cross 	SND_SOC_DAPM_MIC("Mic Jack", NULL),
787e7292dbSSean Cross 	SND_SOC_DAPM_HP("Headphone", NULL),
797e7292dbSSean Cross 	SND_SOC_DAPM_SPK("Speaker", NULL),
807e7292dbSSean Cross 	SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
817e7292dbSSean Cross };
827e7292dbSSean Cross 
83*105e8458SAlper Nebi Yasak static const struct snd_kcontrol_new imx_es8328_controls[] = {
84*105e8458SAlper Nebi Yasak 	SOC_DAPM_PIN_SWITCH("Headphone"),
85*105e8458SAlper Nebi Yasak 	SOC_DAPM_PIN_SWITCH("Mic Jack"),
86*105e8458SAlper Nebi Yasak };
87*105e8458SAlper Nebi Yasak 
imx_es8328_probe(struct platform_device * pdev)887e7292dbSSean Cross static int imx_es8328_probe(struct platform_device *pdev)
897e7292dbSSean Cross {
907e7292dbSSean Cross 	struct device_node *np = pdev->dev.of_node;
91960baba4STakashi Iwai 	struct device_node *ssi_np = NULL, *codec_np = NULL;
927e7292dbSSean Cross 	struct platform_device *ssi_pdev;
937e7292dbSSean Cross 	struct imx_es8328_data *data;
9435f6d302SKuninori Morimoto 	struct snd_soc_dai_link_component *comp;
957e7292dbSSean Cross 	u32 int_port, ext_port;
967e7292dbSSean Cross 	int ret;
977e7292dbSSean Cross 	struct device *dev = &pdev->dev;
987e7292dbSSean Cross 
997e7292dbSSean Cross 	ret = of_property_read_u32(np, "mux-int-port", &int_port);
1007e7292dbSSean Cross 	if (ret) {
1017e7292dbSSean Cross 		dev_err(dev, "mux-int-port missing or invalid\n");
1027e7292dbSSean Cross 		goto fail;
1037e7292dbSSean Cross 	}
1047e7292dbSSean Cross 	if (int_port > MUX_PORT_MAX || int_port == 0) {
1057e7292dbSSean Cross 		dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
1067e7292dbSSean Cross 			MUX_PORT_MAX);
1073b891513SWang Wensheng 		ret = -EINVAL;
1087e7292dbSSean Cross 		goto fail;
1097e7292dbSSean Cross 	}
1107e7292dbSSean Cross 
1117e7292dbSSean Cross 	ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
1127e7292dbSSean Cross 	if (ret) {
1137e7292dbSSean Cross 		dev_err(dev, "mux-ext-port missing or invalid\n");
1147e7292dbSSean Cross 		goto fail;
1157e7292dbSSean Cross 	}
1167e7292dbSSean Cross 	if (ext_port > MUX_PORT_MAX || ext_port == 0) {
1177e7292dbSSean Cross 		dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
1187e7292dbSSean Cross 			MUX_PORT_MAX);
1195c4c99f3STakashi Iwai 		ret = -EINVAL;
1207e7292dbSSean Cross 		goto fail;
1217e7292dbSSean Cross 	}
1227e7292dbSSean Cross 
1237e7292dbSSean Cross 	/*
1247e7292dbSSean Cross 	 * The port numbering in the hardware manual starts at 1, while
1257e7292dbSSean Cross 	 * the audmux API expects it starts at 0.
1267e7292dbSSean Cross 	 */
1277e7292dbSSean Cross 	int_port--;
1287e7292dbSSean Cross 	ext_port--;
1297e7292dbSSean Cross 	ret = imx_audmux_v2_configure_port(int_port,
1307e7292dbSSean Cross 			IMX_AUDMUX_V2_PTCR_SYN |
1317e7292dbSSean Cross 			IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
1327e7292dbSSean Cross 			IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
1337e7292dbSSean Cross 			IMX_AUDMUX_V2_PTCR_TFSDIR |
1347e7292dbSSean Cross 			IMX_AUDMUX_V2_PTCR_TCLKDIR,
1357e7292dbSSean Cross 			IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
1367e7292dbSSean Cross 	if (ret) {
1377e7292dbSSean Cross 		dev_err(dev, "audmux internal port setup failed\n");
1387e7292dbSSean Cross 		return ret;
1397e7292dbSSean Cross 	}
1407e7292dbSSean Cross 	ret = imx_audmux_v2_configure_port(ext_port,
1417e7292dbSSean Cross 			IMX_AUDMUX_V2_PTCR_SYN,
1427e7292dbSSean Cross 			IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
1437e7292dbSSean Cross 	if (ret) {
1447e7292dbSSean Cross 		dev_err(dev, "audmux external port setup failed\n");
1457e7292dbSSean Cross 		return ret;
1467e7292dbSSean Cross 	}
1477e7292dbSSean Cross 
1487e7292dbSSean Cross 	ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
1497e7292dbSSean Cross 	codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
1507e7292dbSSean Cross 	if (!ssi_np || !codec_np) {
1517e7292dbSSean Cross 		dev_err(dev, "phandle missing or invalid\n");
1527e7292dbSSean Cross 		ret = -EINVAL;
1537e7292dbSSean Cross 		goto fail;
1547e7292dbSSean Cross 	}
1557e7292dbSSean Cross 
1567e7292dbSSean Cross 	ssi_pdev = of_find_device_by_node(ssi_np);
1577e7292dbSSean Cross 	if (!ssi_pdev) {
1587e7292dbSSean Cross 		dev_err(dev, "failed to find SSI platform device\n");
1597e7292dbSSean Cross 		ret = -EINVAL;
1607e7292dbSSean Cross 		goto fail;
1617e7292dbSSean Cross 	}
1627e7292dbSSean Cross 
1637e7292dbSSean Cross 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1647e7292dbSSean Cross 	if (!data) {
1657e7292dbSSean Cross 		ret = -ENOMEM;
166e525db7eSYu Kuai 		goto put_device;
1677e7292dbSSean Cross 	}
1687e7292dbSSean Cross 
169d6e28695SKuninori Morimoto 	comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL);
17035f6d302SKuninori Morimoto 	if (!comp) {
17135f6d302SKuninori Morimoto 		ret = -ENOMEM;
172e525db7eSYu Kuai 		goto put_device;
17335f6d302SKuninori Morimoto 	}
17435f6d302SKuninori Morimoto 
1757e7292dbSSean Cross 	data->dev = dev;
1767e7292dbSSean Cross 
1777e7292dbSSean Cross 	data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
1787e7292dbSSean Cross 
179d6e28695SKuninori Morimoto 	/*
180d6e28695SKuninori Morimoto 	 * CPU == Platform
181d6e28695SKuninori Morimoto 	 * platform is using soc-generic-dmaengine-pcm
182d6e28695SKuninori Morimoto 	 */
183d6e28695SKuninori Morimoto 	data->dai.cpus		=
184d6e28695SKuninori Morimoto 	data->dai.platforms	= &comp[0];
18535f6d302SKuninori Morimoto 	data->dai.codecs	= &comp[1];
18635f6d302SKuninori Morimoto 
18735f6d302SKuninori Morimoto 	data->dai.num_cpus	= 1;
18835f6d302SKuninori Morimoto 	data->dai.num_codecs	= 1;
1898bebc822SKuninori Morimoto 	data->dai.num_platforms	= 1;
19035f6d302SKuninori Morimoto 
1917e7292dbSSean Cross 	data->dai.name = "hifi";
1927e7292dbSSean Cross 	data->dai.stream_name = "hifi";
19335f6d302SKuninori Morimoto 	data->dai.codecs->dai_name = "es8328-hifi-analog";
19435f6d302SKuninori Morimoto 	data->dai.codecs->of_node = codec_np;
19535f6d302SKuninori Morimoto 	data->dai.cpus->of_node = ssi_np;
1967e7292dbSSean Cross 	data->dai.init = &imx_es8328_dai_init;
1977e7292dbSSean Cross 	data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
19856b69e4eSMark Brown 			    SND_SOC_DAIFMT_CBP_CFP;
1997e7292dbSSean Cross 
2007e7292dbSSean Cross 	data->card.dev = dev;
2017e7292dbSSean Cross 	data->card.dapm_widgets = imx_es8328_dapm_widgets;
2027e7292dbSSean Cross 	data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
203*105e8458SAlper Nebi Yasak 	data->card.controls = imx_es8328_controls;
204*105e8458SAlper Nebi Yasak 	data->card.num_controls = ARRAY_SIZE(imx_es8328_controls);
2057e7292dbSSean Cross 	ret = snd_soc_of_parse_card_name(&data->card, "model");
2067e7292dbSSean Cross 	if (ret) {
2077e7292dbSSean Cross 		dev_err(dev, "Unable to parse card name\n");
208e525db7eSYu Kuai 		goto put_device;
2097e7292dbSSean Cross 	}
2107e7292dbSSean Cross 	ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
2117e7292dbSSean Cross 	if (ret) {
2127e7292dbSSean Cross 		dev_err(dev, "Unable to parse routing: %d\n", ret);
213e525db7eSYu Kuai 		goto put_device;
2147e7292dbSSean Cross 	}
2157e7292dbSSean Cross 	data->card.num_links = 1;
2167e7292dbSSean Cross 	data->card.owner = THIS_MODULE;
2177e7292dbSSean Cross 	data->card.dai_link = &data->dai;
2187e7292dbSSean Cross 
21928b17011SYang Yingliang 	ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
2207e7292dbSSean Cross 	if (ret) {
2217e7292dbSSean Cross 		dev_err(dev, "Unable to register: %d\n", ret);
222e525db7eSYu Kuai 		goto put_device;
2237e7292dbSSean Cross 	}
2247e7292dbSSean Cross 
2257e7292dbSSean Cross 	platform_set_drvdata(pdev, data);
226e525db7eSYu Kuai put_device:
227e525db7eSYu Kuai 	put_device(&ssi_pdev->dev);
2287e7292dbSSean Cross fail:
2297e7292dbSSean Cross 	of_node_put(ssi_np);
2307e7292dbSSean Cross 	of_node_put(codec_np);
2317e7292dbSSean Cross 
2327e7292dbSSean Cross 	return ret;
2337e7292dbSSean Cross }
2347e7292dbSSean Cross 
2357e7292dbSSean Cross static const struct of_device_id imx_es8328_dt_ids[] = {
2367e7292dbSSean Cross 	{ .compatible = "fsl,imx-audio-es8328", },
2377e7292dbSSean Cross 	{ /* sentinel */ }
2387e7292dbSSean Cross };
2397e7292dbSSean Cross MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
2407e7292dbSSean Cross 
2417e7292dbSSean Cross static struct platform_driver imx_es8328_driver = {
2427e7292dbSSean Cross 	.driver = {
2437e7292dbSSean Cross 		.name = "imx-es8328",
2447e7292dbSSean Cross 		.of_match_table = imx_es8328_dt_ids,
2457e7292dbSSean Cross 	},
2467e7292dbSSean Cross 	.probe = imx_es8328_probe,
2477e7292dbSSean Cross };
2487e7292dbSSean Cross module_platform_driver(imx_es8328_driver);
2497e7292dbSSean Cross 
2507e7292dbSSean Cross MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
2517e7292dbSSean Cross MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
2527e7292dbSSean Cross MODULE_LICENSE("GPL v2");
2537e7292dbSSean Cross MODULE_ALIAS("platform:imx-audio-es8328");
254