aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/omap
diff options
context:
space:
mode:
authorJyri Sarha <jsarha@ti.com>2014-08-15 08:44:44 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2014-12-01 04:09:23 -0500
commit50211be80290b061a923ea7e2d36a6865a48920a (patch)
tree758ca9208240ffdb171f98e459f0bd58fe396bc0 /sound/soc/omap
parent5872b35aaf78ad5dcaab15f2bc874058226615a1 (diff)
ASoC: omap-hdmi-audio: Add platform device for OMAP HDMI audio support
The platform device should only be registered from OMAPDSS HDMI driver. The platform driver registers and unregisters all ASoC components needed for OMAP HDMI audio. The hdmi audio driver implements cpu-dai component using the callbacks provided by OMAPDSS and registers the component under DSS HDMI device. Omap-pcm is registered for platform component also under DSS HDMI device. Dummy codec is used as as codec component. The hdmi audio driver implements also the card and registers it under its own platform device. Signed-off-by: Jyri Sarha <jsarha@ti.com> Acked-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Reviewed-by: Mark Brown <broonie@kernel.org> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'sound/soc/omap')
-rw-r--r--sound/soc/omap/Kconfig15
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/omap-hdmi-audio.c407
3 files changed, 424 insertions, 0 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index d44463a7b0fa..2b32b3e58bf1 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -15,6 +15,21 @@ config SND_OMAP_SOC_MCPDM
15config SND_OMAP_SOC_HDMI 15config SND_OMAP_SOC_HDMI
16 tristate 16 tristate
17 17
18config SND_OMAP_SOC_HDMI_AUDIO
19 tristate "HDMI audio support for OMAP4+ based SoCs"
20 depends on SND_OMAP_SOC
21 help
22 For HDMI audio to work OMAPDSS HDMI support should be
23 enabled.
24 The hdmi audio driver implements cpu-dai component using the
25 callbacks provided by OMAPDSS and registers the component
26 under DSS HDMI device. Omap-pcm is registered for platform
27 component also under DSS HDMI device. Dummy codec is used as
28 as codec component. The hdmi audio driver implements also
29 the card and registers it under its own platform device.
30 The device for the dirver is registered by OMAPDSS hdmi
31 driver.
32
18config SND_OMAP_SOC_N810 33config SND_OMAP_SOC_N810
19 tristate "SoC Audio support for Nokia N810" 34 tristate "SoC Audio support for Nokia N810"
20 depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C 35 depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index a725905b2c68..40688a6e8e98 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -4,12 +4,14 @@ snd-soc-omap-dmic-objs := omap-dmic.o
4snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o 4snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o
5snd-soc-omap-mcpdm-objs := omap-mcpdm.o 5snd-soc-omap-mcpdm-objs := omap-mcpdm.o
6snd-soc-omap-hdmi-objs := omap-hdmi.o 6snd-soc-omap-hdmi-objs := omap-hdmi.o
7snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o
7 8
8obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o 9obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
9obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o 10obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o
10obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o 11obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
11obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o 12obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
12obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o 13obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o
14obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o
13 15
14# OMAP Machine Support 16# OMAP Machine Support
15snd-soc-n810-objs := n810.o 17snd-soc-n810-objs := n810.o
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c
new file mode 100644
index 000000000000..3f9ac7dbdc80
--- /dev/null
+++ b/sound/soc/omap/omap-hdmi-audio.c
@@ -0,0 +1,407 @@
1/*
2 * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library
3 *
4 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
5 *
6 * Author: Jyri Sarha <jsarha@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/err.h>
22#include <linux/string.h>
23#include <linux/platform_device.h>
24#include <sound/soc.h>
25#include <sound/pcm_params.h>
26#include <sound/dmaengine_pcm.h>
27#include <uapi/sound/asound.h>
28#include <sound/asoundef.h>
29#include <sound/omap-pcm.h>
30#include <sound/omap-hdmi-audio.h>
31#include <video/omapdss.h>
32
33#define DRV_NAME "omap-hdmi-audio"
34
35struct hdmi_audio_data {
36 struct snd_soc_card *card;
37
38 const struct omap_hdmi_audio_ops *ops;
39 struct device *dssdev;
40 struct snd_dmaengine_dai_dma_data dma_data;
41 struct omap_dss_audio dss_audio;
42 struct snd_aes_iec958 iec;
43 struct snd_cea_861_aud_if cea;
44
45 struct mutex current_stream_lock;
46 struct snd_pcm_substream *current_stream;
47};
48
49static
50struct hdmi_audio_data *card_drvdata_substream(struct snd_pcm_substream *ss)
51{
52 struct snd_soc_pcm_runtime *rtd = ss->private_data;
53
54 return snd_soc_card_get_drvdata(rtd->card);
55}
56
57static void hdmi_dai_abort(struct device *dev)
58{
59 struct hdmi_audio_data *ad = dev_get_drvdata(dev);
60
61 mutex_lock(&ad->current_stream_lock);
62 if (ad->current_stream && ad->current_stream->runtime &&
63 snd_pcm_running(ad->current_stream)) {
64 dev_err(dev, "HDMI display disabled, aborting playback\n");
65 snd_pcm_stream_lock_irq(ad->current_stream);
66 snd_pcm_stop(ad->current_stream, SNDRV_PCM_STATE_DISCONNECTED);
67 snd_pcm_stream_unlock_irq(ad->current_stream);
68 }
69 mutex_unlock(&ad->current_stream_lock);
70}
71
72static int hdmi_dai_startup(struct snd_pcm_substream *substream,
73 struct snd_soc_dai *dai)
74{
75 struct hdmi_audio_data *ad = card_drvdata_substream(substream);
76 int ret;
77 /*
78 * Make sure that the period bytes are multiple of the DMA packet size.
79 * Largest packet size we use is 32 32-bit words = 128 bytes
80 */
81 ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
82 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
83 if (ret < 0) {
84 dev_err(dai->dev, "could not apply constraint\n");
85 return ret;
86 }
87
88 snd_soc_dai_set_dma_data(dai, substream, &ad->dma_data);
89
90 mutex_lock(&ad->current_stream_lock);
91 ad->current_stream = substream;
92 mutex_unlock(&ad->current_stream_lock);
93
94 ret = ad->ops->audio_startup(ad->dssdev, hdmi_dai_abort);
95
96 if (ret) {
97 mutex_lock(&ad->current_stream_lock);
98 ad->current_stream = NULL;
99 mutex_unlock(&ad->current_stream_lock);
100 }
101
102 return ret;
103}
104
105static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
106 struct snd_pcm_hw_params *params,
107 struct snd_soc_dai *dai)
108{
109 struct hdmi_audio_data *ad = card_drvdata_substream(substream);
110 struct snd_aes_iec958 *iec = &ad->iec;
111 struct snd_cea_861_aud_if *cea = &ad->cea;
112
113 WARN_ON(ad->current_stream != substream);
114
115 switch (params_format(params)) {
116 case SNDRV_PCM_FORMAT_S16_LE:
117 ad->dma_data.maxburst = 16;
118 break;
119 case SNDRV_PCM_FORMAT_S24_LE:
120 ad->dma_data.maxburst = 32;
121 break;
122 default:
123 dev_err(dai->dev, "format not supported!\n");
124 return -EINVAL;
125 }
126
127 ad->dss_audio.iec = iec;
128 ad->dss_audio.cea = cea;
129 /*
130 * fill the IEC-60958 channel status word
131 */
132 /* initialize the word bytes */
133 memset(iec->status, 0, sizeof(iec->status));
134
135 /* specify IEC-60958-3 (commercial use) */
136 iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
137
138 /* specify that the audio is LPCM*/
139 iec->status[0] &= ~IEC958_AES0_NONAUDIO;
140
141 iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
142
143 iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
144
145 iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID;
146
147 iec->status[1] = IEC958_AES1_CON_GENERAL;
148
149 iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
150
151 iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC;
152
153 switch (params_rate(params)) {
154 case 32000:
155 iec->status[3] |= IEC958_AES3_CON_FS_32000;
156 break;
157 case 44100:
158 iec->status[3] |= IEC958_AES3_CON_FS_44100;
159 break;
160 case 48000:
161 iec->status[3] |= IEC958_AES3_CON_FS_48000;
162 break;
163 case 88200:
164 iec->status[3] |= IEC958_AES3_CON_FS_88200;
165 break;
166 case 96000:
167 iec->status[3] |= IEC958_AES3_CON_FS_96000;
168 break;
169 case 176400:
170 iec->status[3] |= IEC958_AES3_CON_FS_176400;
171 break;
172 case 192000:
173 iec->status[3] |= IEC958_AES3_CON_FS_192000;
174 break;
175 default:
176 dev_err(dai->dev, "rate not supported!\n");
177 return -EINVAL;
178 }
179
180 /* specify the clock accuracy */
181 iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM;
182
183 /*
184 * specify the word length. The same word length value can mean
185 * two different lengths. Hence, we need to specify the maximum
186 * word length as well.
187 */
188 switch (params_format(params)) {
189 case SNDRV_PCM_FORMAT_S16_LE:
190 iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16;
191 iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24;
192 break;
193 case SNDRV_PCM_FORMAT_S24_LE:
194 iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20;
195 iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24;
196 break;
197 default:
198 dev_err(dai->dev, "format not supported!\n");
199 return -EINVAL;
200 }
201
202 /*
203 * Fill the CEA-861 audio infoframe (see spec for details)
204 */
205
206 cea->db1_ct_cc = (params_channels(params) - 1)
207 & CEA861_AUDIO_INFOFRAME_DB1CC;
208 cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM;
209
210 cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM;
211 cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM;
212
213 cea->db3 = 0; /* not used, all zeros */
214
215 /*
216 * The OMAP HDMI IP requires to use the 8-channel channel code when
217 * transmitting more than two channels.
218 */
219 if (params_channels(params) == 2)
220 cea->db4_ca = 0x0;
221 else
222 cea->db4_ca = 0x13;
223
224 cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
225 /* the expression is trivial but makes clear what we are doing */
226 cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
227
228 return ad->ops->audio_config(ad->dssdev, &ad->dss_audio);
229}
230
231static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
232 struct snd_soc_dai *dai)
233{
234 struct hdmi_audio_data *ad = card_drvdata_substream(substream);
235 int err = 0;
236
237 WARN_ON(ad->current_stream != substream);
238
239 switch (cmd) {
240 case SNDRV_PCM_TRIGGER_START:
241 case SNDRV_PCM_TRIGGER_RESUME:
242 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
243 err = ad->ops->audio_start(ad->dssdev);
244 break;
245 case SNDRV_PCM_TRIGGER_STOP:
246 case SNDRV_PCM_TRIGGER_SUSPEND:
247 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
248 ad->ops->audio_stop(ad->dssdev);
249 break;
250 default:
251 err = -EINVAL;
252 }
253 return err;
254}
255
256static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
257 struct snd_soc_dai *dai)
258{
259 struct hdmi_audio_data *ad = card_drvdata_substream(substream);
260
261 WARN_ON(ad->current_stream != substream);
262
263 ad->ops->audio_shutdown(ad->dssdev);
264
265 mutex_lock(&ad->current_stream_lock);
266 ad->current_stream = NULL;
267 mutex_unlock(&ad->current_stream_lock);
268}
269
270static const struct snd_soc_dai_ops hdmi_dai_ops = {
271 .startup = hdmi_dai_startup,
272 .hw_params = hdmi_dai_hw_params,
273 .trigger = hdmi_dai_trigger,
274 .shutdown = hdmi_dai_shutdown,
275};
276
277static const struct snd_soc_component_driver omap_hdmi_component = {
278 .name = "omapdss_hdmi",
279};
280
281static struct snd_soc_dai_driver omap5_hdmi_dai = {
282 .name = "omap5-hdmi-dai",
283 .playback = {
284 .channels_min = 2,
285 .channels_max = 8,
286 .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
287 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
288 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
289 SNDRV_PCM_RATE_192000),
290 .formats = SNDRV_PCM_FMTBIT_S16_LE,
291 },
292 .ops = &hdmi_dai_ops,
293};
294
295static struct snd_soc_dai_driver omap4_hdmi_dai = {
296 .name = "omap4-hdmi-dai",
297 .playback = {
298 .channels_min = 2,
299 .channels_max = 8,
300 .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
301 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
302 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
303 SNDRV_PCM_RATE_192000),
304 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
305 },
306 .ops = &hdmi_dai_ops,
307};
308
309static int omap_hdmi_audio_probe(struct platform_device *pdev)
310{
311 struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data;
312 struct device *dev = &pdev->dev;
313 struct hdmi_audio_data *ad;
314 struct snd_soc_dai_driver *dai_drv;
315 struct snd_soc_card *card;
316 int ret;
317
318 if (!ha) {
319 dev_err(dev, "No platform data\n");
320 return -EINVAL;
321 }
322
323 ad = devm_kzalloc(dev, sizeof(*ad), GFP_KERNEL);
324 if (!ad)
325 return -ENOMEM;
326 ad->dssdev = ha->dev;
327 ad->ops = ha->ops;
328 ad->dma_data.addr = ha->audio_dma_addr;
329 ad->dma_data.filter_data = "audio_tx";
330 ad->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
331 mutex_init(&ad->current_stream_lock);
332
333 switch (ha->dss_version) {
334 case OMAPDSS_VER_OMAP4430_ES1:
335 case OMAPDSS_VER_OMAP4430_ES2:
336 case OMAPDSS_VER_OMAP4:
337 dai_drv = &omap4_hdmi_dai;
338 break;
339 case OMAPDSS_VER_OMAP5:
340 dai_drv = &omap5_hdmi_dai;
341 break;
342 default:
343 return -EINVAL;
344 }
345 ret = snd_soc_register_component(ad->dssdev, &omap_hdmi_component,
346 dai_drv, 1);
347 if (ret)
348 return ret;
349
350 ret = omap_pcm_platform_register(ad->dssdev);
351 if (ret)
352 return ret;
353
354 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
355 card->name = devm_kasprintf(dev, GFP_KERNEL,
356 "HDMI %s", dev_name(ad->dssdev));
357 card->owner = THIS_MODULE;
358 card->dai_link =
359 devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL);
360 card->dai_link->name = card->name;
361 card->dai_link->stream_name = card->name;
362 card->dai_link->cpu_dai_name = dev_name(ad->dssdev);
363 card->dai_link->platform_name = dev_name(ad->dssdev);
364 card->dai_link->codec_name = "snd-soc-dummy";
365 card->dai_link->codec_dai_name = "snd-soc-dummy-dai";
366 card->num_links = 1;
367 card->dev = dev;
368
369 ret = snd_soc_register_card(card);
370 if (ret) {
371 dev_err(dev, "snd_soc_register_card failed (%d)\n", ret);
372 snd_soc_unregister_component(ad->dssdev);
373 return ret;
374 }
375
376 ad->card = card;
377 snd_soc_card_set_drvdata(card, ad);
378
379 dev_set_drvdata(dev, ad);
380
381 return 0;
382}
383
384static int omap_hdmi_audio_remove(struct platform_device *pdev)
385{
386 struct hdmi_audio_data *ad = platform_get_drvdata(pdev);
387
388 snd_soc_unregister_card(ad->card);
389 snd_soc_unregister_component(ad->dssdev);
390 return 0;
391}
392
393static struct platform_driver hdmi_audio_driver = {
394 .driver = {
395 .name = DRV_NAME,
396 .owner = THIS_MODULE,
397 },
398 .probe = omap_hdmi_audio_probe,
399 .remove = omap_hdmi_audio_remove,
400};
401
402module_platform_driver(hdmi_audio_driver);
403
404MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
405MODULE_DESCRIPTION("OMAP HDMI Audio Driver");
406MODULE_LICENSE("GPL");
407MODULE_ALIAS("platform:" DRV_NAME);