diff options
Diffstat (limited to 'sound/soc/omap')
-rw-r--r-- | sound/soc/omap/igep0020.c | 137 | ||||
-rw-r--r-- | sound/soc/omap/mcpdm.c | 470 | ||||
-rw-r--r-- | sound/soc/omap/mcpdm.h | 153 | ||||
-rw-r--r-- | sound/soc/omap/omap3beagle.c | 149 | ||||
-rw-r--r-- | sound/soc/omap/omap3evm.c | 135 | ||||
-rw-r--r-- | sound/soc/omap/omap4-hdmi-card.c | 129 | ||||
-rw-r--r-- | sound/soc/omap/overo.c | 139 | ||||
-rw-r--r-- | sound/soc/omap/sdp4430.c | 223 |
8 files changed, 1535 insertions, 0 deletions
diff --git a/sound/soc/omap/igep0020.c b/sound/soc/omap/igep0020.c new file mode 100644 index 00000000000..0ae34702995 --- /dev/null +++ b/sound/soc/omap/igep0020.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * igep0020.c -- SoC audio for IGEP v2 | ||
3 | * | ||
4 | * Based on sound/soc/omap/overo.c by Steve Sakoman | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/soc.h> | ||
27 | |||
28 | #include <asm/mach-types.h> | ||
29 | #include <mach/hardware.h> | ||
30 | #include <mach/gpio.h> | ||
31 | #include <plat/mcbsp.h> | ||
32 | |||
33 | #include "omap-mcbsp.h" | ||
34 | #include "omap-pcm.h" | ||
35 | |||
36 | static int igep2_hw_params(struct snd_pcm_substream *substream, | ||
37 | struct snd_pcm_hw_params *params) | ||
38 | { | ||
39 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
40 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
41 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
42 | int ret; | ||
43 | |||
44 | /* Set codec DAI configuration */ | ||
45 | ret = snd_soc_dai_set_fmt(codec_dai, | ||
46 | SND_SOC_DAIFMT_I2S | | ||
47 | SND_SOC_DAIFMT_NB_NF | | ||
48 | SND_SOC_DAIFMT_CBM_CFM); | ||
49 | if (ret < 0) { | ||
50 | printk(KERN_ERR "can't set codec DAI configuration\n"); | ||
51 | return ret; | ||
52 | } | ||
53 | |||
54 | /* Set cpu DAI configuration */ | ||
55 | ret = snd_soc_dai_set_fmt(cpu_dai, | ||
56 | SND_SOC_DAIFMT_I2S | | ||
57 | SND_SOC_DAIFMT_NB_NF | | ||
58 | SND_SOC_DAIFMT_CBM_CFM); | ||
59 | if (ret < 0) { | ||
60 | printk(KERN_ERR "can't set cpu DAI configuration\n"); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | /* Set the codec system clock for DAC and ADC */ | ||
65 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, | ||
66 | SND_SOC_CLOCK_IN); | ||
67 | if (ret < 0) { | ||
68 | printk(KERN_ERR "can't set codec system clock\n"); | ||
69 | return ret; | ||
70 | } | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static struct snd_soc_ops igep2_ops = { | ||
76 | .hw_params = igep2_hw_params, | ||
77 | }; | ||
78 | |||
79 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
80 | static struct snd_soc_dai_link igep2_dai = { | ||
81 | .name = "TWL4030", | ||
82 | .stream_name = "TWL4030", | ||
83 | .cpu_dai_name = "omap-mcbsp-dai.1", | ||
84 | .codec_dai_name = "twl4030-hifi", | ||
85 | .platform_name = "omap-pcm-audio", | ||
86 | .codec_name = "twl4030-codec", | ||
87 | .ops = &igep2_ops, | ||
88 | }; | ||
89 | |||
90 | /* Audio machine driver */ | ||
91 | static struct snd_soc_card snd_soc_card_igep2 = { | ||
92 | .name = "igep2", | ||
93 | .dai_link = &igep2_dai, | ||
94 | .num_links = 1, | ||
95 | }; | ||
96 | |||
97 | static struct platform_device *igep2_snd_device; | ||
98 | |||
99 | static int __init igep2_soc_init(void) | ||
100 | { | ||
101 | int ret; | ||
102 | |||
103 | if (!machine_is_igep0020()) | ||
104 | return -ENODEV; | ||
105 | printk(KERN_INFO "IGEP v2 SoC init\n"); | ||
106 | |||
107 | igep2_snd_device = platform_device_alloc("soc-audio", -1); | ||
108 | if (!igep2_snd_device) { | ||
109 | printk(KERN_ERR "Platform device allocation failed\n"); | ||
110 | return -ENOMEM; | ||
111 | } | ||
112 | |||
113 | platform_set_drvdata(igep2_snd_device, &snd_soc_card_igep2); | ||
114 | |||
115 | ret = platform_device_add(igep2_snd_device); | ||
116 | if (ret) | ||
117 | goto err1; | ||
118 | |||
119 | return 0; | ||
120 | |||
121 | err1: | ||
122 | printk(KERN_ERR "Unable to add platform device\n"); | ||
123 | platform_device_put(igep2_snd_device); | ||
124 | |||
125 | return ret; | ||
126 | } | ||
127 | module_init(igep2_soc_init); | ||
128 | |||
129 | static void __exit igep2_soc_exit(void) | ||
130 | { | ||
131 | platform_device_unregister(igep2_snd_device); | ||
132 | } | ||
133 | module_exit(igep2_soc_exit); | ||
134 | |||
135 | MODULE_AUTHOR("Enric Balletbo i Serra <eballetbo@iseebcn.com>"); | ||
136 | MODULE_DESCRIPTION("ALSA SoC IGEP v2"); | ||
137 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c new file mode 100644 index 00000000000..50e59194ad8 --- /dev/null +++ b/sound/soc/omap/mcpdm.c | |||
@@ -0,0 +1,470 @@ | |||
1 | /* | ||
2 | * mcpdm.c -- McPDM interface driver | ||
3 | * | ||
4 | * Author: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
5 | * Copyright (C) 2009 - Texas Instruments, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19 | * 02110-1301 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/err.h> | ||
31 | #include <linux/clk.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/io.h> | ||
34 | #include <linux/irq.h> | ||
35 | |||
36 | #include "mcpdm.h" | ||
37 | |||
38 | static struct omap_mcpdm *mcpdm; | ||
39 | |||
40 | static inline void omap_mcpdm_write(u16 reg, u32 val) | ||
41 | { | ||
42 | __raw_writel(val, mcpdm->io_base + reg); | ||
43 | } | ||
44 | |||
45 | static inline int omap_mcpdm_read(u16 reg) | ||
46 | { | ||
47 | return __raw_readl(mcpdm->io_base + reg); | ||
48 | } | ||
49 | |||
50 | static void omap_mcpdm_reg_dump(void) | ||
51 | { | ||
52 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
53 | dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", | ||
54 | omap_mcpdm_read(MCPDM_IRQSTATUS_RAW)); | ||
55 | dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", | ||
56 | omap_mcpdm_read(MCPDM_IRQSTATUS)); | ||
57 | dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", | ||
58 | omap_mcpdm_read(MCPDM_IRQENABLE_SET)); | ||
59 | dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", | ||
60 | omap_mcpdm_read(MCPDM_IRQENABLE_CLR)); | ||
61 | dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", | ||
62 | omap_mcpdm_read(MCPDM_IRQWAKE_EN)); | ||
63 | dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", | ||
64 | omap_mcpdm_read(MCPDM_DMAENABLE_SET)); | ||
65 | dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", | ||
66 | omap_mcpdm_read(MCPDM_DMAENABLE_CLR)); | ||
67 | dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", | ||
68 | omap_mcpdm_read(MCPDM_DMAWAKEEN)); | ||
69 | dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", | ||
70 | omap_mcpdm_read(MCPDM_CTRL)); | ||
71 | dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", | ||
72 | omap_mcpdm_read(MCPDM_DN_DATA)); | ||
73 | dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", | ||
74 | omap_mcpdm_read(MCPDM_UP_DATA)); | ||
75 | dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", | ||
76 | omap_mcpdm_read(MCPDM_FIFO_CTRL_DN)); | ||
77 | dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", | ||
78 | omap_mcpdm_read(MCPDM_FIFO_CTRL_UP)); | ||
79 | dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n", | ||
80 | omap_mcpdm_read(MCPDM_DN_OFFSET)); | ||
81 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Takes the McPDM module in and out of reset state. | ||
86 | * Uplink and downlink can be reset individually. | ||
87 | */ | ||
88 | static void omap_mcpdm_reset_capture(int reset) | ||
89 | { | ||
90 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
91 | |||
92 | if (reset) | ||
93 | ctrl |= SW_UP_RST; | ||
94 | else | ||
95 | ctrl &= ~SW_UP_RST; | ||
96 | |||
97 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
98 | } | ||
99 | |||
100 | static void omap_mcpdm_reset_playback(int reset) | ||
101 | { | ||
102 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
103 | |||
104 | if (reset) | ||
105 | ctrl |= SW_DN_RST; | ||
106 | else | ||
107 | ctrl &= ~SW_DN_RST; | ||
108 | |||
109 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Enables the transfer through the PDM interface to/from the Phoenix | ||
114 | * codec by enabling the corresponding UP or DN channels. | ||
115 | */ | ||
116 | void omap_mcpdm_start(int stream) | ||
117 | { | ||
118 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
119 | |||
120 | if (stream) | ||
121 | ctrl |= mcpdm->up_channels; | ||
122 | else | ||
123 | ctrl |= mcpdm->dn_channels; | ||
124 | |||
125 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * Disables the transfer through the PDM interface to/from the Phoenix | ||
130 | * codec by disabling the corresponding UP or DN channels. | ||
131 | */ | ||
132 | void omap_mcpdm_stop(int stream) | ||
133 | { | ||
134 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
135 | |||
136 | if (stream) | ||
137 | ctrl &= ~mcpdm->up_channels; | ||
138 | else | ||
139 | ctrl &= ~mcpdm->dn_channels; | ||
140 | |||
141 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Configures McPDM uplink for audio recording. | ||
146 | * This function should be called before omap_mcpdm_start. | ||
147 | */ | ||
148 | int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink) | ||
149 | { | ||
150 | int irq_mask = 0; | ||
151 | int ctrl; | ||
152 | |||
153 | if (!uplink) | ||
154 | return -EINVAL; | ||
155 | |||
156 | mcpdm->uplink = uplink; | ||
157 | |||
158 | /* Enable irq request generation */ | ||
159 | irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; | ||
160 | omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); | ||
161 | |||
162 | /* Configure uplink threshold */ | ||
163 | if (uplink->threshold > UP_THRES_MAX) | ||
164 | uplink->threshold = UP_THRES_MAX; | ||
165 | |||
166 | omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold); | ||
167 | |||
168 | /* Configure DMA controller */ | ||
169 | omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE); | ||
170 | |||
171 | /* Set pdm out format */ | ||
172 | ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
173 | ctrl &= ~PDMOUTFORMAT; | ||
174 | ctrl |= uplink->format & PDMOUTFORMAT; | ||
175 | |||
176 | /* Uplink channels */ | ||
177 | mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK); | ||
178 | |||
179 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Configures McPDM downlink for audio playback. | ||
186 | * This function should be called before omap_mcpdm_start. | ||
187 | */ | ||
188 | int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink) | ||
189 | { | ||
190 | int irq_mask = 0; | ||
191 | int ctrl; | ||
192 | |||
193 | if (!downlink) | ||
194 | return -EINVAL; | ||
195 | |||
196 | mcpdm->downlink = downlink; | ||
197 | |||
198 | /* Enable irq request generation */ | ||
199 | irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; | ||
200 | omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); | ||
201 | |||
202 | /* Configure uplink threshold */ | ||
203 | if (downlink->threshold > DN_THRES_MAX) | ||
204 | downlink->threshold = DN_THRES_MAX; | ||
205 | |||
206 | omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold); | ||
207 | |||
208 | /* Enable DMA request generation */ | ||
209 | omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE); | ||
210 | |||
211 | /* Set pdm out format */ | ||
212 | ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
213 | ctrl &= ~PDMOUTFORMAT; | ||
214 | ctrl |= downlink->format & PDMOUTFORMAT; | ||
215 | |||
216 | /* Downlink channels */ | ||
217 | mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK); | ||
218 | |||
219 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Cleans McPDM uplink configuration. | ||
226 | * This function should be called when the stream is closed. | ||
227 | */ | ||
228 | int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink) | ||
229 | { | ||
230 | int irq_mask = 0; | ||
231 | |||
232 | if (!uplink) | ||
233 | return -EINVAL; | ||
234 | |||
235 | /* Disable irq request generation */ | ||
236 | irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; | ||
237 | omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); | ||
238 | |||
239 | /* Disable DMA request generation */ | ||
240 | omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE); | ||
241 | |||
242 | /* Clear Downlink channels */ | ||
243 | mcpdm->up_channels = 0; | ||
244 | |||
245 | mcpdm->uplink = NULL; | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Cleans McPDM downlink configuration. | ||
252 | * This function should be called when the stream is closed. | ||
253 | */ | ||
254 | int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink) | ||
255 | { | ||
256 | int irq_mask = 0; | ||
257 | |||
258 | if (!downlink) | ||
259 | return -EINVAL; | ||
260 | |||
261 | /* Disable irq request generation */ | ||
262 | irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; | ||
263 | omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); | ||
264 | |||
265 | /* Disable DMA request generation */ | ||
266 | omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE); | ||
267 | |||
268 | /* clear Downlink channels */ | ||
269 | mcpdm->dn_channels = 0; | ||
270 | |||
271 | mcpdm->downlink = NULL; | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) | ||
277 | { | ||
278 | struct omap_mcpdm *mcpdm_irq = dev_id; | ||
279 | int irq_status; | ||
280 | |||
281 | irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS); | ||
282 | |||
283 | /* Acknowledge irq event */ | ||
284 | omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status); | ||
285 | |||
286 | if (irq & MCPDM_DN_IRQ_FULL) { | ||
287 | dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); | ||
288 | omap_mcpdm_reset_playback(1); | ||
289 | omap_mcpdm_playback_open(mcpdm_irq->downlink); | ||
290 | omap_mcpdm_reset_playback(0); | ||
291 | } | ||
292 | |||
293 | if (irq & MCPDM_DN_IRQ_EMPTY) { | ||
294 | dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); | ||
295 | omap_mcpdm_reset_playback(1); | ||
296 | omap_mcpdm_playback_open(mcpdm_irq->downlink); | ||
297 | omap_mcpdm_reset_playback(0); | ||
298 | } | ||
299 | |||
300 | if (irq & MCPDM_DN_IRQ) { | ||
301 | dev_dbg(mcpdm_irq->dev, "DN write request\n"); | ||
302 | } | ||
303 | |||
304 | if (irq & MCPDM_UP_IRQ_FULL) { | ||
305 | dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); | ||
306 | omap_mcpdm_reset_capture(1); | ||
307 | omap_mcpdm_capture_open(mcpdm_irq->uplink); | ||
308 | omap_mcpdm_reset_capture(0); | ||
309 | } | ||
310 | |||
311 | if (irq & MCPDM_UP_IRQ_EMPTY) { | ||
312 | dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); | ||
313 | omap_mcpdm_reset_capture(1); | ||
314 | omap_mcpdm_capture_open(mcpdm_irq->uplink); | ||
315 | omap_mcpdm_reset_capture(0); | ||
316 | } | ||
317 | |||
318 | if (irq & MCPDM_UP_IRQ) { | ||
319 | dev_dbg(mcpdm_irq->dev, "UP write request\n"); | ||
320 | } | ||
321 | |||
322 | return IRQ_HANDLED; | ||
323 | } | ||
324 | |||
325 | int omap_mcpdm_request(void) | ||
326 | { | ||
327 | int ret; | ||
328 | |||
329 | clk_enable(mcpdm->clk); | ||
330 | |||
331 | spin_lock(&mcpdm->lock); | ||
332 | |||
333 | if (!mcpdm->free) { | ||
334 | dev_err(mcpdm->dev, "McPDM interface is in use\n"); | ||
335 | spin_unlock(&mcpdm->lock); | ||
336 | ret = -EBUSY; | ||
337 | goto err; | ||
338 | } | ||
339 | mcpdm->free = 0; | ||
340 | |||
341 | spin_unlock(&mcpdm->lock); | ||
342 | |||
343 | /* Disable lines while request is ongoing */ | ||
344 | omap_mcpdm_write(MCPDM_CTRL, 0x00); | ||
345 | |||
346 | ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, | ||
347 | 0, "McPDM", (void *)mcpdm); | ||
348 | if (ret) { | ||
349 | dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n"); | ||
350 | goto err; | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | |||
355 | err: | ||
356 | clk_disable(mcpdm->clk); | ||
357 | return ret; | ||
358 | } | ||
359 | |||
360 | void omap_mcpdm_free(void) | ||
361 | { | ||
362 | spin_lock(&mcpdm->lock); | ||
363 | if (mcpdm->free) { | ||
364 | dev_err(mcpdm->dev, "McPDM interface is already free\n"); | ||
365 | spin_unlock(&mcpdm->lock); | ||
366 | return; | ||
367 | } | ||
368 | mcpdm->free = 1; | ||
369 | spin_unlock(&mcpdm->lock); | ||
370 | |||
371 | clk_disable(mcpdm->clk); | ||
372 | |||
373 | free_irq(mcpdm->irq, (void *)mcpdm); | ||
374 | } | ||
375 | |||
376 | /* Enable/disable DC offset cancelation for the analog | ||
377 | * headset path (PDM channels 1 and 2). | ||
378 | */ | ||
379 | int omap_mcpdm_set_offset(int offset1, int offset2) | ||
380 | { | ||
381 | int offset; | ||
382 | |||
383 | if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX)) | ||
384 | return -EINVAL; | ||
385 | |||
386 | offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2); | ||
387 | |||
388 | /* offset cancellation for channel 1 */ | ||
389 | if (offset1) | ||
390 | offset |= DN_OFST_RX1_EN; | ||
391 | else | ||
392 | offset &= ~DN_OFST_RX1_EN; | ||
393 | |||
394 | /* offset cancellation for channel 2 */ | ||
395 | if (offset2) | ||
396 | offset |= DN_OFST_RX2_EN; | ||
397 | else | ||
398 | offset &= ~DN_OFST_RX2_EN; | ||
399 | |||
400 | omap_mcpdm_write(MCPDM_DN_OFFSET, offset); | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | int __devinit omap_mcpdm_probe(struct platform_device *pdev) | ||
406 | { | ||
407 | struct resource *res; | ||
408 | int ret = 0; | ||
409 | |||
410 | mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL); | ||
411 | if (!mcpdm) { | ||
412 | ret = -ENOMEM; | ||
413 | goto exit; | ||
414 | } | ||
415 | |||
416 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
417 | if (res == NULL) { | ||
418 | dev_err(&pdev->dev, "no resource\n"); | ||
419 | goto err_resource; | ||
420 | } | ||
421 | |||
422 | spin_lock_init(&mcpdm->lock); | ||
423 | mcpdm->free = 1; | ||
424 | mcpdm->io_base = ioremap(res->start, resource_size(res)); | ||
425 | if (!mcpdm->io_base) { | ||
426 | ret = -ENOMEM; | ||
427 | goto err_resource; | ||
428 | } | ||
429 | |||
430 | mcpdm->irq = platform_get_irq(pdev, 0); | ||
431 | |||
432 | mcpdm->clk = clk_get(&pdev->dev, "pdm_ck"); | ||
433 | if (IS_ERR(mcpdm->clk)) { | ||
434 | ret = PTR_ERR(mcpdm->clk); | ||
435 | dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret); | ||
436 | goto err_clk; | ||
437 | } | ||
438 | |||
439 | mcpdm->dev = &pdev->dev; | ||
440 | platform_set_drvdata(pdev, mcpdm); | ||
441 | |||
442 | return 0; | ||
443 | |||
444 | err_clk: | ||
445 | iounmap(mcpdm->io_base); | ||
446 | err_resource: | ||
447 | kfree(mcpdm); | ||
448 | exit: | ||
449 | return ret; | ||
450 | } | ||
451 | |||
452 | int omap_mcpdm_remove(struct platform_device *pdev) | ||
453 | { | ||
454 | struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev); | ||
455 | |||
456 | platform_set_drvdata(pdev, NULL); | ||
457 | |||
458 | clk_put(mcpdm_ptr->clk); | ||
459 | |||
460 | iounmap(mcpdm_ptr->io_base); | ||
461 | |||
462 | mcpdm_ptr->clk = NULL; | ||
463 | mcpdm_ptr->free = 0; | ||
464 | mcpdm_ptr->dev = NULL; | ||
465 | |||
466 | kfree(mcpdm_ptr); | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h new file mode 100644 index 00000000000..20c20a8649f --- /dev/null +++ b/sound/soc/omap/mcpdm.h | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * mcpdm.h -- Defines for McPDM driver | ||
3 | * | ||
4 | * Author: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* McPDM registers */ | ||
23 | |||
24 | #define MCPDM_REVISION 0x00 | ||
25 | #define MCPDM_SYSCONFIG 0x10 | ||
26 | #define MCPDM_IRQSTATUS_RAW 0x24 | ||
27 | #define MCPDM_IRQSTATUS 0x28 | ||
28 | #define MCPDM_IRQENABLE_SET 0x2C | ||
29 | #define MCPDM_IRQENABLE_CLR 0x30 | ||
30 | #define MCPDM_IRQWAKE_EN 0x34 | ||
31 | #define MCPDM_DMAENABLE_SET 0x38 | ||
32 | #define MCPDM_DMAENABLE_CLR 0x3C | ||
33 | #define MCPDM_DMAWAKEEN 0x40 | ||
34 | #define MCPDM_CTRL 0x44 | ||
35 | #define MCPDM_DN_DATA 0x48 | ||
36 | #define MCPDM_UP_DATA 0x4C | ||
37 | #define MCPDM_FIFO_CTRL_DN 0x50 | ||
38 | #define MCPDM_FIFO_CTRL_UP 0x54 | ||
39 | #define MCPDM_DN_OFFSET 0x58 | ||
40 | |||
41 | /* | ||
42 | * MCPDM_IRQ bit fields | ||
43 | * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR | ||
44 | */ | ||
45 | |||
46 | #define MCPDM_DN_IRQ (1 << 0) | ||
47 | #define MCPDM_DN_IRQ_EMPTY (1 << 1) | ||
48 | #define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2) | ||
49 | #define MCPDM_DN_IRQ_FULL (1 << 3) | ||
50 | |||
51 | #define MCPDM_UP_IRQ (1 << 8) | ||
52 | #define MCPDM_UP_IRQ_EMPTY (1 << 9) | ||
53 | #define MCPDM_UP_IRQ_ALMST_FULL (1 << 10) | ||
54 | #define MCPDM_UP_IRQ_FULL (1 << 11) | ||
55 | |||
56 | #define MCPDM_DOWNLINK_IRQ_MASK 0x00F | ||
57 | #define MCPDM_UPLINK_IRQ_MASK 0xF00 | ||
58 | |||
59 | /* | ||
60 | * MCPDM_DMAENABLE bit fields | ||
61 | */ | ||
62 | |||
63 | #define DMA_DN_ENABLE 0x1 | ||
64 | #define DMA_UP_ENABLE 0x2 | ||
65 | |||
66 | /* | ||
67 | * MCPDM_CTRL bit fields | ||
68 | */ | ||
69 | |||
70 | #define PDM_UP1_EN 0x0001 | ||
71 | #define PDM_UP2_EN 0x0002 | ||
72 | #define PDM_UP3_EN 0x0004 | ||
73 | #define PDM_DN1_EN 0x0008 | ||
74 | #define PDM_DN2_EN 0x0010 | ||
75 | #define PDM_DN3_EN 0x0020 | ||
76 | #define PDM_DN4_EN 0x0040 | ||
77 | #define PDM_DN5_EN 0x0080 | ||
78 | #define PDMOUTFORMAT 0x0100 | ||
79 | #define CMD_INT 0x0200 | ||
80 | #define STATUS_INT 0x0400 | ||
81 | #define SW_UP_RST 0x0800 | ||
82 | #define SW_DN_RST 0x1000 | ||
83 | #define PDM_UP_MASK 0x007 | ||
84 | #define PDM_DN_MASK 0x0F8 | ||
85 | #define PDM_CMD_MASK 0x200 | ||
86 | #define PDM_STATUS_MASK 0x400 | ||
87 | |||
88 | |||
89 | #define PDMOUTFORMAT_LJUST (0 << 8) | ||
90 | #define PDMOUTFORMAT_RJUST (1 << 8) | ||
91 | |||
92 | /* | ||
93 | * MCPDM_FIFO_CTRL bit fields | ||
94 | */ | ||
95 | |||
96 | #define UP_THRES_MAX 0xF | ||
97 | #define DN_THRES_MAX 0xF | ||
98 | |||
99 | /* | ||
100 | * MCPDM_DN_OFFSET bit fields | ||
101 | */ | ||
102 | |||
103 | #define DN_OFST_RX1_EN 0x0001 | ||
104 | #define DN_OFST_RX2_EN 0x0100 | ||
105 | |||
106 | #define DN_OFST_RX1 1 | ||
107 | #define DN_OFST_RX2 9 | ||
108 | #define DN_OFST_MAX 0x1F | ||
109 | |||
110 | #define MCPDM_UPLINK 1 | ||
111 | #define MCPDM_DOWNLINK 2 | ||
112 | |||
113 | struct omap_mcpdm_link { | ||
114 | int irq_mask; | ||
115 | int threshold; | ||
116 | int format; | ||
117 | int channels; | ||
118 | }; | ||
119 | |||
120 | struct omap_mcpdm_platform_data { | ||
121 | unsigned long phys_base; | ||
122 | u16 irq; | ||
123 | }; | ||
124 | |||
125 | struct omap_mcpdm { | ||
126 | struct device *dev; | ||
127 | unsigned long phys_base; | ||
128 | void __iomem *io_base; | ||
129 | u8 free; | ||
130 | int irq; | ||
131 | |||
132 | spinlock_t lock; | ||
133 | struct omap_mcpdm_platform_data *pdata; | ||
134 | struct clk *clk; | ||
135 | struct omap_mcpdm_link *downlink; | ||
136 | struct omap_mcpdm_link *uplink; | ||
137 | struct completion irq_completion; | ||
138 | |||
139 | int dn_channels; | ||
140 | int up_channels; | ||
141 | }; | ||
142 | |||
143 | extern void omap_mcpdm_start(int stream); | ||
144 | extern void omap_mcpdm_stop(int stream); | ||
145 | extern int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink); | ||
146 | extern int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink); | ||
147 | extern int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink); | ||
148 | extern int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink); | ||
149 | extern int omap_mcpdm_request(void); | ||
150 | extern void omap_mcpdm_free(void); | ||
151 | extern int omap_mcpdm_set_offset(int offset1, int offset2); | ||
152 | int __devinit omap_mcpdm_probe(struct platform_device *pdev); | ||
153 | int omap_mcpdm_remove(struct platform_device *pdev); | ||
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c new file mode 100644 index 00000000000..40db813c079 --- /dev/null +++ b/sound/soc/omap/omap3beagle.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * omap3beagle.c -- SoC audio for OMAP3 Beagle | ||
3 | * | ||
4 | * Author: Steve Sakoman <steve@sakoman.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/soc.h> | ||
27 | |||
28 | #include <asm/mach-types.h> | ||
29 | #include <mach/hardware.h> | ||
30 | #include <mach/gpio.h> | ||
31 | #include <plat/mcbsp.h> | ||
32 | |||
33 | #include "omap-mcbsp.h" | ||
34 | #include "omap-pcm.h" | ||
35 | |||
36 | static int omap3beagle_hw_params(struct snd_pcm_substream *substream, | ||
37 | struct snd_pcm_hw_params *params) | ||
38 | { | ||
39 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
40 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
41 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
42 | unsigned int fmt; | ||
43 | int ret; | ||
44 | |||
45 | switch (params_channels(params)) { | ||
46 | case 2: /* Stereo I2S mode */ | ||
47 | fmt = SND_SOC_DAIFMT_I2S | | ||
48 | SND_SOC_DAIFMT_NB_NF | | ||
49 | SND_SOC_DAIFMT_CBM_CFM; | ||
50 | break; | ||
51 | case 4: /* Four channel TDM mode */ | ||
52 | fmt = SND_SOC_DAIFMT_DSP_A | | ||
53 | SND_SOC_DAIFMT_IB_NF | | ||
54 | SND_SOC_DAIFMT_CBM_CFM; | ||
55 | break; | ||
56 | default: | ||
57 | return -EINVAL; | ||
58 | } | ||
59 | |||
60 | /* Set codec DAI configuration */ | ||
61 | ret = snd_soc_dai_set_fmt(codec_dai, fmt); | ||
62 | if (ret < 0) { | ||
63 | printk(KERN_ERR "can't set codec DAI configuration\n"); | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | /* Set cpu DAI configuration */ | ||
68 | ret = snd_soc_dai_set_fmt(cpu_dai, fmt); | ||
69 | if (ret < 0) { | ||
70 | printk(KERN_ERR "can't set cpu DAI configuration\n"); | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | /* Set the codec system clock for DAC and ADC */ | ||
75 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, | ||
76 | SND_SOC_CLOCK_IN); | ||
77 | if (ret < 0) { | ||
78 | printk(KERN_ERR "can't set codec system clock\n"); | ||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static struct snd_soc_ops omap3beagle_ops = { | ||
86 | .hw_params = omap3beagle_hw_params, | ||
87 | }; | ||
88 | |||
89 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
90 | static struct snd_soc_dai_link omap3beagle_dai = { | ||
91 | .name = "TWL4030", | ||
92 | .stream_name = "TWL4030", | ||
93 | .cpu_dai_name = "omap-mcbsp-dai.1", | ||
94 | .platform_name = "omap-pcm-audio", | ||
95 | .codec_dai_name = "twl4030-hifi", | ||
96 | .codec_name = "twl4030-codec", | ||
97 | .ops = &omap3beagle_ops, | ||
98 | }; | ||
99 | |||
100 | /* Audio machine driver */ | ||
101 | static struct snd_soc_card snd_soc_omap3beagle = { | ||
102 | .name = "omap3beagle", | ||
103 | .owner = THIS_MODULE, | ||
104 | .dai_link = &omap3beagle_dai, | ||
105 | .num_links = 1, | ||
106 | }; | ||
107 | |||
108 | static struct platform_device *omap3beagle_snd_device; | ||
109 | |||
110 | static int __init omap3beagle_soc_init(void) | ||
111 | { | ||
112 | int ret; | ||
113 | |||
114 | if (!(machine_is_omap3_beagle() || machine_is_devkit8000())) | ||
115 | return -ENODEV; | ||
116 | pr_info("OMAP3 Beagle/Devkit8000 SoC init\n"); | ||
117 | |||
118 | omap3beagle_snd_device = platform_device_alloc("soc-audio", -1); | ||
119 | if (!omap3beagle_snd_device) { | ||
120 | printk(KERN_ERR "Platform device allocation failed\n"); | ||
121 | return -ENOMEM; | ||
122 | } | ||
123 | |||
124 | platform_set_drvdata(omap3beagle_snd_device, &snd_soc_omap3beagle); | ||
125 | |||
126 | ret = platform_device_add(omap3beagle_snd_device); | ||
127 | if (ret) | ||
128 | goto err1; | ||
129 | |||
130 | return 0; | ||
131 | |||
132 | err1: | ||
133 | printk(KERN_ERR "Unable to add platform device\n"); | ||
134 | platform_device_put(omap3beagle_snd_device); | ||
135 | |||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | static void __exit omap3beagle_soc_exit(void) | ||
140 | { | ||
141 | platform_device_unregister(omap3beagle_snd_device); | ||
142 | } | ||
143 | |||
144 | module_init(omap3beagle_soc_init); | ||
145 | module_exit(omap3beagle_soc_exit); | ||
146 | |||
147 | MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>"); | ||
148 | MODULE_DESCRIPTION("ALSA SoC OMAP3 Beagle"); | ||
149 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c new file mode 100644 index 00000000000..0daa0446983 --- /dev/null +++ b/sound/soc/omap/omap3evm.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * omap3evm.c -- ALSA SoC support for OMAP3 EVM | ||
3 | * | ||
4 | * Author: Anuj Aggarwal <anuj.aggarwal@ti.com> | ||
5 | * | ||
6 | * Based on sound/soc/omap/beagle.c by Steve Sakoman | ||
7 | * | ||
8 | * Copyright (C) 2008 Texas Instruments, Incorporated | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation version 2. | ||
13 | * | ||
14 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, | ||
15 | * whether express or implied; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #include <linux/clk.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <sound/pcm.h> | ||
24 | #include <sound/soc.h> | ||
25 | |||
26 | #include <asm/mach-types.h> | ||
27 | #include <mach/hardware.h> | ||
28 | #include <mach/gpio.h> | ||
29 | #include <plat/mcbsp.h> | ||
30 | |||
31 | #include "omap-mcbsp.h" | ||
32 | #include "omap-pcm.h" | ||
33 | |||
34 | static int omap3evm_hw_params(struct snd_pcm_substream *substream, | ||
35 | struct snd_pcm_hw_params *params) | ||
36 | { | ||
37 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
38 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
39 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
40 | int ret; | ||
41 | |||
42 | /* Set codec DAI configuration */ | ||
43 | ret = snd_soc_dai_set_fmt(codec_dai, | ||
44 | SND_SOC_DAIFMT_I2S | | ||
45 | SND_SOC_DAIFMT_NB_NF | | ||
46 | SND_SOC_DAIFMT_CBM_CFM); | ||
47 | if (ret < 0) { | ||
48 | printk(KERN_ERR "Can't set codec DAI configuration\n"); | ||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | /* Set cpu DAI configuration */ | ||
53 | ret = snd_soc_dai_set_fmt(cpu_dai, | ||
54 | SND_SOC_DAIFMT_I2S | | ||
55 | SND_SOC_DAIFMT_NB_NF | | ||
56 | SND_SOC_DAIFMT_CBM_CFM); | ||
57 | if (ret < 0) { | ||
58 | printk(KERN_ERR "Can't set cpu DAI configuration\n"); | ||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | /* Set the codec system clock for DAC and ADC */ | ||
63 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, | ||
64 | SND_SOC_CLOCK_IN); | ||
65 | if (ret < 0) { | ||
66 | printk(KERN_ERR "Can't set codec system clock\n"); | ||
67 | return ret; | ||
68 | } | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static struct snd_soc_ops omap3evm_ops = { | ||
74 | .hw_params = omap3evm_hw_params, | ||
75 | }; | ||
76 | |||
77 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
78 | static struct snd_soc_dai_link omap3evm_dai = { | ||
79 | .name = "TWL4030", | ||
80 | .stream_name = "TWL4030", | ||
81 | .cpu_dai_name = "omap-mcbsp-dai.1", | ||
82 | .codec_dai_name = "twl4030-hifi", | ||
83 | .platform_name = "omap-pcm-audio", | ||
84 | .codec_name = "twl4030-codec", | ||
85 | .ops = &omap3evm_ops, | ||
86 | }; | ||
87 | |||
88 | /* Audio machine driver */ | ||
89 | static struct snd_soc_card snd_soc_omap3evm = { | ||
90 | .name = "omap3evm", | ||
91 | .dai_link = &omap3evm_dai, | ||
92 | .num_links = 1, | ||
93 | }; | ||
94 | |||
95 | static struct platform_device *omap3evm_snd_device; | ||
96 | |||
97 | static int __init omap3evm_soc_init(void) | ||
98 | { | ||
99 | int ret; | ||
100 | |||
101 | if (!machine_is_omap3evm()) | ||
102 | return -ENODEV; | ||
103 | pr_info("OMAP3 EVM SoC init\n"); | ||
104 | |||
105 | omap3evm_snd_device = platform_device_alloc("soc-audio", -1); | ||
106 | if (!omap3evm_snd_device) { | ||
107 | printk(KERN_ERR "Platform device allocation failed\n"); | ||
108 | return -ENOMEM; | ||
109 | } | ||
110 | |||
111 | platform_set_drvdata(omap3evm_snd_device, &snd_soc_omap3evm); | ||
112 | ret = platform_device_add(omap3evm_snd_device); | ||
113 | if (ret) | ||
114 | goto err1; | ||
115 | |||
116 | return 0; | ||
117 | |||
118 | err1: | ||
119 | printk(KERN_ERR "Unable to add platform device\n"); | ||
120 | platform_device_put(omap3evm_snd_device); | ||
121 | |||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static void __exit omap3evm_soc_exit(void) | ||
126 | { | ||
127 | platform_device_unregister(omap3evm_snd_device); | ||
128 | } | ||
129 | |||
130 | module_init(omap3evm_soc_init); | ||
131 | module_exit(omap3evm_soc_exit); | ||
132 | |||
133 | MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>"); | ||
134 | MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM"); | ||
135 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/omap/omap4-hdmi-card.c b/sound/soc/omap/omap4-hdmi-card.c new file mode 100644 index 00000000000..9f32615b81f --- /dev/null +++ b/sound/soc/omap/omap4-hdmi-card.c | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * omap4-hdmi-card.c | ||
3 | * | ||
4 | * OMAP ALSA SoC machine driver for TI OMAP4 HDMI | ||
5 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ | ||
6 | * Author: Ricardo Neri <ricardo.neri@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 <sound/pcm.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <asm/mach-types.h> | ||
27 | #include <video/omapdss.h> | ||
28 | |||
29 | #define DRV_NAME "omap4-hdmi-audio" | ||
30 | |||
31 | static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream, | ||
32 | struct snd_pcm_hw_params *params) | ||
33 | { | ||
34 | int i; | ||
35 | struct omap_overlay_manager *mgr = NULL; | ||
36 | struct device *dev = substream->pcm->card->dev; | ||
37 | |||
38 | /* Find DSS HDMI device */ | ||
39 | for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { | ||
40 | mgr = omap_dss_get_overlay_manager(i); | ||
41 | if (mgr && mgr->device | ||
42 | && mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) | ||
43 | break; | ||
44 | } | ||
45 | |||
46 | if (i == omap_dss_get_num_overlay_managers()) { | ||
47 | dev_err(dev, "HDMI display device not found!\n"); | ||
48 | return -ENODEV; | ||
49 | } | ||
50 | |||
51 | /* Make sure HDMI is power-on to avoid L3 interconnect errors */ | ||
52 | if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) { | ||
53 | dev_err(dev, "HDMI display is not active!\n"); | ||
54 | return -EIO; | ||
55 | } | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static struct snd_soc_ops omap4_hdmi_dai_ops = { | ||
61 | .hw_params = omap4_hdmi_dai_hw_params, | ||
62 | }; | ||
63 | |||
64 | static struct snd_soc_dai_link omap4_hdmi_dai = { | ||
65 | .name = "HDMI", | ||
66 | .stream_name = "HDMI", | ||
67 | .cpu_dai_name = "hdmi-audio-dai", | ||
68 | .platform_name = "omap-pcm-audio", | ||
69 | .codec_name = "omapdss_hdmi", | ||
70 | .codec_dai_name = "hdmi-audio-codec", | ||
71 | .ops = &omap4_hdmi_dai_ops, | ||
72 | }; | ||
73 | |||
74 | static struct snd_soc_card snd_soc_omap4_hdmi = { | ||
75 | .name = "OMAP4HDMI", | ||
76 | .dai_link = &omap4_hdmi_dai, | ||
77 | .num_links = 1, | ||
78 | }; | ||
79 | |||
80 | static __devinit int omap4_hdmi_probe(struct platform_device *pdev) | ||
81 | { | ||
82 | struct snd_soc_card *card = &snd_soc_omap4_hdmi; | ||
83 | int ret; | ||
84 | |||
85 | card->dev = &pdev->dev; | ||
86 | |||
87 | ret = snd_soc_register_card(card); | ||
88 | if (ret) { | ||
89 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); | ||
90 | card->dev = NULL; | ||
91 | return ret; | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int __devexit omap4_hdmi_remove(struct platform_device *pdev) | ||
97 | { | ||
98 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
99 | |||
100 | snd_soc_unregister_card(card); | ||
101 | card->dev = NULL; | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static struct platform_driver omap4_hdmi_driver = { | ||
106 | .driver = { | ||
107 | .name = "omap4-hdmi-audio", | ||
108 | .owner = THIS_MODULE, | ||
109 | }, | ||
110 | .probe = omap4_hdmi_probe, | ||
111 | .remove = __devexit_p(omap4_hdmi_remove), | ||
112 | }; | ||
113 | |||
114 | static int __init omap4_hdmi_init(void) | ||
115 | { | ||
116 | return platform_driver_register(&omap4_hdmi_driver); | ||
117 | } | ||
118 | module_init(omap4_hdmi_init); | ||
119 | |||
120 | static void __exit omap4_hdmi_exit(void) | ||
121 | { | ||
122 | platform_driver_unregister(&omap4_hdmi_driver); | ||
123 | } | ||
124 | module_exit(omap4_hdmi_exit); | ||
125 | |||
126 | MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); | ||
127 | MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver"); | ||
128 | MODULE_LICENSE("GPL"); | ||
129 | MODULE_ALIAS("platform:" DRV_NAME); | ||
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c new file mode 100644 index 00000000000..bbcf380bfb5 --- /dev/null +++ b/sound/soc/omap/overo.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * overo.c -- SoC audio for Gumstix Overo | ||
3 | * | ||
4 | * Author: Steve Sakoman <steve@sakoman.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/soc.h> | ||
27 | |||
28 | #include <asm/mach-types.h> | ||
29 | #include <mach/hardware.h> | ||
30 | #include <mach/gpio.h> | ||
31 | #include <plat/mcbsp.h> | ||
32 | |||
33 | #include "omap-mcbsp.h" | ||
34 | #include "omap-pcm.h" | ||
35 | |||
36 | static int overo_hw_params(struct snd_pcm_substream *substream, | ||
37 | struct snd_pcm_hw_params *params) | ||
38 | { | ||
39 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
40 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
41 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
42 | int ret; | ||
43 | |||
44 | /* Set codec DAI configuration */ | ||
45 | ret = snd_soc_dai_set_fmt(codec_dai, | ||
46 | SND_SOC_DAIFMT_I2S | | ||
47 | SND_SOC_DAIFMT_NB_NF | | ||
48 | SND_SOC_DAIFMT_CBM_CFM); | ||
49 | if (ret < 0) { | ||
50 | printk(KERN_ERR "can't set codec DAI configuration\n"); | ||
51 | return ret; | ||
52 | } | ||
53 | |||
54 | /* Set cpu DAI configuration */ | ||
55 | ret = snd_soc_dai_set_fmt(cpu_dai, | ||
56 | SND_SOC_DAIFMT_I2S | | ||
57 | SND_SOC_DAIFMT_NB_NF | | ||
58 | SND_SOC_DAIFMT_CBM_CFM); | ||
59 | if (ret < 0) { | ||
60 | printk(KERN_ERR "can't set cpu DAI configuration\n"); | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | /* Set the codec system clock for DAC and ADC */ | ||
65 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, | ||
66 | SND_SOC_CLOCK_IN); | ||
67 | if (ret < 0) { | ||
68 | printk(KERN_ERR "can't set codec system clock\n"); | ||
69 | return ret; | ||
70 | } | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static struct snd_soc_ops overo_ops = { | ||
76 | .hw_params = overo_hw_params, | ||
77 | }; | ||
78 | |||
79 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
80 | static struct snd_soc_dai_link overo_dai = { | ||
81 | .name = "TWL4030", | ||
82 | .stream_name = "TWL4030", | ||
83 | .cpu_dai_name = "omap-mcbsp-dai.1", | ||
84 | .codec_dai_name = "twl4030-hifi", | ||
85 | .platform_name = "omap-pcm-audio", | ||
86 | .codec_name = "twl4030-codec", | ||
87 | .ops = &overo_ops, | ||
88 | }; | ||
89 | |||
90 | /* Audio machine driver */ | ||
91 | static struct snd_soc_card snd_soc_card_overo = { | ||
92 | .name = "overo", | ||
93 | .dai_link = &overo_dai, | ||
94 | .num_links = 1, | ||
95 | }; | ||
96 | |||
97 | static struct platform_device *overo_snd_device; | ||
98 | |||
99 | static int __init overo_soc_init(void) | ||
100 | { | ||
101 | int ret; | ||
102 | |||
103 | if (!(machine_is_overo() || machine_is_cm_t35())) { | ||
104 | pr_debug("Incomatible machine!\n"); | ||
105 | return -ENODEV; | ||
106 | } | ||
107 | printk(KERN_INFO "overo SoC init\n"); | ||
108 | |||
109 | overo_snd_device = platform_device_alloc("soc-audio", -1); | ||
110 | if (!overo_snd_device) { | ||
111 | printk(KERN_ERR "Platform device allocation failed\n"); | ||
112 | return -ENOMEM; | ||
113 | } | ||
114 | |||
115 | platform_set_drvdata(overo_snd_device, &snd_soc_card_overo); | ||
116 | |||
117 | ret = platform_device_add(overo_snd_device); | ||
118 | if (ret) | ||
119 | goto err1; | ||
120 | |||
121 | return 0; | ||
122 | |||
123 | err1: | ||
124 | printk(KERN_ERR "Unable to add platform device\n"); | ||
125 | platform_device_put(overo_snd_device); | ||
126 | |||
127 | return ret; | ||
128 | } | ||
129 | module_init(overo_soc_init); | ||
130 | |||
131 | static void __exit overo_soc_exit(void) | ||
132 | { | ||
133 | platform_device_unregister(overo_snd_device); | ||
134 | } | ||
135 | module_exit(overo_soc_exit); | ||
136 | |||
137 | MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>"); | ||
138 | MODULE_DESCRIPTION("ALSA SoC overo"); | ||
139 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c new file mode 100644 index 00000000000..b80efb02bfc --- /dev/null +++ b/sound/soc/omap/sdp4430.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * sdp4430.c -- SoC audio for TI OMAP4430 SDP | ||
3 | * | ||
4 | * Author: Misael Lopez Cruz <x0052729@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/mfd/twl6040.h> | ||
25 | |||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <sound/jack.h> | ||
30 | |||
31 | #include <asm/mach-types.h> | ||
32 | #include <plat/hardware.h> | ||
33 | #include <plat/mux.h> | ||
34 | |||
35 | #include "mcpdm.h" | ||
36 | #include "omap-pcm.h" | ||
37 | #include "../codecs/twl6040.h" | ||
38 | |||
39 | static int sdp4430_hw_params(struct snd_pcm_substream *substream, | ||
40 | struct snd_pcm_hw_params *params) | ||
41 | { | ||
42 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
43 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
44 | int clk_id, freq; | ||
45 | int ret; | ||
46 | |||
47 | clk_id = twl6040_get_clk_id(rtd->codec); | ||
48 | if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) | ||
49 | freq = 38400000; | ||
50 | else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) | ||
51 | freq = 32768; | ||
52 | else | ||
53 | return -EINVAL; | ||
54 | |||
55 | /* set the codec mclk */ | ||
56 | ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, | ||
57 | SND_SOC_CLOCK_IN); | ||
58 | if (ret) { | ||
59 | printk(KERN_ERR "can't set codec system clock\n"); | ||
60 | return ret; | ||
61 | } | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static struct snd_soc_ops sdp4430_ops = { | ||
66 | .hw_params = sdp4430_hw_params, | ||
67 | }; | ||
68 | |||
69 | /* Headset jack */ | ||
70 | static struct snd_soc_jack hs_jack; | ||
71 | |||
72 | /*Headset jack detection DAPM pins */ | ||
73 | static struct snd_soc_jack_pin hs_jack_pins[] = { | ||
74 | { | ||
75 | .pin = "Headset Mic", | ||
76 | .mask = SND_JACK_MICROPHONE, | ||
77 | }, | ||
78 | { | ||
79 | .pin = "Headset Stereophone", | ||
80 | .mask = SND_JACK_HEADPHONE, | ||
81 | }, | ||
82 | }; | ||
83 | |||
84 | /* SDP4430 machine DAPM */ | ||
85 | static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { | ||
86 | SND_SOC_DAPM_MIC("Ext Mic", NULL), | ||
87 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | ||
88 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
89 | SND_SOC_DAPM_HP("Headset Stereophone", NULL), | ||
90 | SND_SOC_DAPM_SPK("Earphone Spk", NULL), | ||
91 | SND_SOC_DAPM_INPUT("Aux/FM Stereo In"), | ||
92 | }; | ||
93 | |||
94 | static const struct snd_soc_dapm_route audio_map[] = { | ||
95 | /* External Mics: MAINMIC, SUBMIC with bias*/ | ||
96 | {"MAINMIC", NULL, "Main Mic Bias"}, | ||
97 | {"SUBMIC", NULL, "Main Mic Bias"}, | ||
98 | {"Main Mic Bias", NULL, "Ext Mic"}, | ||
99 | |||
100 | /* External Speakers: HFL, HFR */ | ||
101 | {"Ext Spk", NULL, "HFL"}, | ||
102 | {"Ext Spk", NULL, "HFR"}, | ||
103 | |||
104 | /* Headset Mic: HSMIC with bias */ | ||
105 | {"HSMIC", NULL, "Headset Mic Bias"}, | ||
106 | {"Headset Mic Bias", NULL, "Headset Mic"}, | ||
107 | |||
108 | /* Headset Stereophone (Headphone): HSOL, HSOR */ | ||
109 | {"Headset Stereophone", NULL, "HSOL"}, | ||
110 | {"Headset Stereophone", NULL, "HSOR"}, | ||
111 | |||
112 | /* Earphone speaker */ | ||
113 | {"Earphone Spk", NULL, "EP"}, | ||
114 | |||
115 | /* Aux/FM Stereo In: AFML, AFMR */ | ||
116 | {"AFML", NULL, "Aux/FM Stereo In"}, | ||
117 | {"AFMR", NULL, "Aux/FM Stereo In"}, | ||
118 | }; | ||
119 | |||
120 | static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) | ||
121 | { | ||
122 | struct snd_soc_codec *codec = rtd->codec; | ||
123 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
124 | int ret; | ||
125 | |||
126 | /* Add SDP4430 specific widgets */ | ||
127 | ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, | ||
128 | ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); | ||
129 | if (ret) | ||
130 | return ret; | ||
131 | |||
132 | /* Set up SDP4430 specific audio path audio_map */ | ||
133 | snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); | ||
134 | |||
135 | /* SDP4430 connected pins */ | ||
136 | snd_soc_dapm_enable_pin(dapm, "Ext Mic"); | ||
137 | snd_soc_dapm_enable_pin(dapm, "Ext Spk"); | ||
138 | snd_soc_dapm_enable_pin(dapm, "AFML"); | ||
139 | snd_soc_dapm_enable_pin(dapm, "AFMR"); | ||
140 | snd_soc_dapm_enable_pin(dapm, "Headset Mic"); | ||
141 | snd_soc_dapm_enable_pin(dapm, "Headset Stereophone"); | ||
142 | |||
143 | ret = snd_soc_dapm_sync(dapm); | ||
144 | if (ret) | ||
145 | return ret; | ||
146 | |||
147 | /* Headset jack detection */ | ||
148 | ret = snd_soc_jack_new(codec, "Headset Jack", | ||
149 | SND_JACK_HEADSET, &hs_jack); | ||
150 | if (ret) | ||
151 | return ret; | ||
152 | |||
153 | ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), | ||
154 | hs_jack_pins); | ||
155 | |||
156 | if (machine_is_omap_4430sdp()) | ||
157 | twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); | ||
158 | else | ||
159 | snd_soc_jack_report(&hs_jack, SND_JACK_HEADSET, SND_JACK_HEADSET); | ||
160 | |||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | /* Digital audio interface glue - connects codec <--> CPU */ | ||
165 | static struct snd_soc_dai_link sdp4430_dai = { | ||
166 | .name = "TWL6040", | ||
167 | .stream_name = "TWL6040", | ||
168 | .cpu_dai_name ="omap-mcpdm-dai", | ||
169 | .codec_dai_name = "twl6040-hifi", | ||
170 | .platform_name = "omap-pcm-audio", | ||
171 | .codec_name = "twl6040-codec", | ||
172 | .init = sdp4430_twl6040_init, | ||
173 | .ops = &sdp4430_ops, | ||
174 | }; | ||
175 | |||
176 | /* Audio machine driver */ | ||
177 | static struct snd_soc_card snd_soc_sdp4430 = { | ||
178 | .name = "SDP4430", | ||
179 | .dai_link = &sdp4430_dai, | ||
180 | .num_links = 1, | ||
181 | }; | ||
182 | |||
183 | static struct platform_device *sdp4430_snd_device; | ||
184 | |||
185 | static int __init sdp4430_soc_init(void) | ||
186 | { | ||
187 | int ret; | ||
188 | |||
189 | if (!machine_is_omap_4430sdp()) | ||
190 | return -ENODEV; | ||
191 | printk(KERN_INFO "SDP4430 SoC init\n"); | ||
192 | |||
193 | sdp4430_snd_device = platform_device_alloc("soc-audio", -1); | ||
194 | if (!sdp4430_snd_device) { | ||
195 | printk(KERN_ERR "Platform device allocation failed\n"); | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | |||
199 | platform_set_drvdata(sdp4430_snd_device, &snd_soc_sdp4430); | ||
200 | |||
201 | ret = platform_device_add(sdp4430_snd_device); | ||
202 | if (ret) | ||
203 | goto err; | ||
204 | |||
205 | return 0; | ||
206 | |||
207 | err: | ||
208 | printk(KERN_ERR "Unable to add platform device\n"); | ||
209 | platform_device_put(sdp4430_snd_device); | ||
210 | return ret; | ||
211 | } | ||
212 | module_init(sdp4430_soc_init); | ||
213 | |||
214 | static void __exit sdp4430_soc_exit(void) | ||
215 | { | ||
216 | platform_device_unregister(sdp4430_snd_device); | ||
217 | } | ||
218 | module_exit(sdp4430_soc_exit); | ||
219 | |||
220 | MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); | ||
221 | MODULE_DESCRIPTION("ALSA SoC SDP4430"); | ||
222 | MODULE_LICENSE("GPL"); | ||
223 | |||