aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Cross <xobs@kosagi.com>2014-07-30 22:43:37 -0400
committerMark Brown <broonie@linaro.org>2014-08-16 18:18:07 -0400
commit7e7292dba2155c1433ce9f9a819f1acb9090747b (patch)
treeb90f3a7f58b55e8911746e9f665a4a7089058620
parente1a65374a3bcaf34eb67322b3e761965f333a66a (diff)
ASoC: fsl: add imx-es8328 machine driver
This adds an initial machine driver for the ES8328 audio codec on Freescale boards. The driver supports headphones and an audio regulator for an onboard speaker amp. Signed-off-by: Sean Cross <xobs@kosagi.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-es8328.txt60
-rw-r--r--sound/soc/fsl/Kconfig14
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/imx-es8328.c232
4 files changed, 308 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt b/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt
new file mode 100644
index 000000000000..07b68ab206fb
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-es8328.txt
@@ -0,0 +1,60 @@
1Freescale i.MX audio complex with ES8328 codec
2
3Required properties:
4- compatible : "fsl,imx-audio-es8328"
5- model : The user-visible name of this sound complex
6- ssi-controller : The phandle of the i.MX SSI controller
7- jack-gpio : Optional GPIO for headphone jack
8- audio-amp-supply : Power regulator for speaker amps
9- audio-codec : The phandle of the ES8328 audio codec
10- audio-routing : A list of the connections between audio components.
11 Each entry is a pair of strings, the first being the
12 connection's sink, the second being the connection's
13 source. Valid names could be power supplies, ES8328
14 pins, and the jacks on the board:
15
16 Power supplies:
17 * audio-amp
18
19 ES8328 pins:
20 * LOUT1
21 * LOUT2
22 * ROUT1
23 * ROUT2
24 * LINPUT1
25 * LINPUT2
26 * RINPUT1
27 * RINPUT2
28 * Mic PGA
29
30 Board connectors:
31 * Headphone
32 * Speaker
33 * Mic Jack
34- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
35- mux-ext-port : The external port of the i.MX audio muxer (AUDMIX)
36
37Note: The AUDMUX port numbering should start at 1, which is consistent with
38hardware manual.
39
40Example:
41
42sound {
43 compatible = "fsl,imx-audio-es8328";
44 model = "imx-audio-es8328";
45 ssi-controller = <&ssi1>;
46 audio-codec = <&codec>;
47 jack-gpio = <&gpio5 15 0>;
48 audio-amp-supply = <&reg_audio_amp>;
49 audio-routing =
50 "Speaker", "LOUT2",
51 "Speaker", "ROUT2",
52 "Speaker", "audio-amp",
53 "Headphone", "ROUT1",
54 "Headphone", "LOUT1",
55 "LINPUT1", "Mic Jack",
56 "RINPUT1", "Mic Jack",
57 "Mic Jack", "Mic Bias";
58 mux-int-port = <1>;
59 mux-ext-port = <3>;
60};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 2b99a9e86899..fa90340dc13a 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -257,6 +257,20 @@ config SND_SOC_IMX_WM8962
257 Say Y if you want to add support for SoC audio on an i.MX board with 257 Say Y if you want to add support for SoC audio on an i.MX board with
258 a wm8962 codec. 258 a wm8962 codec.
259 259
260config SND_SOC_IMX_ES8328
261 tristate "SoC Audio support for i.MX boards with the ES8328 codec"
262 depends on OF && (I2C || SPI)
263 select SND_SOC_ES8328_I2C if I2C
264 select SND_SOC_ES8328_SPI if SPI_MASTER
265 select SND_SOC_IMX_PCM_DMA
266 select SND_SOC_IMX_AUDMUX
267 select SND_SOC_FSL_SSI
268 select SND_SOC_FSL_UTILS
269 select SND_SOC_IMX_PCM_FIQ
270 help
271 Say Y if you want to add support for the ES8328 audio codec connected
272 via SSI/I2S over either SPI or I2C.
273
260config SND_SOC_IMX_SGTL5000 274config SND_SOC_IMX_SGTL5000
261 tristate "SoC Audio support for i.MX boards with sgtl5000" 275 tristate "SoC Audio support for i.MX boards with sgtl5000"
262 depends on OF && I2C 276 depends on OF && I2C
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8f6d84efa973..d28dc25c9375 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -52,6 +52,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
52snd-soc-phycore-ac97-objs := phycore-ac97.o 52snd-soc-phycore-ac97-objs := phycore-ac97.o
53snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o 53snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
54snd-soc-wm1133-ev1-objs := wm1133-ev1.o 54snd-soc-wm1133-ev1-objs := wm1133-ev1.o
55snd-soc-imx-es8328-objs := imx-es8328.o
55snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o 56snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
56snd-soc-imx-wm8962-objs := imx-wm8962.o 57snd-soc-imx-wm8962-objs := imx-wm8962.o
57snd-soc-imx-spdif-objs := imx-spdif.o 58snd-soc-imx-spdif-objs := imx-spdif.o
@@ -61,6 +62,7 @@ obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
61obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o 62obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
62obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o 63obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
63obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o 64obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
65obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
64obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o 66obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
65obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o 67obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
66obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o 68obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c
new file mode 100644
index 000000000000..653e66d150c8
--- /dev/null
+++ b/sound/soc/fsl/imx-es8328.c
@@ -0,0 +1,232 @@
1/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 * Copyright 2012 Linaro Ltd.
4 *
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
8 *
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
11 */
12
13#include <linux/gpio.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/of_platform.h>
17#include <linux/i2c.h>
18#include <linux/of_gpio.h>
19#include <sound/soc.h>
20#include <sound/jack.h>
21
22#include "imx-audmux.h"
23
24#define DAI_NAME_SIZE 32
25#define MUX_PORT_MAX 7
26
27struct imx_es8328_data {
28 struct device *dev;
29 struct snd_soc_dai_link dai;
30 struct snd_soc_card card;
31 char codec_dai_name[DAI_NAME_SIZE];
32 char platform_name[DAI_NAME_SIZE];
33 int jack_gpio;
34};
35
36static struct snd_soc_jack_gpio headset_jack_gpios[] = {
37 {
38 .gpio = -1,
39 .name = "headset-gpio",
40 .report = SND_JACK_HEADSET,
41 .invert = 0,
42 .debounce_time = 200,
43 },
44};
45
46static struct snd_soc_jack headset_jack;
47
48static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
49{
50 struct imx_es8328_data *data = container_of(rtd->card,
51 struct imx_es8328_data, card);
52 int ret = 0;
53
54 /* Headphone jack detection */
55 if (gpio_is_valid(data->jack_gpio)) {
56 ret = snd_soc_jack_new(rtd->codec, "Headphone",
57 SND_JACK_HEADPHONE | SND_JACK_BTN_0,
58 &headset_jack);
59 if (ret)
60 return ret;
61
62 headset_jack_gpios[0].gpio = data->jack_gpio;
63 ret = snd_soc_jack_add_gpios(&headset_jack,
64 ARRAY_SIZE(headset_jack_gpios),
65 headset_jack_gpios);
66 }
67
68 return ret;
69}
70
71static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
72 SND_SOC_DAPM_MIC("Mic Jack", NULL),
73 SND_SOC_DAPM_HP("Headphone", NULL),
74 SND_SOC_DAPM_SPK("Speaker", NULL),
75 SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
76};
77
78static int imx_es8328_probe(struct platform_device *pdev)
79{
80 struct device_node *np = pdev->dev.of_node;
81 struct device_node *ssi_np, *codec_np;
82 struct platform_device *ssi_pdev;
83 struct imx_es8328_data *data;
84 u32 int_port, ext_port;
85 int ret;
86 struct device *dev = &pdev->dev;
87
88 ret = of_property_read_u32(np, "mux-int-port", &int_port);
89 if (ret) {
90 dev_err(dev, "mux-int-port missing or invalid\n");
91 goto fail;
92 }
93 if (int_port > MUX_PORT_MAX || int_port == 0) {
94 dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
95 MUX_PORT_MAX);
96 goto fail;
97 }
98
99 ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
100 if (ret) {
101 dev_err(dev, "mux-ext-port missing or invalid\n");
102 goto fail;
103 }
104 if (ext_port > MUX_PORT_MAX || ext_port == 0) {
105 dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
106 MUX_PORT_MAX);
107 goto fail;
108 }
109
110 /*
111 * The port numbering in the hardware manual starts at 1, while
112 * the audmux API expects it starts at 0.
113 */
114 int_port--;
115 ext_port--;
116 ret = imx_audmux_v2_configure_port(int_port,
117 IMX_AUDMUX_V2_PTCR_SYN |
118 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
119 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
120 IMX_AUDMUX_V2_PTCR_TFSDIR |
121 IMX_AUDMUX_V2_PTCR_TCLKDIR,
122 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
123 if (ret) {
124 dev_err(dev, "audmux internal port setup failed\n");
125 return ret;
126 }
127 ret = imx_audmux_v2_configure_port(ext_port,
128 IMX_AUDMUX_V2_PTCR_SYN,
129 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
130 if (ret) {
131 dev_err(dev, "audmux external port setup failed\n");
132 return ret;
133 }
134
135 ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
136 codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
137 if (!ssi_np || !codec_np) {
138 dev_err(dev, "phandle missing or invalid\n");
139 ret = -EINVAL;
140 goto fail;
141 }
142
143 ssi_pdev = of_find_device_by_node(ssi_np);
144 if (!ssi_pdev) {
145 dev_err(dev, "failed to find SSI platform device\n");
146 ret = -EINVAL;
147 goto fail;
148 }
149
150 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
151 if (!data) {
152 ret = -ENOMEM;
153 goto fail;
154 }
155
156 data->dev = dev;
157
158 data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
159
160 data->dai.name = "hifi";
161 data->dai.stream_name = "hifi";
162 data->dai.codec_dai_name = "es8328-hifi-analog";
163 data->dai.codec_of_node = codec_np;
164 data->dai.cpu_of_node = ssi_np;
165 data->dai.platform_of_node = ssi_np;
166 data->dai.init = &imx_es8328_dai_init;
167 data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
168 SND_SOC_DAIFMT_CBM_CFM;
169
170 data->card.dev = dev;
171 data->card.dapm_widgets = imx_es8328_dapm_widgets;
172 data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
173 ret = snd_soc_of_parse_card_name(&data->card, "model");
174 if (ret) {
175 dev_err(dev, "Unable to parse card name\n");
176 goto fail;
177 }
178 ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
179 if (ret) {
180 dev_err(dev, "Unable to parse routing: %d\n", ret);
181 goto fail;
182 }
183 data->card.num_links = 1;
184 data->card.owner = THIS_MODULE;
185 data->card.dai_link = &data->dai;
186
187 ret = snd_soc_register_card(&data->card);
188 if (ret) {
189 dev_err(dev, "Unable to register: %d\n", ret);
190 goto fail;
191 }
192
193 platform_set_drvdata(pdev, data);
194fail:
195 of_node_put(ssi_np);
196 of_node_put(codec_np);
197
198 return ret;
199}
200
201static int imx_es8328_remove(struct platform_device *pdev)
202{
203 struct imx_es8328_data *data = platform_get_drvdata(pdev);
204
205 snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
206 headset_jack_gpios);
207
208 snd_soc_unregister_card(&data->card);
209
210 return 0;
211}
212
213static const struct of_device_id imx_es8328_dt_ids[] = {
214 { .compatible = "fsl,imx-audio-es8328", },
215 { /* sentinel */ }
216};
217MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
218
219static struct platform_driver imx_es8328_driver = {
220 .driver = {
221 .name = "imx-es8328",
222 .of_match_table = imx_es8328_dt_ids,
223 },
224 .probe = imx_es8328_probe,
225 .remove = imx_es8328_remove,
226};
227module_platform_driver(imx_es8328_driver);
228
229MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
230MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
231MODULE_LICENSE("GPL v2");
232MODULE_ALIAS("platform:imx-audio-es8328");