1 // SPDX-License-Identifier: GPL-2.0-only
3 * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library
5 * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
7 * Author: Jyri Sarha <jsarha@ti.com>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/err.h>
13 #include <linux/string.h>
14 #include <linux/platform_device.h>
15 #include <sound/soc.h>
16 #include <sound/pcm_params.h>
17 #include <sound/dmaengine_pcm.h>
18 #include <uapi/sound/asound.h>
19 #include <sound/asoundef.h>
20 #include <sound/omap-hdmi-audio.h>
24 #define DRV_NAME "omap-hdmi-audio"
26 struct hdmi_audio_data
{
27 struct snd_soc_card
*card
;
29 const struct omap_hdmi_audio_ops
*ops
;
30 struct device
*dssdev
;
31 struct snd_dmaengine_dai_dma_data dma_data
;
32 struct omap_dss_audio dss_audio
;
33 struct snd_aes_iec958 iec
;
34 struct snd_cea_861_aud_if cea
;
36 struct mutex current_stream_lock
;
37 struct snd_pcm_substream
*current_stream
;
41 struct hdmi_audio_data
*card_drvdata_substream(struct snd_pcm_substream
*ss
)
43 struct snd_soc_pcm_runtime
*rtd
= ss
->private_data
;
45 return snd_soc_card_get_drvdata(rtd
->card
);
48 static void hdmi_dai_abort(struct device
*dev
)
50 struct hdmi_audio_data
*ad
= dev_get_drvdata(dev
);
52 mutex_lock(&ad
->current_stream_lock
);
53 if (ad
->current_stream
&& ad
->current_stream
->runtime
&&
54 snd_pcm_running(ad
->current_stream
)) {
55 dev_err(dev
, "HDMI display disabled, aborting playback\n");
56 snd_pcm_stream_lock_irq(ad
->current_stream
);
57 snd_pcm_stop(ad
->current_stream
, SNDRV_PCM_STATE_DISCONNECTED
);
58 snd_pcm_stream_unlock_irq(ad
->current_stream
);
60 mutex_unlock(&ad
->current_stream_lock
);
63 static int hdmi_dai_startup(struct snd_pcm_substream
*substream
,
64 struct snd_soc_dai
*dai
)
66 struct hdmi_audio_data
*ad
= card_drvdata_substream(substream
);
69 * Make sure that the period bytes are multiple of the DMA packet size.
70 * Largest packet size we use is 32 32-bit words = 128 bytes
72 ret
= snd_pcm_hw_constraint_step(substream
->runtime
, 0,
73 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 128);
75 dev_err(dai
->dev
, "Could not apply period constraint: %d\n",
79 ret
= snd_pcm_hw_constraint_step(substream
->runtime
, 0,
80 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 128);
82 dev_err(dai
->dev
, "Could not apply buffer constraint: %d\n",
87 snd_soc_dai_set_dma_data(dai
, substream
, &ad
->dma_data
);
89 mutex_lock(&ad
->current_stream_lock
);
90 ad
->current_stream
= substream
;
91 mutex_unlock(&ad
->current_stream_lock
);
93 ret
= ad
->ops
->audio_startup(ad
->dssdev
, hdmi_dai_abort
);
96 mutex_lock(&ad
->current_stream_lock
);
97 ad
->current_stream
= NULL
;
98 mutex_unlock(&ad
->current_stream_lock
);
104 static int hdmi_dai_hw_params(struct snd_pcm_substream
*substream
,
105 struct snd_pcm_hw_params
*params
,
106 struct snd_soc_dai
*dai
)
108 struct hdmi_audio_data
*ad
= card_drvdata_substream(substream
);
109 struct snd_aes_iec958
*iec
= &ad
->iec
;
110 struct snd_cea_861_aud_if
*cea
= &ad
->cea
;
112 WARN_ON(ad
->current_stream
!= substream
);
114 switch (params_format(params
)) {
115 case SNDRV_PCM_FORMAT_S16_LE
:
116 ad
->dma_data
.maxburst
= 16;
118 case SNDRV_PCM_FORMAT_S24_LE
:
119 ad
->dma_data
.maxburst
= 32;
122 dev_err(dai
->dev
, "format not supported!\n");
126 ad
->dss_audio
.iec
= iec
;
127 ad
->dss_audio
.cea
= cea
;
129 * fill the IEC-60958 channel status word
131 /* initialize the word bytes */
132 memset(iec
->status
, 0, sizeof(iec
->status
));
134 /* specify IEC-60958-3 (commercial use) */
135 iec
->status
[0] &= ~IEC958_AES0_PROFESSIONAL
;
137 /* specify that the audio is LPCM*/
138 iec
->status
[0] &= ~IEC958_AES0_NONAUDIO
;
140 iec
->status
[0] |= IEC958_AES0_CON_NOT_COPYRIGHT
;
142 iec
->status
[0] |= IEC958_AES0_CON_EMPHASIS_NONE
;
144 iec
->status
[1] = IEC958_AES1_CON_GENERAL
;
146 iec
->status
[2] |= IEC958_AES2_CON_SOURCE_UNSPEC
;
148 iec
->status
[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC
;
150 switch (params_rate(params
)) {
152 iec
->status
[3] |= IEC958_AES3_CON_FS_32000
;
155 iec
->status
[3] |= IEC958_AES3_CON_FS_44100
;
158 iec
->status
[3] |= IEC958_AES3_CON_FS_48000
;
161 iec
->status
[3] |= IEC958_AES3_CON_FS_88200
;
164 iec
->status
[3] |= IEC958_AES3_CON_FS_96000
;
167 iec
->status
[3] |= IEC958_AES3_CON_FS_176400
;
170 iec
->status
[3] |= IEC958_AES3_CON_FS_192000
;
173 dev_err(dai
->dev
, "rate not supported!\n");
177 /* specify the clock accuracy */
178 iec
->status
[3] |= IEC958_AES3_CON_CLOCK_1000PPM
;
181 * specify the word length. The same word length value can mean
182 * two different lengths. Hence, we need to specify the maximum
183 * word length as well.
185 switch (params_format(params
)) {
186 case SNDRV_PCM_FORMAT_S16_LE
:
187 iec
->status
[4] |= IEC958_AES4_CON_WORDLEN_20_16
;
188 iec
->status
[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24
;
190 case SNDRV_PCM_FORMAT_S24_LE
:
191 iec
->status
[4] |= IEC958_AES4_CON_WORDLEN_24_20
;
192 iec
->status
[4] |= IEC958_AES4_CON_MAX_WORDLEN_24
;
195 dev_err(dai
->dev
, "format not supported!\n");
200 * Fill the CEA-861 audio infoframe (see spec for details)
203 cea
->db1_ct_cc
= (params_channels(params
) - 1)
204 & CEA861_AUDIO_INFOFRAME_DB1CC
;
205 cea
->db1_ct_cc
|= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM
;
207 cea
->db2_sf_ss
= CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM
;
208 cea
->db2_sf_ss
|= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM
;
210 cea
->db3
= 0; /* not used, all zeros */
212 if (params_channels(params
) == 2)
214 else if (params_channels(params
) == 6)
219 if (cea
->db4_ca
== 0x00)
220 cea
->db5_dminh_lsv
= CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED
;
222 cea
->db5_dminh_lsv
= CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED
;
224 /* the expression is trivial but makes clear what we are doing */
225 cea
->db5_dminh_lsv
|= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV
);
227 return ad
->ops
->audio_config(ad
->dssdev
, &ad
->dss_audio
);
230 static int hdmi_dai_trigger(struct snd_pcm_substream
*substream
, int cmd
,
231 struct snd_soc_dai
*dai
)
233 struct hdmi_audio_data
*ad
= card_drvdata_substream(substream
);
236 WARN_ON(ad
->current_stream
!= substream
);
239 case SNDRV_PCM_TRIGGER_START
:
240 case SNDRV_PCM_TRIGGER_RESUME
:
241 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
242 err
= ad
->ops
->audio_start(ad
->dssdev
);
244 case SNDRV_PCM_TRIGGER_STOP
:
245 case SNDRV_PCM_TRIGGER_SUSPEND
:
246 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
247 ad
->ops
->audio_stop(ad
->dssdev
);
255 static void hdmi_dai_shutdown(struct snd_pcm_substream
*substream
,
256 struct snd_soc_dai
*dai
)
258 struct hdmi_audio_data
*ad
= card_drvdata_substream(substream
);
260 WARN_ON(ad
->current_stream
!= substream
);
262 ad
->ops
->audio_shutdown(ad
->dssdev
);
264 mutex_lock(&ad
->current_stream_lock
);
265 ad
->current_stream
= NULL
;
266 mutex_unlock(&ad
->current_stream_lock
);
269 static const struct snd_soc_dai_ops hdmi_dai_ops
= {
270 .startup
= hdmi_dai_startup
,
271 .hw_params
= hdmi_dai_hw_params
,
272 .trigger
= hdmi_dai_trigger
,
273 .shutdown
= hdmi_dai_shutdown
,
276 static const struct snd_soc_component_driver omap_hdmi_component
= {
277 .name
= "omapdss_hdmi",
280 static struct snd_soc_dai_driver omap5_hdmi_dai
= {
281 .name
= "omap5-hdmi-dai",
285 .rates
= (SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
|
286 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_88200
|
287 SNDRV_PCM_RATE_96000
| SNDRV_PCM_RATE_176400
|
288 SNDRV_PCM_RATE_192000
),
289 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
291 .ops
= &hdmi_dai_ops
,
294 static struct snd_soc_dai_driver omap4_hdmi_dai
= {
295 .name
= "omap4-hdmi-dai",
299 .rates
= (SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
|
300 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_88200
|
301 SNDRV_PCM_RATE_96000
| SNDRV_PCM_RATE_176400
|
302 SNDRV_PCM_RATE_192000
),
303 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
,
305 .ops
= &hdmi_dai_ops
,
308 static int omap_hdmi_audio_probe(struct platform_device
*pdev
)
310 struct omap_hdmi_audio_pdata
*ha
= pdev
->dev
.platform_data
;
311 struct device
*dev
= &pdev
->dev
;
312 struct hdmi_audio_data
*ad
;
313 struct snd_soc_dai_driver
*dai_drv
;
314 struct snd_soc_card
*card
;
315 struct snd_soc_dai_link_component
*compnent
;
319 dev_err(dev
, "No platform data\n");
323 ad
= devm_kzalloc(dev
, sizeof(*ad
), GFP_KERNEL
);
326 ad
->dssdev
= ha
->dev
;
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
);
333 switch (ha
->version
) {
335 dai_drv
= &omap4_hdmi_dai
;
338 dai_drv
= &omap5_hdmi_dai
;
343 ret
= devm_snd_soc_register_component(ad
->dssdev
, &omap_hdmi_component
,
348 ret
= sdma_pcm_platform_register(ad
->dssdev
, "audio_tx", NULL
);
352 card
= devm_kzalloc(dev
, sizeof(*card
), GFP_KERNEL
);
356 card
->name
= devm_kasprintf(dev
, GFP_KERNEL
,
357 "HDMI %s", dev_name(ad
->dssdev
));
361 card
->owner
= THIS_MODULE
;
363 devm_kzalloc(dev
, sizeof(*(card
->dai_link
)), GFP_KERNEL
);
367 compnent
= devm_kzalloc(dev
, 3 * sizeof(*compnent
), GFP_KERNEL
);
370 card
->dai_link
->cpus
= &compnent
[0];
371 card
->dai_link
->num_cpus
= 1;
372 card
->dai_link
->codecs
= &compnent
[1];
373 card
->dai_link
->num_codecs
= 1;
374 card
->dai_link
->platforms
= &compnent
[2];
375 card
->dai_link
->num_platforms
= 1;
377 card
->dai_link
->name
= card
->name
;
378 card
->dai_link
->stream_name
= card
->name
;
379 card
->dai_link
->cpus
->dai_name
= dev_name(ad
->dssdev
);
380 card
->dai_link
->platforms
->name
= dev_name(ad
->dssdev
);
381 card
->dai_link
->codecs
->name
= "snd-soc-dummy";
382 card
->dai_link
->codecs
->dai_name
= "snd-soc-dummy-dai";
386 ret
= snd_soc_register_card(card
);
388 dev_err(dev
, "snd_soc_register_card failed (%d)\n", ret
);
393 snd_soc_card_set_drvdata(card
, ad
);
395 dev_set_drvdata(dev
, ad
);
400 static int omap_hdmi_audio_remove(struct platform_device
*pdev
)
402 struct hdmi_audio_data
*ad
= platform_get_drvdata(pdev
);
404 snd_soc_unregister_card(ad
->card
);
408 static struct platform_driver hdmi_audio_driver
= {
412 .probe
= omap_hdmi_audio_probe
,
413 .remove
= omap_hdmi_audio_remove
,
416 module_platform_driver(hdmi_audio_driver
);
418 MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
419 MODULE_DESCRIPTION("OMAP HDMI Audio Driver");
420 MODULE_LICENSE("GPL");
421 MODULE_ALIAS("platform:" DRV_NAME
);