aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-03-10 10:02:37 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-03-10 10:02:37 -0500
commitfad837c16cdd856c68ce2e1335ad0fe836ed8ecd (patch)
tree1a6babdc2ac7e5388c482e93505fdfaf5ff97f61 /sound/soc
parent51c6ab130642ed975681df843c772dda48a1d2ed (diff)
parent57d54889cd00db2752994b389ba714138652e60c (diff)
Merge commit 'v2.6.34-rc1' into for-2.6.35
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/au1x/Kconfig10
-rw-r--r--sound/soc/au1x/Makefile4
-rw-r--r--sound/soc/au1x/db1200.c141
-rw-r--r--sound/soc/au1x/dbdma2.c14
-rw-r--r--sound/soc/au1x/sample-ac97.c144
-rw-r--r--sound/soc/codecs/ak4104.c6
-rw-r--r--sound/soc/codecs/uda1380.c2
-rw-r--r--sound/soc/codecs/wm8350.c8
-rw-r--r--sound/soc/fsl/efika-audio-fabric.c2
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c2
-rw-r--r--sound/soc/imx/imx-pcm-fiq.c40
-rw-r--r--sound/soc/omap/Kconfig3
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/mcpdm.c484
-rw-r--r--sound/soc/omap/mcpdm.h151
-rw-r--r--sound/soc/omap/omap-mcbsp.c146
-rw-r--r--sound/soc/omap/omap-mcbsp.h4
-rw-r--r--sound/soc/omap/omap-mcpdm.c251
-rw-r--r--sound/soc/omap/omap-mcpdm.h29
-rw-r--r--sound/soc/omap/omap-pcm.c15
-rw-r--r--sound/soc/omap/omap-pcm.h4
-rw-r--r--sound/soc/sh/fsi.c46
-rw-r--r--sound/soc/sh/siu.h2
-rw-r--r--sound/soc/sh/siu_pcm.c2
-rw-r--r--sound/soc/soc-core.c20
25 files changed, 1313 insertions, 219 deletions
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
index 410a893aa66b..4b67140fdec3 100644
--- a/sound/soc/au1x/Kconfig
+++ b/sound/soc/au1x/Kconfig
@@ -22,11 +22,13 @@ config SND_SOC_AU1XPSC_AC97
22## 22##
23## Boards 23## Boards
24## 24##
25config SND_SOC_SAMPLE_PSC_AC97 25config SND_SOC_DB1200
26 tristate "Sample Au12x0/Au1550 PSC AC97 sound machine" 26 tristate "DB1200 AC97+I2S audio support"
27 depends on SND_SOC_AU1XPSC 27 depends on SND_SOC_AU1XPSC
28 select SND_SOC_AU1XPSC_AC97 28 select SND_SOC_AU1XPSC_AC97
29 select SND_SOC_AC97_CODEC 29 select SND_SOC_AC97_CODEC
30 select SND_SOC_AU1XPSC_I2S
31 select SND_SOC_WM8731
30 help 32 help
31 This is a sample AC97 sound machine for use in Au12x0/Au1550 33 Select this option to enable audio (AC97 or I2S) on the
32 based systems which have audio on PSC1 (e.g. Db1200 demoboard). 34 Alchemy/AMD/RMI DB1200 demoboard.
diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile
index 6c6950b8003a..16873076e8c4 100644
--- a/sound/soc/au1x/Makefile
+++ b/sound/soc/au1x/Makefile
@@ -8,6 +8,6 @@ obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
8obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o 8obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
9 9
10# Boards 10# Boards
11snd-soc-sample-ac97-objs := sample-ac97.o 11snd-soc-db1200-objs := db1200.o
12 12
13obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o 13obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
new file mode 100644
index 000000000000..cdf7be1b9b91
--- /dev/null
+++ b/sound/soc/au1x/db1200.c
@@ -0,0 +1,141 @@
1/*
2 * DB1200 ASoC audio fabric support code.
3 *
4 * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com>
5 *
6 */
7
8#include <linux/module.h>
9#include <linux/moduleparam.h>
10#include <linux/timer.h>
11#include <linux/interrupt.h>
12#include <linux/platform_device.h>
13#include <sound/core.h>
14#include <sound/pcm.h>
15#include <sound/soc.h>
16#include <sound/soc-dapm.h>
17#include <asm/mach-au1x00/au1000.h>
18#include <asm/mach-au1x00/au1xxx_psc.h>
19#include <asm/mach-au1x00/au1xxx_dbdma.h>
20#include <asm/mach-db1x00/bcsr.h>
21
22#include "../codecs/ac97.h"
23#include "../codecs/wm8731.h"
24#include "psc.h"
25
26/*------------------------- AC97 PART ---------------------------*/
27
28static struct snd_soc_dai_link db1200_ac97_dai = {
29 .name = "AC97",
30 .stream_name = "AC97 HiFi",
31 .cpu_dai = &au1xpsc_ac97_dai,
32 .codec_dai = &ac97_dai,
33};
34
35static struct snd_soc_card db1200_ac97_machine = {
36 .name = "DB1200_AC97",
37 .dai_link = &db1200_ac97_dai,
38 .num_links = 1,
39 .platform = &au1xpsc_soc_platform,
40};
41
42static struct snd_soc_device db1200_ac97_devdata = {
43 .card = &db1200_ac97_machine,
44 .codec_dev = &soc_codec_dev_ac97,
45};
46
47/*------------------------- I2S PART ---------------------------*/
48
49static int db1200_i2s_startup(struct snd_pcm_substream *substream)
50{
51 struct snd_soc_pcm_runtime *rtd = substream->private_data;
52 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
53 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
54 int ret;
55
56 /* WM8731 has its own 12MHz crystal */
57 snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
58 12000000, SND_SOC_CLOCK_IN);
59
60 /* codec is bitclock and lrclk master */
61 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_LEFT_J |
62 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
63 if (ret < 0)
64 goto out;
65
66 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_LEFT_J |
67 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
68 if (ret < 0)
69 goto out;
70
71 ret = 0;
72out:
73 return ret;
74}
75
76static struct snd_soc_ops db1200_i2s_wm8731_ops = {
77 .startup = db1200_i2s_startup,
78};
79
80static struct snd_soc_dai_link db1200_i2s_dai = {
81 .name = "WM8731",
82 .stream_name = "WM8731 PCM",
83 .cpu_dai = &au1xpsc_i2s_dai,
84 .codec_dai = &wm8731_dai,
85 .ops = &db1200_i2s_wm8731_ops,
86};
87
88static struct snd_soc_card db1200_i2s_machine = {
89 .name = "DB1200_I2S",
90 .dai_link = &db1200_i2s_dai,
91 .num_links = 1,
92 .platform = &au1xpsc_soc_platform,
93};
94
95static struct snd_soc_device db1200_i2s_devdata = {
96 .card = &db1200_i2s_machine,
97 .codec_dev = &soc_codec_dev_wm8731,
98};
99
100/*------------------------- COMMON PART ---------------------------*/
101
102static struct platform_device *db1200_asoc_dev;
103
104static int __init db1200_audio_load(void)
105{
106 int ret;
107
108 ret = -ENOMEM;
109 db1200_asoc_dev = platform_device_alloc("soc-audio", -1);
110 if (!db1200_asoc_dev)
111 goto out;
112
113 /* DB1200 board setup set PSC1MUX to preferred audio device */
114 if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX)
115 platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_devdata);
116 else
117 platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_devdata);
118
119 db1200_ac97_devdata.dev = &db1200_asoc_dev->dev;
120 db1200_i2s_devdata.dev = &db1200_asoc_dev->dev;
121 ret = platform_device_add(db1200_asoc_dev);
122
123 if (ret) {
124 platform_device_put(db1200_asoc_dev);
125 db1200_asoc_dev = NULL;
126 }
127out:
128 return ret;
129}
130
131static void __exit db1200_audio_unload(void)
132{
133 platform_device_unregister(db1200_asoc_dev);
134}
135
136module_init(db1200_audio_load);
137module_exit(db1200_audio_unload);
138
139MODULE_LICENSE("GPL");
140MODULE_DESCRIPTION("DB1200 ASoC audio support");
141MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 19e4d37eba1c..6d9f4c624949 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -51,8 +51,8 @@ struct au1xpsc_audio_dmadata {
51 struct snd_pcm_substream *substream; 51 struct snd_pcm_substream *substream;
52 unsigned long curr_period; /* current segment DDMA is working on */ 52 unsigned long curr_period; /* current segment DDMA is working on */
53 unsigned long q_period; /* queue period(s) */ 53 unsigned long q_period; /* queue period(s) */
54 unsigned long dma_area; /* address of queued DMA area */ 54 dma_addr_t dma_area; /* address of queued DMA area */
55 unsigned long dma_area_s; /* start address of DMA area */ 55 dma_addr_t dma_area_s; /* start address of DMA area */
56 unsigned long pos; /* current byte position being played */ 56 unsigned long pos; /* current byte position being played */
57 unsigned long periods; /* number of SG segments in total */ 57 unsigned long periods; /* number of SG segments in total */
58 unsigned long period_bytes; /* size in bytes of one SG segment */ 58 unsigned long period_bytes; /* size in bytes of one SG segment */
@@ -94,8 +94,7 @@ static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
94 94
95static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd) 95static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
96{ 96{
97 au1xxx_dbdma_put_source_flags(cd->ddma_chan, 97 au1xxx_dbdma_put_source(cd->ddma_chan, cd->dma_area,
98 (void *)phys_to_virt(cd->dma_area),
99 cd->period_bytes, DDMA_FLAGS_IE); 98 cd->period_bytes, DDMA_FLAGS_IE);
100 99
101 /* update next-to-queue period */ 100 /* update next-to-queue period */
@@ -109,9 +108,8 @@ static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
109 108
110static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd) 109static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
111{ 110{
112 au1xxx_dbdma_put_dest_flags(cd->ddma_chan, 111 au1xxx_dbdma_put_dest(cd->ddma_chan, cd->dma_area,
113 (void *)phys_to_virt(cd->dma_area), 112 cd->period_bytes, DDMA_FLAGS_IE);
114 cd->period_bytes, DDMA_FLAGS_IE);
115 113
116 /* update next-to-queue period */ 114 /* update next-to-queue period */
117 ++cd->q_period; 115 ++cd->q_period;
@@ -233,7 +231,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
233 pcd->substream = substream; 231 pcd->substream = substream;
234 pcd->period_bytes = params_period_bytes(params); 232 pcd->period_bytes = params_period_bytes(params);
235 pcd->periods = params_periods(params); 233 pcd->periods = params_periods(params);
236 pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr; 234 pcd->dma_area_s = pcd->dma_area = runtime->dma_addr;
237 pcd->q_period = 0; 235 pcd->q_period = 0;
238 pcd->curr_period = 0; 236 pcd->curr_period = 0;
239 pcd->pos = 0; 237 pcd->pos = 0;
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c
deleted file mode 100644
index 27683eb7905e..000000000000
--- a/sound/soc/au1x/sample-ac97.c
+++ /dev/null
@@ -1,144 +0,0 @@
1/*
2 * Sample Au12x0/Au1550 PSC AC97 sound machine.
3 *
4 * Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms outlined in the file COPYING at the root of this
8 * source archive.
9 *
10 * This is a very generic AC97 sound machine driver for boards which
11 * have (AC97) audio at PSC1 (e.g. DB1200 demoboards).
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/timer.h>
17#include <linux/interrupt.h>
18#include <linux/platform_device.h>
19#include <sound/core.h>
20#include <sound/pcm.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23#include <asm/mach-au1x00/au1000.h>
24#include <asm/mach-au1x00/au1xxx_psc.h>
25#include <asm/mach-au1x00/au1xxx_dbdma.h>
26
27#include "../codecs/ac97.h"
28#include "psc.h"
29
30static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec)
31{
32 snd_soc_dapm_sync(codec);
33 return 0;
34}
35
36static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
37 .name = "AC97",
38 .stream_name = "AC97 HiFi",
39 .cpu_dai = &au1xpsc_ac97_dai, /* see psc-ac97.c */
40 .codec_dai = &ac97_dai, /* see codecs/ac97.c */
41 .init = au1xpsc_sample_ac97_init,
42 .ops = NULL,
43};
44
45static struct snd_soc_card au1xpsc_sample_ac97_machine = {
46 .name = "Au1xxx PSC AC97 Audio",
47 .dai_link = &au1xpsc_sample_ac97_dai,
48 .num_links = 1,
49};
50
51static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
52 .card = &au1xpsc_sample_ac97_machine,
53 .platform = &au1xpsc_soc_platform, /* see dbdma2.c */
54 .codec_dev = &soc_codec_dev_ac97,
55};
56
57static struct resource au1xpsc_psc1_res[] = {
58 [0] = {
59 .start = CPHYSADDR(PSC1_BASE_ADDR),
60 .end = CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff,
61 .flags = IORESOURCE_MEM,
62 },
63 [1] = {
64#ifdef CONFIG_SOC_AU1200
65 .start = AU1200_PSC1_INT,
66 .end = AU1200_PSC1_INT,
67#elif defined(CONFIG_SOC_AU1550)
68 .start = AU1550_PSC1_INT,
69 .end = AU1550_PSC1_INT,
70#endif
71 .flags = IORESOURCE_IRQ,
72 },
73 [2] = {
74 .start = DSCR_CMD0_PSC1_TX,
75 .end = DSCR_CMD0_PSC1_TX,
76 .flags = IORESOURCE_DMA,
77 },
78 [3] = {
79 .start = DSCR_CMD0_PSC1_RX,
80 .end = DSCR_CMD0_PSC1_RX,
81 .flags = IORESOURCE_DMA,
82 },
83};
84
85static struct platform_device *au1xpsc_sample_ac97_dev;
86
87static int __init au1xpsc_sample_ac97_load(void)
88{
89 int ret;
90
91#ifdef CONFIG_SOC_AU1200
92 unsigned long io;
93
94 /* modify sys_pinfunc for AC97 on PSC1 */
95 io = au_readl(SYS_PINFUNC);
96 io |= SYS_PINFUNC_P1C;
97 io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B);
98 au_writel(io, SYS_PINFUNC);
99 au_sync();
100#endif
101
102 ret = -ENOMEM;
103
104 /* setup PSC clock source for AC97 part: external clock provided
105 * by codec. The psc-ac97.c driver depends on this setting!
106 */
107 au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET);
108 au_sync();
109
110 au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1);
111 if (!au1xpsc_sample_ac97_dev)
112 goto out;
113
114 au1xpsc_sample_ac97_dev->resource =
115 kmemdup(au1xpsc_psc1_res, sizeof(struct resource) *
116 ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL);
117 au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res);
118 au1xpsc_sample_ac97_dev->id = 1;
119
120 platform_set_drvdata(au1xpsc_sample_ac97_dev,
121 &au1xpsc_sample_ac97_devdata);
122 au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev;
123 ret = platform_device_add(au1xpsc_sample_ac97_dev);
124
125 if (ret) {
126 platform_device_put(au1xpsc_sample_ac97_dev);
127 au1xpsc_sample_ac97_dev = NULL;
128 }
129
130out:
131 return ret;
132}
133
134static void __exit au1xpsc_sample_ac97_exit(void)
135{
136 platform_device_unregister(au1xpsc_sample_ac97_dev);
137}
138
139module_init(au1xpsc_sample_ac97_load);
140module_exit(au1xpsc_sample_ac97_exit);
141
142MODULE_LICENSE("GPL");
143MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine");
144MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index b9ef7e45891d..b68d99fb6af0 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -90,12 +90,10 @@ static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
90 if (reg >= codec->reg_cache_size) 90 if (reg >= codec->reg_cache_size)
91 return -EINVAL; 91 return -EINVAL;
92 92
93 reg &= AK4104_REG_MASK;
94 reg |= AK4104_WRITE;
95
96 /* only write to the hardware if value has changed */ 93 /* only write to the hardware if value has changed */
97 if (cache[reg] != value) { 94 if (cache[reg] != value) {
98 u8 tmp[2] = { reg, value }; 95 u8 tmp[2] = { (reg & AK4104_REG_MASK) | AK4104_WRITE, value };
96
99 if (spi_write(spi, tmp, sizeof(tmp))) { 97 if (spi_write(spi, tmp, sizeof(tmp))) {
100 dev_err(&spi->dev, "SPI write failed\n"); 98 dev_err(&spi->dev, "SPI write failed\n");
101 return -EIO; 99 return -EIO;
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index a2763c2e7348..9cd0a66b7663 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -137,7 +137,7 @@ static void uda1380_flush_work(struct work_struct *work)
137{ 137{
138 int bit, reg; 138 int bit, reg;
139 139
140 for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) { 140 for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
141 reg = 0x10 + bit; 141 reg = 0x10 + bit;
142 pr_debug("uda1380: flush reg %x val %x:\n", reg, 142 pr_debug("uda1380: flush reg %x val %x:\n", reg,
143 uda1380_read_reg_cache(uda1380_codec, reg)); 143 uda1380_read_reg_cache(uda1380_codec, reg));
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 718ef912e758..df2c6d9617fb 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1349,7 +1349,7 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
1349 int mask; 1349 int mask;
1350 struct wm8350_jack_data *jack = NULL; 1350 struct wm8350_jack_data *jack = NULL;
1351 1351
1352 switch (irq) { 1352 switch (irq - wm8350->irq_base) {
1353 case WM8350_IRQ_CODEC_JCK_DET_L: 1353 case WM8350_IRQ_CODEC_JCK_DET_L:
1354 jack = &priv->hpl; 1354 jack = &priv->hpl;
1355 mask = WM8350_JACK_L_LVL; 1355 mask = WM8350_JACK_L_LVL;
@@ -1424,7 +1424,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
1424 wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena); 1424 wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
1425 1425
1426 /* Sync status */ 1426 /* Sync status */
1427 wm8350_hp_jack_handler(irq, priv); 1427 wm8350_hp_jack_handler(irq + wm8350->irq_base, priv);
1428 1428
1429 return 0; 1429 return 0;
1430} 1430}
@@ -1521,8 +1521,8 @@ static int wm8350_remove(struct platform_device *pdev)
1521 WM8350_JDL_ENA | WM8350_JDR_ENA); 1521 WM8350_JDL_ENA | WM8350_JDR_ENA);
1522 wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); 1522 wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
1523 1523
1524 wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); 1524 wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
1525 wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); 1525 wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
1526 1526
1527 priv->hpl.jack = NULL; 1527 priv->hpl.jack = NULL;
1528 priv->hpr.jack = NULL; 1528 priv->hpr.jack = NULL;
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c
index 3326e2a1e863..1a5b8e0d6a34 100644
--- a/sound/soc/fsl/efika-audio-fabric.c
+++ b/sound/soc/fsl/efika-audio-fabric.c
@@ -55,7 +55,7 @@ static __init int efika_fabric_init(void)
55 struct platform_device *pdev; 55 struct platform_device *pdev;
56 int rc; 56 int rc;
57 57
58 if (!machine_is_compatible("bplan,efika")) 58 if (!of_machine_is_compatible("bplan,efika"))
59 return -ENODEV; 59 return -ENODEV;
60 60
61 card.platform = &mpc5200_audio_dma_platform; 61 card.platform = &mpc5200_audio_dma_platform;
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
index b928ef7d28eb..6644cba7cbf2 100644
--- a/sound/soc/fsl/pcm030-audio-fabric.c
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -55,7 +55,7 @@ static __init int pcm030_fabric_init(void)
55 struct platform_device *pdev; 55 struct platform_device *pdev;
56 int rc; 56 int rc;
57 57
58 if (!machine_is_compatible("phytec,pcm030")) 58 if (!of_machine_is_compatible("phytec,pcm030"))
59 return -ENODEV; 59 return -ENODEV;
60 60
61 card.platform = &mpc5200_audio_dma_platform; 61 card.platform = &mpc5200_audio_dma_platform;
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
index 5532579ece4d..d9cb9849b033 100644
--- a/sound/soc/imx/imx-pcm-fiq.c
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -35,22 +35,25 @@
35struct imx_pcm_runtime_data { 35struct imx_pcm_runtime_data {
36 int period; 36 int period;
37 int periods; 37 int periods;
38 unsigned long dma_addr;
39 int dma;
40 unsigned long offset; 38 unsigned long offset;
39 unsigned long last_offset;
41 unsigned long size; 40 unsigned long size;
42 unsigned long period_cnt;
43 void *buf;
44 struct timer_list timer; 41 struct timer_list timer;
45 int period_time; 42 int poll_time;
46}; 43};
47 44
45static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd)
46{
47 iprtd->timer.expires = jiffies + iprtd->poll_time;
48}
49
48static void imx_ssi_timer_callback(unsigned long data) 50static void imx_ssi_timer_callback(unsigned long data)
49{ 51{
50 struct snd_pcm_substream *substream = (void *)data; 52 struct snd_pcm_substream *substream = (void *)data;
51 struct snd_pcm_runtime *runtime = substream->runtime; 53 struct snd_pcm_runtime *runtime = substream->runtime;
52 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 54 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
53 struct pt_regs regs; 55 struct pt_regs regs;
56 unsigned long delta;
54 57
55 get_fiq_regs(&regs); 58 get_fiq_regs(&regs);
56 59
@@ -59,9 +62,25 @@ static void imx_ssi_timer_callback(unsigned long data)
59 else 62 else
60 iprtd->offset = regs.ARM_r9 & 0xffff; 63 iprtd->offset = regs.ARM_r9 & 0xffff;
61 64
62 iprtd->timer.expires = jiffies + iprtd->period_time; 65 /* How much data have we transferred since the last period report? */
66 if (iprtd->offset >= iprtd->last_offset)
67 delta = iprtd->offset - iprtd->last_offset;
68 else
69 delta = runtime->buffer_size + iprtd->offset
70 - iprtd->last_offset;
71
72 /* If we've transferred at least a period then report it and
73 * reset our poll time */
74 if (delta >= runtime->period_size) {
75 snd_pcm_period_elapsed(substream);
76 iprtd->last_offset = iprtd->offset;
77
78 imx_ssi_set_next_poll(iprtd);
79 }
80
81 /* Restart the timer; if we didn't report we'll run on the next tick */
63 add_timer(&iprtd->timer); 82 add_timer(&iprtd->timer);
64 snd_pcm_period_elapsed(substream); 83
65} 84}
66 85
67static struct fiq_handler fh = { 86static struct fiq_handler fh = {
@@ -76,9 +95,10 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
76 95
77 iprtd->size = params_buffer_bytes(params); 96 iprtd->size = params_buffer_bytes(params);
78 iprtd->periods = params_periods(params); 97 iprtd->periods = params_periods(params);
79 iprtd->period = params_period_bytes(params); 98 iprtd->period = params_period_bytes(params) ;
80 iprtd->offset = 0; 99 iprtd->offset = 0;
81 iprtd->period_time = HZ / (params_rate(params) / params_period_size(params)); 100 iprtd->last_offset = 0;
101 iprtd->poll_time = HZ / (params_rate(params) / params_period_size(params));
82 102
83 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 103 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
84 104
@@ -114,7 +134,7 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
114 case SNDRV_PCM_TRIGGER_START: 134 case SNDRV_PCM_TRIGGER_START:
115 case SNDRV_PCM_TRIGGER_RESUME: 135 case SNDRV_PCM_TRIGGER_RESUME:
116 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 136 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
117 iprtd->timer.expires = jiffies + iprtd->period_time; 137 imx_ssi_set_next_poll(iprtd);
118 add_timer(&iprtd->timer); 138 add_timer(&iprtd->timer);
119 if (++fiq_enable == 1) 139 if (++fiq_enable == 1)
120 enable_fiq(imx_pcm_fiq); 140 enable_fiq(imx_pcm_fiq);
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 18ebdc7d0a51..f11963c21873 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -6,6 +6,9 @@ config SND_OMAP_SOC_MCBSP
6 tristate 6 tristate
7 select OMAP_MCBSP 7 select OMAP_MCBSP
8 8
9config SND_OMAP_SOC_MCPDM
10 tristate
11
9config SND_OMAP_SOC_N810 12config SND_OMAP_SOC_N810
10 tristate "SoC Audio support for Nokia N810" 13 tristate "SoC Audio support for Nokia N810"
11 depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C 14 depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 19283e5edfbf..0bc00ca14b37 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -1,9 +1,11 @@
1# OMAP Platform Support 1# OMAP Platform Support
2snd-soc-omap-objs := omap-pcm.o 2snd-soc-omap-objs := omap-pcm.o
3snd-soc-omap-mcbsp-objs := omap-mcbsp.o 3snd-soc-omap-mcbsp-objs := omap-mcbsp.o
4snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o
4 5
5obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o 6obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
6obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o 7obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
8obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
7 9
8# OMAP Machine Support 10# OMAP Machine Support
9snd-soc-n810-objs := n810.o 11snd-soc-n810-objs := n810.o
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c
new file mode 100644
index 000000000000..ad8df6cfae88
--- /dev/null
+++ b/sound/soc/omap/mcpdm.c
@@ -0,0 +1,484 @@
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/interrupt.h>
29#include <linux/err.h>
30#include <linux/clk.h>
31#include <linux/delay.h>
32#include <linux/io.h>
33#include <linux/irq.h>
34
35#include "mcpdm.h"
36
37static struct omap_mcpdm *mcpdm;
38
39static inline void omap_mcpdm_write(u16 reg, u32 val)
40{
41 __raw_writel(val, mcpdm->io_base + reg);
42}
43
44static inline int omap_mcpdm_read(u16 reg)
45{
46 return __raw_readl(mcpdm->io_base + reg);
47}
48
49static void omap_mcpdm_reg_dump(void)
50{
51 dev_dbg(mcpdm->dev, "***********************\n");
52 dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
53 omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
54 dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
55 omap_mcpdm_read(MCPDM_IRQSTATUS));
56 dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
57 omap_mcpdm_read(MCPDM_IRQENABLE_SET));
58 dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
59 omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
60 dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
61 omap_mcpdm_read(MCPDM_IRQWAKE_EN));
62 dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
63 omap_mcpdm_read(MCPDM_DMAENABLE_SET));
64 dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
65 omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
66 dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
67 omap_mcpdm_read(MCPDM_DMAWAKEEN));
68 dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
69 omap_mcpdm_read(MCPDM_CTRL));
70 dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
71 omap_mcpdm_read(MCPDM_DN_DATA));
72 dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
73 omap_mcpdm_read(MCPDM_UP_DATA));
74 dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
75 omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
76 dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
77 omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
78 dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n",
79 omap_mcpdm_read(MCPDM_DN_OFFSET));
80 dev_dbg(mcpdm->dev, "***********************\n");
81}
82
83/*
84 * Takes the McPDM module in and out of reset state.
85 * Uplink and downlink can be reset individually.
86 */
87static void omap_mcpdm_reset_capture(int reset)
88{
89 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
90
91 if (reset)
92 ctrl |= SW_UP_RST;
93 else
94 ctrl &= ~SW_UP_RST;
95
96 omap_mcpdm_write(MCPDM_CTRL, ctrl);
97}
98
99static void omap_mcpdm_reset_playback(int reset)
100{
101 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
102
103 if (reset)
104 ctrl |= SW_DN_RST;
105 else
106 ctrl &= ~SW_DN_RST;
107
108 omap_mcpdm_write(MCPDM_CTRL, ctrl);
109}
110
111/*
112 * Enables the transfer through the PDM interface to/from the Phoenix
113 * codec by enabling the corresponding UP or DN channels.
114 */
115void omap_mcpdm_start(int stream)
116{
117 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
118
119 if (stream)
120 ctrl |= mcpdm->up_channels;
121 else
122 ctrl |= mcpdm->dn_channels;
123
124 omap_mcpdm_write(MCPDM_CTRL, ctrl);
125}
126
127/*
128 * Disables the transfer through the PDM interface to/from the Phoenix
129 * codec by disabling the corresponding UP or DN channels.
130 */
131void omap_mcpdm_stop(int stream)
132{
133 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
134
135 if (stream)
136 ctrl &= ~mcpdm->up_channels;
137 else
138 ctrl &= ~mcpdm->dn_channels;
139
140 omap_mcpdm_write(MCPDM_CTRL, ctrl);
141}
142
143/*
144 * Configures McPDM uplink for audio recording.
145 * This function should be called before omap_mcpdm_start.
146 */
147int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink)
148{
149 int irq_mask = 0;
150 int ctrl;
151
152 if (!uplink)
153 return -EINVAL;
154
155 mcpdm->uplink = uplink;
156
157 /* Enable irq request generation */
158 irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
159 omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
160
161 /* Configure uplink threshold */
162 if (uplink->threshold > UP_THRES_MAX)
163 uplink->threshold = UP_THRES_MAX;
164
165 omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
166
167 /* Configure DMA controller */
168 omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
169
170 /* Set pdm out format */
171 ctrl = omap_mcpdm_read(MCPDM_CTRL);
172 ctrl &= ~PDMOUTFORMAT;
173 ctrl |= uplink->format & PDMOUTFORMAT;
174
175 /* Uplink channels */
176 mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK);
177
178 omap_mcpdm_write(MCPDM_CTRL, ctrl);
179
180 return 0;
181}
182
183/*
184 * Configures McPDM downlink for audio playback.
185 * This function should be called before omap_mcpdm_start.
186 */
187int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink)
188{
189 int irq_mask = 0;
190 int ctrl;
191
192 if (!downlink)
193 return -EINVAL;
194
195 mcpdm->downlink = downlink;
196
197 /* Enable irq request generation */
198 irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
199 omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
200
201 /* Configure uplink threshold */
202 if (downlink->threshold > DN_THRES_MAX)
203 downlink->threshold = DN_THRES_MAX;
204
205 omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
206
207 /* Enable DMA request generation */
208 omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
209
210 /* Set pdm out format */
211 ctrl = omap_mcpdm_read(MCPDM_CTRL);
212 ctrl &= ~PDMOUTFORMAT;
213 ctrl |= downlink->format & PDMOUTFORMAT;
214
215 /* Downlink channels */
216 mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK);
217
218 omap_mcpdm_write(MCPDM_CTRL, ctrl);
219
220 return 0;
221}
222
223/*
224 * Cleans McPDM uplink configuration.
225 * This function should be called when the stream is closed.
226 */
227int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink)
228{
229 int irq_mask = 0;
230
231 if (!uplink)
232 return -EINVAL;
233
234 /* Disable irq request generation */
235 irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
236 omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
237
238 /* Disable DMA request generation */
239 omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
240
241 /* Clear Downlink channels */
242 mcpdm->up_channels = 0;
243
244 mcpdm->uplink = NULL;
245
246 return 0;
247}
248
249/*
250 * Cleans McPDM downlink configuration.
251 * This function should be called when the stream is closed.
252 */
253int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink)
254{
255 int irq_mask = 0;
256
257 if (!downlink)
258 return -EINVAL;
259
260 /* Disable irq request generation */
261 irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
262 omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
263
264 /* Disable DMA request generation */
265 omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
266
267 /* clear Downlink channels */
268 mcpdm->dn_channels = 0;
269
270 mcpdm->downlink = NULL;
271
272 return 0;
273}
274
275static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
276{
277 struct omap_mcpdm *mcpdm_irq = dev_id;
278 int irq_status;
279
280 irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
281
282 /* Acknowledge irq event */
283 omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
284
285 if (irq & MCPDM_DN_IRQ_FULL) {
286 dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
287 omap_mcpdm_reset_playback(1);
288 omap_mcpdm_playback_open(mcpdm_irq->downlink);
289 omap_mcpdm_reset_playback(0);
290 }
291
292 if (irq & MCPDM_DN_IRQ_EMPTY) {
293 dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
294 omap_mcpdm_reset_playback(1);
295 omap_mcpdm_playback_open(mcpdm_irq->downlink);
296 omap_mcpdm_reset_playback(0);
297 }
298
299 if (irq & MCPDM_DN_IRQ) {
300 dev_dbg(mcpdm_irq->dev, "DN write request\n");
301 }
302
303 if (irq & MCPDM_UP_IRQ_FULL) {
304 dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
305 omap_mcpdm_reset_capture(1);
306 omap_mcpdm_capture_open(mcpdm_irq->uplink);
307 omap_mcpdm_reset_capture(0);
308 }
309
310 if (irq & MCPDM_UP_IRQ_EMPTY) {
311 dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
312 omap_mcpdm_reset_capture(1);
313 omap_mcpdm_capture_open(mcpdm_irq->uplink);
314 omap_mcpdm_reset_capture(0);
315 }
316
317 if (irq & MCPDM_UP_IRQ) {
318 dev_dbg(mcpdm_irq->dev, "UP write request\n");
319 }
320
321 return IRQ_HANDLED;
322}
323
324int omap_mcpdm_request(void)
325{
326 int ret;
327
328 clk_enable(mcpdm->clk);
329
330 spin_lock(&mcpdm->lock);
331
332 if (!mcpdm->free) {
333 dev_err(mcpdm->dev, "McPDM interface is in use\n");
334 spin_unlock(&mcpdm->lock);
335 ret = -EBUSY;
336 goto err;
337 }
338 mcpdm->free = 0;
339
340 spin_unlock(&mcpdm->lock);
341
342 /* Disable lines while request is ongoing */
343 omap_mcpdm_write(MCPDM_CTRL, 0x00);
344
345 ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
346 0, "McPDM", (void *)mcpdm);
347 if (ret) {
348 dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n");
349 goto err;
350 }
351
352 return 0;
353
354err:
355 clk_disable(mcpdm->clk);
356 return ret;
357}
358
359void omap_mcpdm_free(void)
360{
361 spin_lock(&mcpdm->lock);
362 if (mcpdm->free) {
363 dev_err(mcpdm->dev, "McPDM interface is already free\n");
364 spin_unlock(&mcpdm->lock);
365 return;
366 }
367 mcpdm->free = 1;
368 spin_unlock(&mcpdm->lock);
369
370 clk_disable(mcpdm->clk);
371
372 free_irq(mcpdm->irq, (void *)mcpdm);
373}
374
375/* Enable/disable DC offset cancelation for the analog
376 * headset path (PDM channels 1 and 2).
377 */
378int omap_mcpdm_set_offset(int offset1, int offset2)
379{
380 int offset;
381
382 if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
383 return -EINVAL;
384
385 offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2);
386
387 /* offset cancellation for channel 1 */
388 if (offset1)
389 offset |= DN_OFST_RX1_EN;
390 else
391 offset &= ~DN_OFST_RX1_EN;
392
393 /* offset cancellation for channel 2 */
394 if (offset2)
395 offset |= DN_OFST_RX2_EN;
396 else
397 offset &= ~DN_OFST_RX2_EN;
398
399 omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
400
401 return 0;
402}
403
404static int __devinit omap_mcpdm_probe(struct platform_device *pdev)
405{
406 struct resource *res;
407 int ret = 0;
408
409 mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
410 if (!mcpdm) {
411 ret = -ENOMEM;
412 goto exit;
413 }
414
415 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416 if (res == NULL) {
417 dev_err(&pdev->dev, "no resource\n");
418 goto err_resource;
419 }
420
421 spin_lock_init(&mcpdm->lock);
422 mcpdm->free = 1;
423 mcpdm->io_base = ioremap(res->start, resource_size(res));
424 if (!mcpdm->io_base) {
425 ret = -ENOMEM;
426 goto err_resource;
427 }
428
429 mcpdm->irq = platform_get_irq(pdev, 0);
430
431 mcpdm->clk = clk_get(&pdev->dev, "pdm_ck");
432 if (IS_ERR(mcpdm->clk)) {
433 ret = PTR_ERR(mcpdm->clk);
434 dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret);
435 goto err_clk;
436 }
437
438 mcpdm->dev = &pdev->dev;
439 platform_set_drvdata(pdev, mcpdm);
440
441 return 0;
442
443err_clk:
444 iounmap(mcpdm->io_base);
445err_resource:
446 kfree(mcpdm);
447exit:
448 return ret;
449}
450
451static int __devexit omap_mcpdm_remove(struct platform_device *pdev)
452{
453 struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
454
455 platform_set_drvdata(pdev, NULL);
456
457 clk_put(mcpdm_ptr->clk);
458
459 iounmap(mcpdm_ptr->io_base);
460
461 mcpdm_ptr->clk = NULL;
462 mcpdm_ptr->free = 0;
463 mcpdm_ptr->dev = NULL;
464
465 kfree(mcpdm_ptr);
466
467 return 0;
468}
469
470static struct platform_driver omap_mcpdm_driver = {
471 .probe = omap_mcpdm_probe,
472 .remove = __devexit_p(omap_mcpdm_remove),
473 .driver = {
474 .name = "omap-mcpdm",
475 },
476};
477
478static struct platform_device *omap_mcpdm_device;
479
480static int __init omap_mcpdm_init(void)
481{
482 return platform_driver_register(&omap_mcpdm_driver);
483}
484arch_initcall(omap_mcpdm_init);
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h
new file mode 100644
index 000000000000..7bb326ef0886
--- /dev/null
+++ b/sound/soc/omap/mcpdm.h
@@ -0,0 +1,151 @@
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
113struct omap_mcpdm_link {
114 int irq_mask;
115 int threshold;
116 int format;
117 int channels;
118};
119
120struct omap_mcpdm_platform_data {
121 unsigned long phys_base;
122 u16 irq;
123};
124
125struct 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
143extern void omap_mcpdm_start(int stream);
144extern void omap_mcpdm_stop(int stream);
145extern int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink);
146extern int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink);
147extern int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink);
148extern int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink);
149extern int omap_mcpdm_request(void);
150extern void omap_mcpdm_free(void);
151extern int omap_mcpdm_set_offset(int offset1, int offset2);
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 6bbbd2ab0ee7..e814a9591f78 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -39,6 +39,14 @@
39 39
40#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) 40#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
41 41
42#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
43 xhandler_get, xhandler_put) \
44{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
45 .info = omap_mcbsp_st_info_volsw, \
46 .get = xhandler_get, .put = xhandler_put, \
47 .private_value = (unsigned long) &(struct soc_mixer_control) \
48 {.min = xmin, .max = xmax} }
49
42struct omap_mcbsp_data { 50struct omap_mcbsp_data {
43 unsigned int bus_id; 51 unsigned int bus_id;
44 struct omap_mcbsp_reg_cfg regs; 52 struct omap_mcbsp_reg_cfg regs;
@@ -82,11 +90,11 @@ static const int omap1_dma_reqs[][2] = {};
82static const unsigned long omap1_mcbsp_port[][2] = {}; 90static const unsigned long omap1_mcbsp_port[][2] = {};
83#endif 91#endif
84 92
85#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) 93#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
86static const int omap24xx_dma_reqs[][2] = { 94static const int omap24xx_dma_reqs[][2] = {
87 { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, 95 { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX },
88 { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, 96 { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX },
89#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) 97#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)
90 { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, 98 { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX },
91 { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, 99 { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX },
92 { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, 100 { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX },
@@ -124,7 +132,7 @@ static const unsigned long omap2430_mcbsp_port[][2] = {
124static const unsigned long omap2430_mcbsp_port[][2] = {}; 132static const unsigned long omap2430_mcbsp_port[][2] = {};
125#endif 133#endif
126 134
127#if defined(CONFIG_ARCH_OMAP34XX) 135#if defined(CONFIG_ARCH_OMAP3)
128static const unsigned long omap34xx_mcbsp_port[][2] = { 136static const unsigned long omap34xx_mcbsp_port[][2] = {
129 { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, 137 { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
130 OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, 138 OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
@@ -287,6 +295,8 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
287 omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; 295 omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
288 omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; 296 omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
289 omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; 297 omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode;
298 omap_mcbsp_dai_dma_params[id][substream->stream].data_type =
299 OMAP_DMA_DATA_TYPE_S16;
290 cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; 300 cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
291 301
292 if (mcbsp_data->configured) { 302 if (mcbsp_data->configured) {
@@ -637,6 +647,136 @@ struct snd_soc_dai omap_mcbsp_dai[] = {
637 647
638EXPORT_SYMBOL_GPL(omap_mcbsp_dai); 648EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
639 649
650int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
651 struct snd_ctl_elem_info *uinfo)
652{
653 struct soc_mixer_control *mc =
654 (struct soc_mixer_control *)kcontrol->private_value;
655 int max = mc->max;
656 int min = mc->min;
657
658 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
659 uinfo->count = 1;
660 uinfo->value.integer.min = min;
661 uinfo->value.integer.max = max;
662 return 0;
663}
664
665#define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(id, channel) \
666static int \
667omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
668 struct snd_ctl_elem_value *uc) \
669{ \
670 struct soc_mixer_control *mc = \
671 (struct soc_mixer_control *)kc->private_value; \
672 int max = mc->max; \
673 int min = mc->min; \
674 int val = uc->value.integer.value[0]; \
675 \
676 if (val < min || val > max) \
677 return -EINVAL; \
678 \
679 /* OMAP McBSP implementation uses index values 0..4 */ \
680 return omap_st_set_chgain((id)-1, channel, val); \
681}
682
683#define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(id, channel) \
684static int \
685omap_mcbsp##id##_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
686 struct snd_ctl_elem_value *uc) \
687{ \
688 s16 chgain; \
689 \
690 if (omap_st_get_chgain((id)-1, channel, &chgain)) \
691 return -EAGAIN; \
692 \
693 uc->value.integer.value[0] = chgain; \
694 return 0; \
695}
696
697OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 0)
698OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 1)
699OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 0)
700OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 1)
701OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 0)
702OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 1)
703OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 0)
704OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 1)
705
706static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
707 struct snd_ctl_elem_value *ucontrol)
708{
709 struct soc_mixer_control *mc =
710 (struct soc_mixer_control *)kcontrol->private_value;
711 u8 value = ucontrol->value.integer.value[0];
712
713 if (value == omap_st_is_enabled(mc->reg))
714 return 0;
715
716 if (value)
717 omap_st_enable(mc->reg);
718 else
719 omap_st_disable(mc->reg);
720
721 return 1;
722}
723
724static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
725 struct snd_ctl_elem_value *ucontrol)
726{
727 struct soc_mixer_control *mc =
728 (struct soc_mixer_control *)kcontrol->private_value;
729
730 ucontrol->value.integer.value[0] = omap_st_is_enabled(mc->reg);
731 return 0;
732}
733
734static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = {
735 SOC_SINGLE_EXT("McBSP2 Sidetone Switch", 1, 0, 1, 0,
736 omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),
737 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume",
738 -32768, 32767,
739 omap_mcbsp2_get_st_ch0_volume,
740 omap_mcbsp2_set_st_ch0_volume),
741 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume",
742 -32768, 32767,
743 omap_mcbsp2_get_st_ch1_volume,
744 omap_mcbsp2_set_st_ch1_volume),
745};
746
747static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = {
748 SOC_SINGLE_EXT("McBSP3 Sidetone Switch", 2, 0, 1, 0,
749 omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),
750 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume",
751 -32768, 32767,
752 omap_mcbsp3_get_st_ch0_volume,
753 omap_mcbsp3_set_st_ch0_volume),
754 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume",
755 -32768, 32767,
756 omap_mcbsp3_get_st_ch1_volume,
757 omap_mcbsp3_set_st_ch1_volume),
758};
759
760int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id)
761{
762 if (!cpu_is_omap34xx())
763 return -ENODEV;
764
765 switch (mcbsp_id) {
766 case 1: /* McBSP 2 */
767 return snd_soc_add_controls(codec, omap_mcbsp2_st_controls,
768 ARRAY_SIZE(omap_mcbsp2_st_controls));
769 case 2: /* McBSP 3 */
770 return snd_soc_add_controls(codec, omap_mcbsp3_st_controls,
771 ARRAY_SIZE(omap_mcbsp3_st_controls));
772 default:
773 break;
774 }
775
776 return -EINVAL;
777}
778EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
779
640static int __init snd_omap_mcbsp_init(void) 780static int __init snd_omap_mcbsp_init(void)
641{ 781{
642 return snd_soc_register_dais(omap_mcbsp_dai, 782 return snd_soc_register_dais(omap_mcbsp_dai,
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index 647d2f981ab0..6c363e5f4387 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -50,11 +50,13 @@ enum omap_mcbsp_div {
50#undef NUM_LINKS 50#undef NUM_LINKS
51#define NUM_LINKS 3 51#define NUM_LINKS 3
52#endif 52#endif
53#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) 53#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)
54#undef NUM_LINKS 54#undef NUM_LINKS
55#define NUM_LINKS 5 55#define NUM_LINKS 5
56#endif 56#endif
57 57
58extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; 58extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
59 59
60int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id);
61
60#endif 62#endif
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
new file mode 100644
index 000000000000..25f19e4728bf
--- /dev/null
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -0,0 +1,251 @@
1/*
2 * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port
3 *
4 * Copyright (C) 2009 Texas Instruments
5 *
6 * Author: Misael Lopez Cruz <x0052729@ti.com>
7 * Contact: Jorge Eduardo Candelaria <x0107209@ti.com>
8 * Margarita Olaya <magi.olaya@ti.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; 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 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 *
24 */
25
26#include <linux/init.h>
27#include <linux/module.h>
28#include <linux/device.h>
29#include <sound/core.h>
30#include <sound/pcm.h>
31#include <sound/pcm_params.h>
32#include <sound/initval.h>
33#include <sound/soc.h>
34
35#include <plat/control.h>
36#include <plat/dma.h>
37#include <plat/mcbsp.h>
38#include "mcpdm.h"
39#include "omap-mcpdm.h"
40#include "omap-pcm.h"
41
42struct omap_mcpdm_data {
43 struct omap_mcpdm_link *links;
44 int active;
45};
46
47static struct omap_mcpdm_link omap_mcpdm_links[] = {
48 /* downlink */
49 {
50 .irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL,
51 .threshold = 1,
52 .format = PDMOUTFORMAT_LJUST,
53 },
54 /* uplink */
55 {
56 .irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL,
57 .threshold = 1,
58 .format = PDMOUTFORMAT_LJUST,
59 },
60};
61
62static struct omap_mcpdm_data mcpdm_data = {
63 .links = omap_mcpdm_links,
64 .active = 0,
65};
66
67/*
68 * Stream DMA parameters
69 */
70static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
71 {
72 .name = "Audio playback",
73 .dma_req = OMAP44XX_DMA_MCPDM_DL,
74 .data_type = OMAP_DMA_DATA_TYPE_S32,
75 .sync_mode = OMAP_DMA_SYNC_PACKET,
76 .packet_size = 16,
77 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA,
78 },
79 {
80 .name = "Audio capture",
81 .dma_req = OMAP44XX_DMA_MCPDM_UP,
82 .data_type = OMAP_DMA_DATA_TYPE_S32,
83 .sync_mode = OMAP_DMA_SYNC_PACKET,
84 .packet_size = 16,
85 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA,
86 },
87};
88
89static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
90 struct snd_soc_dai *dai)
91{
92 struct snd_soc_pcm_runtime *rtd = substream->private_data;
93 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
94 int err = 0;
95
96 if (!cpu_dai->active)
97 err = omap_mcpdm_request();
98
99 return err;
100}
101
102static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
103 struct snd_soc_dai *dai)
104{
105 struct snd_soc_pcm_runtime *rtd = substream->private_data;
106 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
107
108 if (!cpu_dai->active)
109 omap_mcpdm_free();
110}
111
112static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd,
113 struct snd_soc_dai *dai)
114{
115 struct snd_soc_pcm_runtime *rtd = substream->private_data;
116 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
117 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
118 int stream = substream->stream;
119 int err = 0;
120
121 switch (cmd) {
122 case SNDRV_PCM_TRIGGER_START:
123 case SNDRV_PCM_TRIGGER_RESUME:
124 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
125 if (!mcpdm_priv->active++)
126 omap_mcpdm_start(stream);
127 break;
128
129 case SNDRV_PCM_TRIGGER_STOP:
130 case SNDRV_PCM_TRIGGER_SUSPEND:
131 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
132 if (!--mcpdm_priv->active)
133 omap_mcpdm_stop(stream);
134 break;
135 default:
136 err = -EINVAL;
137 }
138
139 return err;
140}
141
142static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
143 struct snd_pcm_hw_params *params,
144 struct snd_soc_dai *dai)
145{
146 struct snd_soc_pcm_runtime *rtd = substream->private_data;
147 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
148 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
149 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
150 int stream = substream->stream;
151 int channels, err, link_mask = 0;
152
153 cpu_dai->dma_data = &omap_mcpdm_dai_dma_params[stream];
154
155 channels = params_channels(params);
156 switch (channels) {
157 case 4:
158 if (stream == SNDRV_PCM_STREAM_CAPTURE)
159 /* up to 2 channels for capture */
160 return -EINVAL;
161 link_mask |= 1 << 3;
162 case 3:
163 if (stream == SNDRV_PCM_STREAM_CAPTURE)
164 /* up to 2 channels for capture */
165 return -EINVAL;
166 link_mask |= 1 << 2;
167 case 2:
168 link_mask |= 1 << 1;
169 case 1:
170 link_mask |= 1 << 0;
171 break;
172 default:
173 /* unsupported number of channels */
174 return -EINVAL;
175 }
176
177 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
178 mcpdm_links[stream].channels = link_mask << 3;
179 err = omap_mcpdm_playback_open(&mcpdm_links[stream]);
180 } else {
181 mcpdm_links[stream].channels = link_mask << 0;
182 err = omap_mcpdm_capture_open(&mcpdm_links[stream]);
183 }
184
185 return err;
186}
187
188static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream,
189 struct snd_soc_dai *dai)
190{
191 struct snd_soc_pcm_runtime *rtd = substream->private_data;
192 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
193 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
194 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
195 int stream = substream->stream;
196 int err;
197
198 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
199 err = omap_mcpdm_playback_close(&mcpdm_links[stream]);
200 else
201 err = omap_mcpdm_capture_close(&mcpdm_links[stream]);
202
203 return err;
204}
205
206static struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
207 .startup = omap_mcpdm_dai_startup,
208 .shutdown = omap_mcpdm_dai_shutdown,
209 .trigger = omap_mcpdm_dai_trigger,
210 .hw_params = omap_mcpdm_dai_hw_params,
211 .hw_free = omap_mcpdm_dai_hw_free,
212};
213
214#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
215#define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
216
217struct snd_soc_dai omap_mcpdm_dai = {
218 .name = "omap-mcpdm",
219 .id = -1,
220 .playback = {
221 .channels_min = 1,
222 .channels_max = 4,
223 .rates = OMAP_MCPDM_RATES,
224 .formats = OMAP_MCPDM_FORMATS,
225 },
226 .capture = {
227 .channels_min = 1,
228 .channels_max = 2,
229 .rates = OMAP_MCPDM_RATES,
230 .formats = OMAP_MCPDM_FORMATS,
231 },
232 .ops = &omap_mcpdm_dai_ops,
233 .private_data = &mcpdm_data,
234};
235EXPORT_SYMBOL_GPL(omap_mcpdm_dai);
236
237static int __init snd_omap_mcpdm_init(void)
238{
239 return snd_soc_register_dai(&omap_mcpdm_dai);
240}
241module_init(snd_omap_mcpdm_init);
242
243static void __exit snd_omap_mcpdm_exit(void)
244{
245 snd_soc_unregister_dai(&omap_mcpdm_dai);
246}
247module_exit(snd_omap_mcpdm_exit);
248
249MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
250MODULE_DESCRIPTION("OMAP PDM SoC Interface");
251MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/omap/omap-mcpdm.h
new file mode 100644
index 000000000000..73b80d559345
--- /dev/null
+++ b/sound/soc/omap/omap-mcpdm.h
@@ -0,0 +1,29 @@
1/*
2 * omap-mcpdm.h
3 *
4 * Copyright (C) 2009 Texas Instruments
5 *
6 * Contact: Misael Lopez Cruz <x0052729@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#ifndef __OMAP_MCPDM_H__
25#define __OMAP_MCPDM_H__
26
27extern struct snd_soc_dai omap_mcpdm_dai;
28
29#endif /* End of __OMAP_MCPDM_H__ */
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 9db2770e9640..825db385f01f 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -37,7 +37,8 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
37 SNDRV_PCM_INFO_INTERLEAVED | 37 SNDRV_PCM_INFO_INTERLEAVED |
38 SNDRV_PCM_INFO_PAUSE | 38 SNDRV_PCM_INFO_PAUSE |
39 SNDRV_PCM_INFO_RESUME, 39 SNDRV_PCM_INFO_RESUME,
40 .formats = SNDRV_PCM_FMTBIT_S16_LE, 40 .formats = SNDRV_PCM_FMTBIT_S16_LE |
41 SNDRV_PCM_FMTBIT_S32_LE,
41 .period_bytes_min = 32, 42 .period_bytes_min = 32,
42 .period_bytes_max = 64 * 1024, 43 .period_bytes_max = 64 * 1024,
43 .periods_min = 2, 44 .periods_min = 2,
@@ -149,6 +150,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
149 struct omap_runtime_data *prtd = runtime->private_data; 150 struct omap_runtime_data *prtd = runtime->private_data;
150 struct omap_pcm_dma_data *dma_data = prtd->dma_data; 151 struct omap_pcm_dma_data *dma_data = prtd->dma_data;
151 struct omap_dma_channel_params dma_params; 152 struct omap_dma_channel_params dma_params;
153 int bytes;
152 154
153 /* return if this is a bufferless transfer e.g. 155 /* return if this is a bufferless transfer e.g.
154 * codec <--> BT codec or GSM modem -- lg FIXME */ 156 * codec <--> BT codec or GSM modem -- lg FIXME */
@@ -156,11 +158,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
156 return 0; 158 return 0;
157 159
158 memset(&dma_params, 0, sizeof(dma_params)); 160 memset(&dma_params, 0, sizeof(dma_params));
159 /* 161 dma_params.data_type = dma_data->data_type;
160 * Note: Regardless of interface data formats supported by OMAP McBSP
161 * or EAC blocks, internal representation is always fixed 16-bit/sample
162 */
163 dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
164 dma_params.trigger = dma_data->dma_req; 162 dma_params.trigger = dma_data->dma_req;
165 dma_params.sync_mode = dma_data->sync_mode; 163 dma_params.sync_mode = dma_data->sync_mode;
166 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 164 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -170,6 +168,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
170 dma_params.src_start = runtime->dma_addr; 168 dma_params.src_start = runtime->dma_addr;
171 dma_params.dst_start = dma_data->port_addr; 169 dma_params.dst_start = dma_data->port_addr;
172 dma_params.dst_port = OMAP_DMA_PORT_MPUI; 170 dma_params.dst_port = OMAP_DMA_PORT_MPUI;
171 dma_params.dst_fi = dma_data->packet_size;
173 } else { 172 } else {
174 dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; 173 dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
175 dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; 174 dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
@@ -177,6 +176,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
177 dma_params.src_start = dma_data->port_addr; 176 dma_params.src_start = dma_data->port_addr;
178 dma_params.dst_start = runtime->dma_addr; 177 dma_params.dst_start = runtime->dma_addr;
179 dma_params.src_port = OMAP_DMA_PORT_MPUI; 178 dma_params.src_port = OMAP_DMA_PORT_MPUI;
179 dma_params.src_fi = dma_data->packet_size;
180 } 180 }
181 /* 181 /*
182 * Set DMA transfer frame size equal to ALSA period size and frame 182 * Set DMA transfer frame size equal to ALSA period size and frame
@@ -184,7 +184,8 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
184 * we can transfer the whole ALSA buffer with single DMA transfer but 184 * we can transfer the whole ALSA buffer with single DMA transfer but
185 * still can get an interrupt at each period bounary 185 * still can get an interrupt at each period bounary
186 */ 186 */
187 dma_params.elem_count = snd_pcm_lib_period_bytes(substream) / 2; 187 bytes = snd_pcm_lib_period_bytes(substream);
188 dma_params.elem_count = bytes >> dma_data->data_type;
188 dma_params.frame_count = runtime->periods; 189 dma_params.frame_count = runtime->periods;
189 omap_set_dma_params(prtd->dma_ch, &dma_params); 190 omap_set_dma_params(prtd->dma_ch, &dma_params);
190 191
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
index 38a821dd4118..b19975d26907 100644
--- a/sound/soc/omap/omap-pcm.h
+++ b/sound/soc/omap/omap-pcm.h
@@ -29,8 +29,10 @@ struct omap_pcm_dma_data {
29 char *name; /* stream identifier */ 29 char *name; /* stream identifier */
30 int dma_req; /* DMA request line */ 30 int dma_req; /* DMA request line */
31 unsigned long port_addr; /* transmit/receive register */ 31 unsigned long port_addr; /* transmit/receive register */
32 int sync_mode; /* DMA sync mode */
33 void (*set_threshold)(struct snd_pcm_substream *substream); 32 void (*set_threshold)(struct snd_pcm_substream *substream);
33 int data_type; /* data type 8,16,32 */
34 int sync_mode; /* DMA sync mode */
35 int packet_size; /* packet size only in PACKET mode */
34}; 36};
35 37
36extern struct snd_soc_platform omap_soc_platform; 38extern struct snd_soc_platform omap_soc_platform;
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 3c36d24a6c20..993abb730dfa 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -388,7 +388,7 @@ static void fsi_soft_all_reset(struct fsi_master *master)
388} 388}
389 389
390/* playback interrupt */ 390/* playback interrupt */
391static int fsi_data_push(struct fsi_priv *fsi) 391static int fsi_data_push(struct fsi_priv *fsi, int startup)
392{ 392{
393 struct snd_pcm_runtime *runtime; 393 struct snd_pcm_runtime *runtime;
394 struct snd_pcm_substream *substream = NULL; 394 struct snd_pcm_substream *substream = NULL;
@@ -397,7 +397,7 @@ static int fsi_data_push(struct fsi_priv *fsi)
397 int fifo_free; 397 int fifo_free;
398 int width; 398 int width;
399 u8 *start; 399 u8 *start;
400 int i, ret, over_period; 400 int i, over_period;
401 401
402 if (!fsi || 402 if (!fsi ||
403 !fsi->substream || 403 !fsi->substream ||
@@ -453,24 +453,26 @@ static int fsi_data_push(struct fsi_priv *fsi)
453 453
454 fsi->byte_offset += send * width; 454 fsi->byte_offset += send * width;
455 455
456 ret = 0;
457 status = fsi_reg_read(fsi, DOFF_ST); 456 status = fsi_reg_read(fsi, DOFF_ST);
458 if (status & ERR_OVER) { 457 if (!startup) {
459 struct snd_soc_dai *dai = fsi_get_dai(substream); 458 struct snd_soc_dai *dai = fsi_get_dai(substream);
460 dev_err(dai->dev, "over run error\n"); 459
461 fsi_reg_write(fsi, DOFF_ST, status & ~ST_ERR); 460 if (status & ERR_OVER)
462 ret = -EIO; 461 dev_err(dai->dev, "over run\n");
462 if (status & ERR_UNDER)
463 dev_err(dai->dev, "under run\n");
463 } 464 }
465 fsi_reg_write(fsi, DOFF_ST, 0);
464 466
465 fsi_irq_enable(fsi, 1); 467 fsi_irq_enable(fsi, 1);
466 468
467 if (over_period) 469 if (over_period)
468 snd_pcm_period_elapsed(substream); 470 snd_pcm_period_elapsed(substream);
469 471
470 return ret; 472 return 0;
471} 473}
472 474
473static int fsi_data_pop(struct fsi_priv *fsi) 475static int fsi_data_pop(struct fsi_priv *fsi, int startup)
474{ 476{
475 struct snd_pcm_runtime *runtime; 477 struct snd_pcm_runtime *runtime;
476 struct snd_pcm_substream *substream = NULL; 478 struct snd_pcm_substream *substream = NULL;
@@ -479,7 +481,7 @@ static int fsi_data_pop(struct fsi_priv *fsi)
479 int fifo_fill; 481 int fifo_fill;
480 int width; 482 int width;
481 u8 *start; 483 u8 *start;
482 int i, ret, over_period; 484 int i, over_period;
483 485
484 if (!fsi || 486 if (!fsi ||
485 !fsi->substream || 487 !fsi->substream ||
@@ -534,21 +536,23 @@ static int fsi_data_pop(struct fsi_priv *fsi)
534 536
535 fsi->byte_offset += fifo_fill * width; 537 fsi->byte_offset += fifo_fill * width;
536 538
537 ret = 0;
538 status = fsi_reg_read(fsi, DIFF_ST); 539 status = fsi_reg_read(fsi, DIFF_ST);
539 if (status & ERR_UNDER) { 540 if (!startup) {
540 struct snd_soc_dai *dai = fsi_get_dai(substream); 541 struct snd_soc_dai *dai = fsi_get_dai(substream);
541 dev_err(dai->dev, "under run error\n"); 542
542 fsi_reg_write(fsi, DIFF_ST, status & ~ST_ERR); 543 if (status & ERR_OVER)
543 ret = -EIO; 544 dev_err(dai->dev, "over run\n");
545 if (status & ERR_UNDER)
546 dev_err(dai->dev, "under run\n");
544 } 547 }
548 fsi_reg_write(fsi, DIFF_ST, 0);
545 549
546 fsi_irq_enable(fsi, 0); 550 fsi_irq_enable(fsi, 0);
547 551
548 if (over_period) 552 if (over_period)
549 snd_pcm_period_elapsed(substream); 553 snd_pcm_period_elapsed(substream);
550 554
551 return ret; 555 return 0;
552} 556}
553 557
554static irqreturn_t fsi_interrupt(int irq, void *data) 558static irqreturn_t fsi_interrupt(int irq, void *data)
@@ -562,13 +566,13 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
562 fsi_master_write(master, SOFT_RST, status | 0x00000010); 566 fsi_master_write(master, SOFT_RST, status | 0x00000010);
563 567
564 if (int_st & INT_A_OUT) 568 if (int_st & INT_A_OUT)
565 fsi_data_push(&master->fsia); 569 fsi_data_push(&master->fsia, 0);
566 if (int_st & INT_B_OUT) 570 if (int_st & INT_B_OUT)
567 fsi_data_push(&master->fsib); 571 fsi_data_push(&master->fsib, 0);
568 if (int_st & INT_A_IN) 572 if (int_st & INT_A_IN)
569 fsi_data_pop(&master->fsia); 573 fsi_data_pop(&master->fsia, 0);
570 if (int_st & INT_B_IN) 574 if (int_st & INT_B_IN)
571 fsi_data_pop(&master->fsib); 575 fsi_data_pop(&master->fsib, 0);
572 576
573 fsi_master_write(master, INT_ST, 0x0000000); 577 fsi_master_write(master, INT_ST, 0x0000000);
574 578
@@ -726,7 +730,7 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
726 fsi_stream_push(fsi, substream, 730 fsi_stream_push(fsi, substream,
727 frames_to_bytes(runtime, runtime->buffer_size), 731 frames_to_bytes(runtime, runtime->buffer_size),
728 frames_to_bytes(runtime, runtime->period_size)); 732 frames_to_bytes(runtime, runtime->period_size));
729 ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi); 733 ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1);
730 break; 734 break;
731 case SNDRV_PCM_TRIGGER_STOP: 735 case SNDRV_PCM_TRIGGER_STOP:
732 fsi_irq_disable(fsi, is_play); 736 fsi_irq_disable(fsi, is_play);
diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h
index 9cc04ab2bce7..c0bfab8fed3d 100644
--- a/sound/soc/sh/siu.h
+++ b/sound/soc/sh/siu.h
@@ -72,7 +72,7 @@ struct siu_firmware {
72#include <linux/interrupt.h> 72#include <linux/interrupt.h>
73#include <linux/io.h> 73#include <linux/io.h>
74 74
75#include <asm/dma-sh.h> 75#include <asm/dmaengine.h>
76 76
77#include <sound/core.h> 77#include <sound/core.h>
78#include <sound/pcm.h> 78#include <sound/pcm.h>
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index c5efc30f0136..ba7f8d05d977 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -32,7 +32,7 @@
32#include <sound/pcm_params.h> 32#include <sound/pcm_params.h>
33#include <sound/soc-dai.h> 33#include <sound/soc-dai.h>
34 34
35#include <asm/dma-sh.h> 35#include <asm/dmaengine.h>
36#include <asm/siu.h> 36#include <asm/siu.h>
37 37
38#include "siu.h" 38#include "siu.h"
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 4011ad3dc57a..06c38d1502b7 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -427,24 +427,24 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
427 if (!runtime->hw.rates) { 427 if (!runtime->hw.rates) {
428 printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", 428 printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
429 codec_dai->name, cpu_dai->name); 429 codec_dai->name, cpu_dai->name);
430 goto machine_err; 430 goto config_err;
431 } 431 }
432 if (!runtime->hw.formats) { 432 if (!runtime->hw.formats) {
433 printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", 433 printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
434 codec_dai->name, cpu_dai->name); 434 codec_dai->name, cpu_dai->name);
435 goto machine_err; 435 goto config_err;
436 } 436 }
437 if (!runtime->hw.channels_min || !runtime->hw.channels_max) { 437 if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
438 printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", 438 printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
439 codec_dai->name, cpu_dai->name); 439 codec_dai->name, cpu_dai->name);
440 goto machine_err; 440 goto config_err;
441 } 441 }
442 442
443 /* Symmetry only applies if we've already got an active stream. */ 443 /* Symmetry only applies if we've already got an active stream. */
444 if (cpu_dai->active || codec_dai->active) { 444 if (cpu_dai->active || codec_dai->active) {
445 ret = soc_pcm_apply_symmetry(substream); 445 ret = soc_pcm_apply_symmetry(substream);
446 if (ret != 0) 446 if (ret != 0)
447 goto machine_err; 447 goto config_err;
448 } 448 }
449 449
450 pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); 450 pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
@@ -467,10 +467,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
467 mutex_unlock(&pcm_mutex); 467 mutex_unlock(&pcm_mutex);
468 return 0; 468 return 0;
469 469
470machine_err: 470config_err:
471 if (machine->ops && machine->ops->shutdown) 471 if (machine->ops && machine->ops->shutdown)
472 machine->ops->shutdown(substream); 472 machine->ops->shutdown(substream);
473 473
474machine_err:
475 if (codec_dai->ops->shutdown)
476 codec_dai->ops->shutdown(substream, codec_dai);
477
474codec_dai_err: 478codec_dai_err:
475 if (platform->pcm_ops->close) 479 if (platform->pcm_ops->close)
476 platform->pcm_ops->close(substream); 480 platform->pcm_ops->close(substream);
@@ -1002,6 +1006,12 @@ static int soc_resume(struct device *dev)
1002 struct snd_soc_card *card = socdev->card; 1006 struct snd_soc_card *card = socdev->card;
1003 struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai; 1007 struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
1004 1008
1009 /* If the initialization of this soc device failed, there is no codec
1010 * associated with it. Just bail out in this case.
1011 */
1012 if (!card->codec)
1013 return 0;
1014
1005 /* AC97 devices might have other drivers hanging off them so 1015 /* AC97 devices might have other drivers hanging off them so
1006 * need to resume immediately. Other drivers don't have that 1016 * need to resume immediately. Other drivers don't have that
1007 * problem and may take a substantial amount of time to resume 1017 * problem and may take a substantial amount of time to resume