4 * OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors.
5 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
6 * Authors: Jorge Candelaria <jorge.candelaria@ti.com>
7 * Ricardo Neri <ricardo.neri@ti.com>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * version 2 as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
25 #include <linux/init.h>
26 #include <linux/module.h>
27 #include <linux/device.h>
28 #include <sound/core.h>
29 #include <sound/pcm.h>
30 #include <sound/pcm_params.h>
31 #include <sound/initval.h>
32 #include <sound/soc.h>
33 #include <sound/asound.h>
34 #include <sound/asoundef.h>
35 #include <sound/dmaengine_pcm.h>
36 #include <video/omapdss.h>
38 #include "omap-hdmi.h"
40 #define DRV_NAME "omap-hdmi-audio-dai"
43 struct snd_dmaengine_dai_dma_data dma_data
;
45 struct omap_dss_audio dss_audio
;
46 struct snd_aes_iec958 iec
;
47 struct snd_cea_861_aud_if cea
;
48 struct omap_dss_device
*dssdev
;
51 static int omap_hdmi_dai_startup(struct snd_pcm_substream
*substream
,
52 struct snd_soc_dai
*dai
)
54 struct hdmi_priv
*priv
= snd_soc_dai_get_drvdata(dai
);
57 * Make sure that the period bytes are multiple of the DMA packet size.
58 * Largest packet size we use is 32 32-bit words = 128 bytes
60 err
= snd_pcm_hw_constraint_step(substream
->runtime
, 0,
61 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 128);
63 dev_err(dai
->dev
, "could not apply constraint\n");
67 if (!priv
->dssdev
->driver
->audio_supported(priv
->dssdev
)) {
68 dev_err(dai
->dev
, "audio not supported\n");
72 snd_soc_dai_set_dma_data(dai
, substream
, &priv
->dma_data
);
77 static int omap_hdmi_dai_prepare(struct snd_pcm_substream
*substream
,
78 struct snd_soc_dai
*dai
)
80 struct hdmi_priv
*priv
= snd_soc_dai_get_drvdata(dai
);
82 return priv
->dssdev
->driver
->audio_enable(priv
->dssdev
);
85 static int omap_hdmi_dai_hw_params(struct snd_pcm_substream
*substream
,
86 struct snd_pcm_hw_params
*params
,
87 struct snd_soc_dai
*dai
)
89 struct hdmi_priv
*priv
= snd_soc_dai_get_drvdata(dai
);
90 struct snd_aes_iec958
*iec
= &priv
->iec
;
91 struct snd_cea_861_aud_if
*cea
= &priv
->cea
;
94 switch (params_format(params
)) {
95 case SNDRV_PCM_FORMAT_S16_LE
:
96 priv
->dma_data
.maxburst
= 16;
98 case SNDRV_PCM_FORMAT_S24_LE
:
99 priv
->dma_data
.maxburst
= 32;
102 dev_err(dai
->dev
, "format not supported!\n");
107 * fill the IEC-60958 channel status word
109 /* initialize the word bytes */
110 memset(iec
->status
, 0, sizeof(iec
->status
));
112 /* specify IEC-60958-3 (commercial use) */
113 iec
->status
[0] &= ~IEC958_AES0_PROFESSIONAL
;
115 /* specify that the audio is LPCM*/
116 iec
->status
[0] &= ~IEC958_AES0_NONAUDIO
;
118 iec
->status
[0] |= IEC958_AES0_CON_NOT_COPYRIGHT
;
120 iec
->status
[0] |= IEC958_AES0_CON_EMPHASIS_NONE
;
122 iec
->status
[0] |= IEC958_AES1_PRO_MODE_NOTID
;
124 iec
->status
[1] = IEC958_AES1_CON_GENERAL
;
126 iec
->status
[2] |= IEC958_AES2_CON_SOURCE_UNSPEC
;
128 iec
->status
[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC
;
130 switch (params_rate(params
)) {
132 iec
->status
[3] |= IEC958_AES3_CON_FS_32000
;
135 iec
->status
[3] |= IEC958_AES3_CON_FS_44100
;
138 iec
->status
[3] |= IEC958_AES3_CON_FS_48000
;
141 iec
->status
[3] |= IEC958_AES3_CON_FS_88200
;
144 iec
->status
[3] |= IEC958_AES3_CON_FS_96000
;
147 iec
->status
[3] |= IEC958_AES3_CON_FS_176400
;
150 iec
->status
[3] |= IEC958_AES3_CON_FS_192000
;
153 dev_err(dai
->dev
, "rate not supported!\n");
157 /* specify the clock accuracy */
158 iec
->status
[3] |= IEC958_AES3_CON_CLOCK_1000PPM
;
161 * specify the word length. The same word length value can mean
162 * two different lengths. Hence, we need to specify the maximum
163 * word length as well.
165 switch (params_format(params
)) {
166 case SNDRV_PCM_FORMAT_S16_LE
:
167 iec
->status
[4] |= IEC958_AES4_CON_WORDLEN_20_16
;
168 iec
->status
[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24
;
170 case SNDRV_PCM_FORMAT_S24_LE
:
171 iec
->status
[4] |= IEC958_AES4_CON_WORDLEN_24_20
;
172 iec
->status
[4] |= IEC958_AES4_CON_MAX_WORDLEN_24
;
175 dev_err(dai
->dev
, "format not supported!\n");
180 * Fill the CEA-861 audio infoframe (see spec for details)
183 cea
->db1_ct_cc
= (params_channels(params
) - 1)
184 & CEA861_AUDIO_INFOFRAME_DB1CC
;
185 cea
->db1_ct_cc
|= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM
;
187 cea
->db2_sf_ss
= CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM
;
188 cea
->db2_sf_ss
|= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM
;
190 cea
->db3
= 0; /* not used, all zeros */
193 * The OMAP HDMI IP requires to use the 8-channel channel code when
194 * transmitting more than two channels.
196 if (params_channels(params
) == 2)
201 cea
->db5_dminh_lsv
= CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED
;
202 /* the expression is trivial but makes clear what we are doing */
203 cea
->db5_dminh_lsv
|= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV
);
205 priv
->dss_audio
.iec
= iec
;
206 priv
->dss_audio
.cea
= cea
;
208 err
= priv
->dssdev
->driver
->audio_config(priv
->dssdev
,
214 static int omap_hdmi_dai_trigger(struct snd_pcm_substream
*substream
, int cmd
,
215 struct snd_soc_dai
*dai
)
217 struct hdmi_priv
*priv
= snd_soc_dai_get_drvdata(dai
);
221 case SNDRV_PCM_TRIGGER_START
:
222 case SNDRV_PCM_TRIGGER_RESUME
:
223 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
224 err
= priv
->dssdev
->driver
->audio_start(priv
->dssdev
);
226 case SNDRV_PCM_TRIGGER_STOP
:
227 case SNDRV_PCM_TRIGGER_SUSPEND
:
228 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
229 priv
->dssdev
->driver
->audio_stop(priv
->dssdev
);
237 static void omap_hdmi_dai_shutdown(struct snd_pcm_substream
*substream
,
238 struct snd_soc_dai
*dai
)
240 struct hdmi_priv
*priv
= snd_soc_dai_get_drvdata(dai
);
242 priv
->dssdev
->driver
->audio_disable(priv
->dssdev
);
245 static const struct snd_soc_dai_ops omap_hdmi_dai_ops
= {
246 .startup
= omap_hdmi_dai_startup
,
247 .hw_params
= omap_hdmi_dai_hw_params
,
248 .prepare
= omap_hdmi_dai_prepare
,
249 .trigger
= omap_hdmi_dai_trigger
,
250 .shutdown
= omap_hdmi_dai_shutdown
,
253 static struct snd_soc_dai_driver omap_hdmi_dai
= {
257 .rates
= OMAP_HDMI_RATES
,
258 .formats
= OMAP_HDMI_FORMATS
,
260 .ops
= &omap_hdmi_dai_ops
,
263 static const struct snd_soc_component_driver omap_hdmi_component
= {
267 static int omap_hdmi_probe(struct platform_device
*pdev
)
270 struct resource
*hdmi_rsrc
;
271 struct hdmi_priv
*hdmi_data
;
272 bool hdmi_dev_found
= false;
274 hdmi_data
= devm_kzalloc(&pdev
->dev
, sizeof(*hdmi_data
), GFP_KERNEL
);
275 if (hdmi_data
== NULL
) {
276 dev_err(&pdev
->dev
, "Cannot allocate memory for HDMI data\n");
280 hdmi_rsrc
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
282 dev_err(&pdev
->dev
, "Cannot obtain IORESOURCE_MEM HDMI\n");
286 hdmi_data
->dma_data
.addr
= hdmi_rsrc
->start
+ OMAP_HDMI_AUDIO_DMA_PORT
;
288 hdmi_rsrc
= platform_get_resource(pdev
, IORESOURCE_DMA
, 0);
290 dev_err(&pdev
->dev
, "Cannot obtain IORESOURCE_DMA HDMI\n");
294 hdmi_data
->dma_req
= hdmi_rsrc
->start
;
295 hdmi_data
->dma_data
.filter_data
= &hdmi_data
->dma_req
;
296 hdmi_data
->dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
299 * TODO: We assume that there is only one DSS HDMI device. Future
300 * OMAP implementations may support more than one HDMI devices and
301 * we should provided separate audio support for all of them.
303 /* Find an HDMI device. */
304 for_each_dss_dev(hdmi_data
->dssdev
) {
305 omap_dss_get_device(hdmi_data
->dssdev
);
307 if (!hdmi_data
->dssdev
->driver
) {
308 omap_dss_put_device(hdmi_data
->dssdev
);
312 if (hdmi_data
->dssdev
->type
== OMAP_DISPLAY_TYPE_HDMI
) {
313 hdmi_dev_found
= true;
318 if (!hdmi_dev_found
) {
319 dev_err(&pdev
->dev
, "no driver for HDMI display found\n");
323 dev_set_drvdata(&pdev
->dev
, hdmi_data
);
324 ret
= snd_soc_register_component(&pdev
->dev
, &omap_hdmi_component
,
330 static int omap_hdmi_remove(struct platform_device
*pdev
)
332 struct hdmi_priv
*hdmi_data
= dev_get_drvdata(&pdev
->dev
);
334 snd_soc_unregister_component(&pdev
->dev
);
336 if (hdmi_data
== NULL
) {
337 dev_err(&pdev
->dev
, "cannot obtain HDMi data\n");
341 omap_dss_put_device(hdmi_data
->dssdev
);
345 static struct platform_driver hdmi_dai_driver
= {
348 .owner
= THIS_MODULE
,
350 .probe
= omap_hdmi_probe
,
351 .remove
= omap_hdmi_remove
,
354 module_platform_driver(hdmi_dai_driver
);
356 MODULE_AUTHOR("Jorge Candelaria <jorge.candelaria@ti.com>");
357 MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
358 MODULE_DESCRIPTION("OMAP HDMI SoC Interface");
359 MODULE_LICENSE("GPL");
360 MODULE_ALIAS("platform:" DRV_NAME
);