blob: c2d593f9e4bb73add2dbca8f81f1edd9205868dd [file] [log] [blame]
Liam Girdwood0340c172011-02-03 18:12:46 +00001/*
2 * omap-hdmi.c -- OMAP ALSA SoC DAI driver for HDMI audio
3 *
4 * Copyright (C) 2009 Texas Instruments
5 *
6 * Contact: Jorge Candelaria <x0107209@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 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/init.h>
25#include <linux/module.h>
26#include <linux/device.h>
27#include <sound/core.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/initval.h>
31#include <sound/soc.h>
32
33#include <plat/dma.h>
34#include "omap-pcm.h"
35#include "omap-hdmi.h"
36
37#define CONFIG_HDMI_NO_IP_MODULE
38#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \
39 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
40
41/* Support for 16 and 24 bits */
42#define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
43 SNDRV_PCM_FMTBIT_S24_LE)
44
45#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
46#include <plat/hdmi_lib.h>
47
48struct omap_hdmi_data {
49 struct hdmi_notifier notifier;
50};
51
52struct omap_hdmi_data hdmi_data;
53#else
54struct hdmi_ip_driver hdmi_audio_core;
55#endif
56
57static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = {
58 .name = "HDMI playback",
59 .dma_req = OMAP44XX_DMA_DSS_HDMI_REQ,
60 .port_addr = HDMI_WP + HDMI_WP_AUDIO_DATA,
61 .sync_mode = OMAP_DMA_SYNC_PACKET,
62};
63
64#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
65static void hdmi_hpd_notifier(int state, void *data)
66{
67 struct snd_pcm_substream *substream = data;
68
69 if (!state && substream)
70 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
71}
72#endif
73
74static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
75 struct snd_soc_dai *dai)
76{
77 int err = 0;
78#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
79 struct hdmi_notifier *notifier = &hdmi_data.notifier;
80
81 notifier->hpd_notifier = hdmi_hpd_notifier;
82 notifier->private_data = substream;
83 hdmi_add_notifier(notifier);
84
85 err = hdmi_w1_wrapper_enable(HDMI_WP);
86#else
87 if (hdmi_audio_core.module_loaded)
88 err = hdmi_audio_core.wrapper_enable(HDMI_WP);
89 else
90 printk(KERN_WARNING "Warning: hdmi_core.ko is not enabled");
91#endif
92 return err;
93}
94
95static void omap_hdmi_dai_shutdown(struct snd_pcm_substream *substream,
96 struct snd_soc_dai *dai)
97{
98#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
99 struct hdmi_notifier *notifier = &hdmi_data.notifier;
100
101 hdmi_w1_wrapper_disable(HDMI_WP);
102
103 hdmi_remove_notifier(notifier);
104 notifier->private_data = NULL;
105#else
106 if (hdmi_audio_core.module_loaded)
107 hdmi_audio_core.wrapper_disable(HDMI_WP);
108 else
109 printk(KERN_WARNING "Warning: hdmi_core.ko is not enabled");
110#endif
111 return;
112}
113
114static int omap_hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
115 struct snd_soc_dai *dai)
116{
117 int err = 0;
118
119 switch (cmd) {
120 case SNDRV_PCM_TRIGGER_START:
121 case SNDRV_PCM_TRIGGER_RESUME:
122 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
123#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
124 err = hdmi_w1_start_audio_transfer(HDMI_WP);
125#else
126 if (hdmi_audio_core.module_loaded)
127 err = hdmi_audio_core.start_audio(HDMI_WP);
128 else
129 printk(KERN_WARNING "Warning: hdmi_core.ko is "
130 "not enabled");
131#endif
132 break;
133
134 case SNDRV_PCM_TRIGGER_STOP:
135 case SNDRV_PCM_TRIGGER_SUSPEND:
136 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
137#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
138 err = hdmi_w1_stop_audio_transfer(HDMI_WP);
139#else
140 if (hdmi_audio_core.module_loaded)
141 err = hdmi_audio_core.stop_audio(HDMI_WP);
142 else
143 printk(KERN_WARNING "Warning: hdmi_core.ko is "
144 "not enabled");
145#endif
146 break;
147 default:
148 err = -EINVAL;
149 }
150
151 return err;
152}
153
154static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
155 struct snd_pcm_hw_params *params,
156 struct snd_soc_dai *dai)
157{
158 int err = 0;
159
160 switch (params_format(params)) {
161 case SNDRV_PCM_FORMAT_S16_LE:
162#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
163 err = hdmi_configure_audio_sample_size(HDMI_SAMPLE_16BITS);
164#else
165 if (hdmi_audio_core.module_loaded)
166 err = hdmi_audio_core.config_sample_size(HDMI_WP,
167 HDMI_SAMPLE_16BITS);
168 else
169 printk(KERN_WARNING "Warning: hdmi_core.ko is "
170 "not enabled");
171#endif
172 omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
173 break;
174 case SNDRV_PCM_FORMAT_S24_LE:
175#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
176 err = hdmi_configure_audio_sample_size(HDMI_SAMPLE_24BITS);
177#else
178 if (hdmi_audio_core.module_loaded)
179 err = hdmi_audio_core.config_sample_size(HDMI_WP,
180 HDMI_SAMPLE_24BITS);
181 else
182 printk(KERN_WARNING "Warning: hdmi_core.ko is "
183 "not enabled");
184#endif
185 omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
186 break;
187 default:
188 err = -EINVAL;
189 }
190 if (err < 0)
191 return err;
192
193#if defined(CONFIG_HDMI_NO_IP) || defined(CONFIG_HDMI_NO_IP_MODULE)
194 err = hdmi_configure_audio_channels(params_channels(params),
195 HDMI_CEA_CODE_00);
196 if (err < 0)
197 return err;
198 err = hdmi_configure_audio_sample_rate(params_rate(params));
199 if (err < 0)
200 return err;
201 err = hdmi_configure_audio();
202 if (err < 0)
203 return err;
204#else
205 err = hdmi_audio_core.config_audio_channels(HDMI_WP,
206 params_channels(params), HDMI_CEA_CODE_00);
207 if (err < 0)
208 return err;
209 err = hdmi_audio_core.config_audio_sample_rate(HDMI_WP,
210 params_rate(params));
211 if (err < 0)
212 return err;
213#endif
214
215 omap_hdmi_dai_dma_params.packet_size = 0x20;
216
217 snd_soc_dai_set_dma_data(dai, substream,
218 &omap_hdmi_dai_dma_params);
219
220 return err;
221}
222
223static struct snd_soc_dai_ops omap_hdmi_dai_ops = {
224 .startup = omap_hdmi_dai_startup,
225 .shutdown = omap_hdmi_dai_shutdown,
226 .trigger = omap_hdmi_dai_trigger,
227 .hw_params = omap_hdmi_dai_hw_params,
228};
229
230static struct snd_soc_dai_driver omap_hdmi_dai = {
231 .playback = {
232 .channels_min = 2,
233 /* currently we support only stereo HDMI */
234 .channels_max = 2,
235 .rates = OMAP_HDMI_RATES,
236 .formats = OMAP_HDMI_FORMATS,
237 },
238 .ops = &omap_hdmi_dai_ops,
239};
240
241static __devinit int omap_hdmi_probe(struct platform_device *pdev)
242{
243 return snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
244}
245
246static int __devexit omap_hdmi_remove(struct platform_device *pdev)
247{
248 snd_soc_unregister_dai(&pdev->dev);
249 return 0;
250}
251
252static struct platform_driver hdmi_dai_driver = {
253 .driver = {
254 .name = "hdmi-dai",
255 .owner = THIS_MODULE,
256 },
257 .probe = omap_hdmi_probe,
258 .remove = __devexit_p(omap_hdmi_remove),
259};
260
261static int __init hdmi_dai_init(void)
262{
263 return platform_driver_register(&hdmi_dai_driver);
264}
265module_init(hdmi_dai_init);
266
267static void __exit hdmi_dai_exit(void)
268{
269 platform_driver_unregister(&hdmi_dai_driver);
270}
271module_exit(hdmi_dai_exit);
272
273#if !defined(CONFIG_HDMI_NO_IP) && !defined(CONFIG_HDMI_NO_IP_MODULE)
274
275/* stub */
276int audio_stub_lib_init(void)
277{
278 printk(KERN_WARNING "ERR: please install HDMI IP kernel module\n");
279 return -1;
280}
281void audio_stub_lib_exit(void)
282{
283 printk(KERN_WARNING "HDMI module does not exist!\n");
284}
285
286#define EXPORT_SYMTAB
287
288/* HDMI panel driver */
289void hdmi_audio_core_stub_init(void)
290{
291 hdmi_audio_core.stop_video = NULL;
292 hdmi_audio_core.start_video = NULL;
293 hdmi_audio_core.wrapper_enable = NULL;
294 hdmi_audio_core.wrapper_disable = NULL;
295 hdmi_audio_core.stop_audio = NULL;
296 hdmi_audio_core.start_audio = NULL;
297 hdmi_audio_core.config_video = NULL;
298 hdmi_audio_core.set_wait_pll = NULL;
299 hdmi_audio_core.set_wait_pwr = NULL;
300 hdmi_audio_core.set_wait_srst = NULL;
301 hdmi_audio_core.read_edid = NULL;
302 hdmi_audio_core.config_audio_sample_rate = NULL;
303 hdmi_audio_core.config_sample_size = NULL;
304 hdmi_audio_core.config_audio_channels = NULL;
305 hdmi_audio_core.ip_init = audio_stub_lib_init;
306 hdmi_audio_core.ip_exit = audio_stub_lib_exit;
307 hdmi_audio_core.module_loaded = 0;
308}
309
310#endif
311
312
313MODULE_AUTHOR("Jorge Candelaria <x0107209@ti.com");
314MODULE_DESCRIPTION("OMAP HDMI SoC Interface");
315MODULE_LICENSE("GPL");